diff options
Diffstat (limited to 'Build/source/texk/web2c/synctexdir/synctex_parser.c')
-rw-r--r-- | Build/source/texk/web2c/synctexdir/synctex_parser.c | 3217 |
1 files changed, 3217 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/synctexdir/synctex_parser.c b/Build/source/texk/web2c/synctexdir/synctex_parser.c new file mode 100644 index 00000000000..187c13d25cb --- /dev/null +++ b/Build/source/texk/web2c/synctexdir/synctex_parser.c @@ -0,0 +1,3217 @@ +/* +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. + +*/ + +#include "web2c/c-auto.h" /* for inline && HAVE_xxx */ + +#include "stddef.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "limits.h" +#include "time.h" +#include "math.h" +#include "errno.h" + +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +/* This custom malloc functions initializes to 0 the newly allocated memory. */ +void *_synctex_malloc(size_t size) { + void * ptr = malloc(size); + if(ptr) { + bzero(ptr,size); + } + return (void *)ptr; +} + +/* The data is organized in a graph with multiple entries. + * The root object is a scanner, it is created with the contents on a synctex file. + * Each leaf of the tree is a synctex_node_t object. + * There are 3 subtrees, two of them sharing the same leaves. + * The first tree is the list of input records, where input file names are associated with tags. + * The second tree is the box tree as given by TeX when shipping pages out. + * First level objects are sheets, containing boxes, glues, kerns... + * The third tree allows to browse leaves according to tag and line. + */ + +#include "synctex_parser.h" + +/* each synctex node has a class */ +typedef struct __synctex_class_t _synctex_class_t; +typedef _synctex_class_t * synctex_class_t; + + +/* synctex_node_t is a pointer to a node + * _synctex_node is the target of the synctex_node_t pointer + * It is a pseudo object oriented program. + * class is a pointer to the class object the node belongs to. + * implementation is meant to contain the private data of the node + * basically, there are 2 kinds of information: navigation information and + * synctex information. Both will depend on the type of the node, + * thus different nodes will have different private data. + * There is no inheritancy overhead. + */ +typedef union _synctex_info_t { + int INT; + char * PTR; +} synctex_info_t; + +struct _synctex_node { + synctex_class_t class; + synctex_info_t * implementation; +}; + +/* Each node of the tree, except the scanner itself belongs to a class. + * The class object is just a struct declaring the owning scanner + * This is a pointer to the scanner as root of the tree. + * The type is used to identify the kind of node. + * The class declares pointers to a creator and a destructor method. + * The log and display fields are used to log and display the node. + * display will also display the child, sibling and parent sibling. + * parent, child and sibling are used to navigate the tree, + * from TeX box hierarchy point of view. + * The friend field points to a method which allows to navigate from friend to friend. + * A friend is a node with very close tag and line numbers. + * Finally, the info field point to a method giving the private node info offset. + */ + +typedef synctex_node_t *(*_synctex_node_getter_t)(synctex_node_t); +typedef synctex_info_t *(*_synctex_info_getter_t)(synctex_node_t); + +struct __synctex_class_t { + synctex_scanner_t scanner; + int type; + synctex_node_t (*new)(synctex_scanner_t scanner); + void (*free)(synctex_node_t); + void (*log)(synctex_node_t); + void (*display)(synctex_node_t); + _synctex_node_getter_t parent; + _synctex_node_getter_t child; + _synctex_node_getter_t sibling; + _synctex_node_getter_t friend; + _synctex_info_getter_t info; +}; + +# pragma mark - +# pragma mark Abstract OBJECTS and METHODS + +/* These macros are shortcuts + * This macro checks if a message can be sent. + */ +# define SYNCTEX_CAN_PERFORM(NODE,SELECTOR)\ + (NULL!=((((NODE)->class))->SELECTOR)) + +/* This macro is some kind of objc_msg_send. + * It takes care of sending the proper message if possible. + */ +# define SYNCTEX_MSG_SEND(NODE,SELECTOR) if(NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR)) {\ + (*((((NODE)->class))->SELECTOR))(NODE);\ + } + +/* read only safe getter + */ +# define SYNCTEX_GET(NODE,SELECTOR)((NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR))?SYNCTEX_GETTER(NODE,SELECTOR)[0]:(NULL)) + +/* read/write getter + */ +# define SYNCTEX_GETTER(NODE,SELECTOR)\ + ((synctex_node_t *)((*((((NODE)->class))->SELECTOR))(NODE))) + +# define SYNCTEX_FREE(NODE) SYNCTEX_MSG_SEND(NODE,free); + +/* Parent getter and setter + */ +# define SYNCTEX_PARENT(NODE) SYNCTEX_GET(NODE,parent) +# define SYNCTEX_SET_PARENT(NODE,NEW_PARENT) if(NODE && NEW_PARENT && SYNCTEX_CAN_PERFORM(NODE,parent)){\ + SYNCTEX_GETTER(NODE,parent)[0]=NEW_PARENT;\ + } + +/* Child getter and setter + */ +# define SYNCTEX_CHILD(NODE) SYNCTEX_GET(NODE,child) +# define SYNCTEX_SET_CHILD(NODE,NEW_CHILD) if(NODE && NEW_CHILD){\ + SYNCTEX_GETTER(NODE,child)[0]=NEW_CHILD;\ + SYNCTEX_GETTER(NEW_CHILD,parent)[0]=NODE;\ + } + +/* Sibling getter and setter + */ +# define SYNCTEX_SIBLING(NODE) SYNCTEX_GET(NODE,sibling) +# define SYNCTEX_SET_SIBLING(NODE,NEW_SIBLING) if(NODE && NEW_SIBLING) {\ + SYNCTEX_GETTER(NODE,sibling)[0]=NEW_SIBLING;\ + if(SYNCTEX_CAN_PERFORM(NEW_SIBLING,parent) && SYNCTEX_CAN_PERFORM(NODE,parent)) {\ + SYNCTEX_GETTER(NEW_SIBLING,parent)[0]=SYNCTEX_GETTER(NODE,parent)[0];\ + }\ + } +/* Friend getter and setter + */ +# define SYNCTEX_FRIEND(NODE) SYNCTEX_GET(NODE,friend) +# define SYNCTEX_SET_FRIEND(NODE,NEW_FRIEND) if(NODE && NEW_FRIEND){\ + SYNCTEX_GETTER(NODE,friend)[0]=NEW_FRIEND;\ + } + +/* A node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for all nodes with children. + */ +void _synctex_free_node(synctex_node_t node) { + if(node) { + (*((node->class)->sibling))(node); + SYNCTEX_FREE(SYNCTEX_SIBLING(node)); + SYNCTEX_FREE(SYNCTEX_CHILD(node)); + free(node); + } + return; +} + +/* A node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for nodes with no child. + */ +void _synctex_free_leaf(synctex_node_t node) { + if(node) { + SYNCTEX_FREE(SYNCTEX_SIBLING(node)); + free(node); + } + return; +} + +#include <zlib.h> + +/* The synctex scanner is the root object. + * Is is initialized with the contents of a text file. + * The buffer_? are first used to parse the text. + */ +struct __synctex_scanner_t { + gzFile file; /* The (compressed) file */ + unsigned char * buffer_cur; /* current location in the buffer */ + unsigned char * buffer_start; /* start of the buffer */ + unsigned char * buffer_end; /* end of the buffer */ + char * output_fmt; /* dvi or pdf, not yet used */ + char * output; /* the output name used to create the scanner */ + int version; /* 1, not yet used */ + int pre_magnification; /* magnification from the synctex preamble */ + int pre_unit; /* unit from the synctex preamble */ + int pre_x_offset; /* X offste from the synctex preamble */ + int pre_y_offset; /* Y offset from the synctex preamble */ + int count; /* Number of records, from the synctex postamble */ + float unit; /* real unit, from synctex preamble or post scriptum */ + float x_offset; /* X offset, from synctex preamble or post scriptum */ + float y_offset; /* Y Offset, from synctex preamble or post scriptum */ + synctex_node_t sheet; /* The first sheet node, its siblings are the other sheet nodes */ + synctex_node_t input; /* The first input node, its siblings are the other input nodes */ + int number_of_lists; /* The number of friend lists */ + synctex_node_t * lists_of_friends;/* The friend lists */ + _synctex_class_t class[synctex_node_type_last]; /* The classes of the nodes of the scanner */ +}; + +/* SYNCTEX_CUR, SYNCTEX_START and SYNCTEX_END are convenient shortcuts + */ +# define SYNCTEX_CUR (scanner->buffer_cur) +# define SYNCTEX_START (scanner->buffer_start) +# define SYNCTEX_END (scanner->buffer_end) + +# pragma mark - +# pragma mark OBJECTS, their creators and destructors. + +/* Here, we define the indices for the different informations. + * They are used to declare the size of the implementation. + * For example, if one object uses SYNCTEX_HORIZ_IDX is its size, + * then its info will contain a tag, line, column, horiz but no width nor height nor depth + */ +# define SYNCTEX_PAGE_IDX 0 + +/* The sheet is a first level node. + * It has no parent (the parent is the scanner itself) + * Its sibling points to another sheet. + * Its child points to its first child, in general a box. + * A sheet node contains only one synctex information: the page. + * This is the 1 based page index as given by TeX. + */ +/* The next macros are used to access the node info + * SYNCTEX_INFO(node) points to the first synctex integer or pointer data of node + * SYNCTEX_INFO(node)[index] is the information at index + * for example, the page of a sheet is stored in SYNCTEX_INFO(sheet)[SYNCTEX_PAGE_IDX] + */ +# define SYNCTEX_INFO(NODE) ((*((((NODE)->class))->info))(NODE)) +# define SYNCTEX_PAGE_IDX 0 +# define SYNCTEX_PAGE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_PAGE_IDX].INT + +typedef struct { + synctex_class_t class; + synctex_info_t implementation[2+SYNCTEX_PAGE_IDX+1];/* child, sibling + * SYNCTEX_PAGE_IDX */ +} synctex_sheet_t; + +/* This macro defines implementation offsets + * It is only used for pointer values + */ +# define SYNCTEX_MAKE_GET(SYNCTEX_GETTER,OFFSET)\ +synctex_node_t * SYNCTEX_GETTER (synctex_node_t node) {\ + return node?(synctex_node_t *)((&((node)->implementation))+OFFSET):NULL;\ +} +SYNCTEX_MAKE_GET(_synctex_implementation_0,0) +SYNCTEX_MAKE_GET(_synctex_implementation_1,1) +SYNCTEX_MAKE_GET(_synctex_implementation_2,2) +SYNCTEX_MAKE_GET(_synctex_implementation_3,3) +SYNCTEX_MAKE_GET(_synctex_implementation_4,4) + +synctex_node_t _synctex_new_sheet(synctex_scanner_t scanner); +void _synctex_display_sheet(synctex_node_t sheet); +void _synctex_log_sheet(synctex_node_t sheet); + +static const _synctex_class_t synctex_class_sheet = { + NULL, /* No scanner yet */ + synctex_node_type_sheet, /* Node type */ + &_synctex_new_sheet, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_sheet, /* log */ + &_synctex_display_sheet, /* display */ + NULL, /* No parent */ + &_synctex_implementation_0, /* child */ + &_synctex_implementation_1, /* sibling */ + NULL, /* No friend */ + (_synctex_info_getter_t)&_synctex_implementation_2 /* info */ +}; + +/* sheet node creator */ +synctex_node_t _synctex_new_sheet(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_sheet_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_sheet:(synctex_class_t)&synctex_class_sheet; + } + return node; +} + +/* A box node contains navigation and synctex information + * There are different kind of boxes. + * Only horizontal boxes are treated differently because of their visible size. + */ +# define SYNCTEX_TAG_IDX 0 +# define SYNCTEX_LINE_IDX (SYNCTEX_TAG_IDX+1) +# define SYNCTEX_COLUMN_IDX (SYNCTEX_LINE_IDX+1) +# define SYNCTEX_HORIZ_IDX (SYNCTEX_COLUMN_IDX+1) +# define SYNCTEX_VERT_IDX (SYNCTEX_HORIZ_IDX+1) +# define SYNCTEX_WIDTH_IDX (SYNCTEX_VERT_IDX+1) +# define SYNCTEX_HEIGHT_IDX (SYNCTEX_WIDTH_IDX+1) +# define SYNCTEX_DEPTH_IDX (SYNCTEX_HEIGHT_IDX+1) +/* the corresponding info accessors */ +# define SYNCTEX_TAG(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_TAG_IDX].INT +# define SYNCTEX_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_LINE_IDX].INT +# define SYNCTEX_COLUMN(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_COLUMN_IDX].INT +# define SYNCTEX_HORIZ(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_IDX].INT +# define SYNCTEX_VERT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_IDX].INT +# define SYNCTEX_WIDTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_IDX].INT +# define SYNCTEX_HEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_IDX].INT +# define SYNCTEX_DEPTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_IDX].INT + +typedef struct { + synctex_class_t class; + synctex_info_t implementation[4+SYNCTEX_DEPTH_IDX+1]; /* parent,child,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH */ +} synctex_vert_box_node_t; + +synctex_node_t _synctex_new_vbox(synctex_scanner_t scanner); +void _synctex_log_box(synctex_node_t sheet); +void _synctex_display_vbox(synctex_node_t node); + +/* These are static class objects, each scanner will make a copy of them and setup the scanner field. + */ +static const _synctex_class_t synctex_class_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_vbox, /* Node type */ + &_synctex_new_vbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_box, /* log */ + &_synctex_display_vbox, /* display */ + &_synctex_implementation_0, /* parent */ + &_synctex_implementation_1, /* child */ + &_synctex_implementation_2, /* sibling */ + &_synctex_implementation_3, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_4 +}; + +/* vertical box node creator */ +synctex_node_t _synctex_new_vbox(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_vert_box_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_vbox:(synctex_class_t)&synctex_class_vbox; + } + return node; +} + +# define SYNCTEX_HORIZ_V_IDX (SYNCTEX_DEPTH_IDX+1) +# define SYNCTEX_VERT_V_IDX (SYNCTEX_HORIZ_V_IDX+1) +# define SYNCTEX_WIDTH_V_IDX (SYNCTEX_VERT_V_IDX+1) +# define SYNCTEX_HEIGHT_V_IDX (SYNCTEX_WIDTH_V_IDX+1) +# define SYNCTEX_DEPTH_V_IDX (SYNCTEX_HEIGHT_V_IDX+1) +/* the corresponding info accessors */ +# define SYNCTEX_HORIZ_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_V_IDX].INT +# define SYNCTEX_VERT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_V_IDX].INT +# define SYNCTEX_WIDTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_V_IDX].INT +# define SYNCTEX_HEIGHT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_V_IDX].INT +# define SYNCTEX_DEPTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_V_IDX].INT + +/* Horizontal boxes must contain visible size, because 0 width does not mean emptiness */ +typedef struct { + synctex_class_t class; + synctex_info_t implementation[4+SYNCTEX_DEPTH_V_IDX+1]; /*parent,child,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH, + * SYNCTEX_HORIZ_V,SYNCTEX_VERT_V,SYNCTEX_WIDTH_V,SYNCTEX_HEIGHT_V,SYNCTEX_DEPTH_V*/ +} synctex_horiz_box_node_t; + +synctex_node_t _synctex_new_hbox(synctex_scanner_t scanner); +void _synctex_display_hbox(synctex_node_t node); +void _synctex_log_horiz_box(synctex_node_t sheet); + + +static const _synctex_class_t synctex_class_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_hbox, /* Node type */ + &_synctex_new_hbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_horiz_box, /* log */ + &_synctex_display_hbox, /* display */ + &_synctex_implementation_0, /* parent */ + &_synctex_implementation_1, /* child */ + &_synctex_implementation_2, /* sibling */ + &_synctex_implementation_3, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_4 +}; + +/* horizontal box node creator */ +synctex_node_t _synctex_new_hbox(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_horiz_box_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_hbox:(synctex_class_t)&synctex_class_hbox; + } + return node; +} + +/* This void box node implementation is either horizontal or vertical + * It does not contain a child field. + */ +typedef struct { + synctex_class_t class; + synctex_info_t implementation[3+SYNCTEX_DEPTH_IDX+1]; /* parent,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH*/ +} synctex_void_box_node_t; + +synctex_node_t _synctex_new_void_vbox(synctex_scanner_t scanner); +void _synctex_log_void_box(synctex_node_t sheet); +void _synctex_display_void_vbox(synctex_node_t node); + +static const _synctex_class_t synctex_class_void_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_void_vbox,/* Node type */ + &_synctex_new_void_vbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_void_box, /* log */ + &_synctex_display_void_vbox,/* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +/* vertical void box node creator */ +synctex_node_t _synctex_new_void_vbox(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_void_box_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_void_vbox:(synctex_class_t)&synctex_class_void_vbox; + } + return node; +} + +synctex_node_t _synctex_new_void_hbox(synctex_scanner_t scanner); +void _synctex_display_void_hbox(synctex_node_t node); + +static const _synctex_class_t synctex_class_void_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_void_hbox,/* Node type */ + &_synctex_new_void_hbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_void_box, /* log */ + &_synctex_display_void_hbox,/* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +/* horizontal void box node creator */ +synctex_node_t _synctex_new_void_hbox(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_void_box_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_void_hbox:(synctex_class_t)&synctex_class_void_hbox; + } + return node; +} + +/* The medium nodes correspond to kern, glue and math nodes. */ +typedef struct { + synctex_class_t class; + synctex_info_t implementation[3+SYNCTEX_WIDTH_IDX+1]; /* parent,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH */ +} synctex_medium_node_t; + +synctex_node_t _synctex_new_math(synctex_scanner_t scanner); +void _synctex_log_medium_node(synctex_node_t sheet); +void _synctex_display_math(synctex_node_t node); + +static const _synctex_class_t synctex_class_math = { + NULL, /* No scanner yet */ + synctex_node_type_math, /* Node type */ + &_synctex_new_math, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_medium_node, /* log */ + &_synctex_display_math, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +/* math node creator */ +synctex_node_t _synctex_new_math(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_medium_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_math:(synctex_class_t)&synctex_class_math; + } + return node; +} + +synctex_node_t _synctex_new_glue(synctex_scanner_t scanner); +void _synctex_display_glue(synctex_node_t node); + +static const _synctex_class_t synctex_class_glue = { + NULL, /* No scanner yet */ + synctex_node_type_glue, /* Node type */ + &_synctex_new_glue, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_medium_node, /* log */ + &_synctex_display_glue, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; +/* glue node creator */ +synctex_node_t _synctex_new_glue(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_medium_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_glue:(synctex_class_t)&synctex_class_glue; + } + return node; +} + +synctex_node_t _synctex_new_kern(synctex_scanner_t scanner); +void _synctex_display_kern(synctex_node_t node); + +static const _synctex_class_t synctex_class_kern = { + NULL, /* No scanner yet */ + synctex_node_type_kern, /* Node type */ + &_synctex_new_kern, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_medium_node, /* log */ + &_synctex_display_kern, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +/* kern node creator */ +synctex_node_t _synctex_new_kern(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_medium_node_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_kern:(synctex_class_t)&synctex_class_kern; + } + return node; +} + +# define SYNCTEX_NAME_IDX (SYNCTEX_TAG_IDX+1) +# define SYNCTEX_NAME(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NAME_IDX].PTR + +/* Input nodes only know about their sibling, which is another input node. + * The synctex information is the SYNCTEX_TAG and SYNCTEX_NAME*/ +typedef struct { + synctex_class_t class; + synctex_info_t implementation[1+SYNCTEX_NAME_IDX+1]; /* sibling, + * SYNCTEX_TAG,SYNCTEX_NAME */ +} synctex_input_t; + +synctex_node_t _synctex_new_input(synctex_scanner_t scanner); +void _synctex_free_input(synctex_node_t node); +void _synctex_display_input(synctex_node_t node); +void _synctex_log_input(synctex_node_t sheet); + +static const _synctex_class_t synctex_class_input = { + NULL, /* No scanner yet */ + synctex_node_type_input, /* Node type */ + &_synctex_new_input, /* creator */ + &_synctex_free_input, /* destructor */ + &_synctex_log_input, /* log */ + &_synctex_display_input, /* display */ + NULL, /* No parent */ + NULL, /* No child */ + &_synctex_implementation_0, /* sibling */ + NULL, /* No friend */ + (_synctex_info_getter_t)&_synctex_implementation_1 +}; + +synctex_node_t _synctex_new_input(synctex_scanner_t scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_input_t)); + if(node) { + node->class = scanner?scanner->class+synctex_node_type_input:(synctex_class_t)&synctex_class_input; + } + return node; +} +void _synctex_free_input(synctex_node_t node){ + if(node) { + SYNCTEX_FREE(SYNCTEX_SIBLING(node)); + free(SYNCTEX_NAME(node)); + free(node); + } +} +#pragma mark - +#pragma mark Navigation +synctex_node_t synctex_node_parent(synctex_node_t node) +{ + return SYNCTEX_PARENT(node); +} +synctex_node_t synctex_node_sheet(synctex_node_t node) +{ + while(node && node->class->type != synctex_node_type_sheet) { + node = SYNCTEX_PARENT(node); + } + /* exit the while loop either when node is NULL or node is a sheet */ + return node; +} +synctex_node_t synctex_node_child(synctex_node_t node) +{ + return SYNCTEX_CHILD(node); +} +synctex_node_t synctex_node_sibling(synctex_node_t node) +{ + return SYNCTEX_SIBLING(node); +} +synctex_node_t synctex_node_next(synctex_node_t node) { + if(SYNCTEX_CHILD(node)) { + return SYNCTEX_CHILD(node); + } +sibling: + if(SYNCTEX_SIBLING(node)) { + return SYNCTEX_SIBLING(node); + } + if((node = SYNCTEX_PARENT(node)) != NULL) { + if(node->class->type == synctex_node_type_sheet) {/* EXC_BAD_ACCESS? */ + return NULL; + } + goto sibling; + } + return NULL; +} +#pragma mark - +#pragma mark CLASS + +/* Public node accessor: the type */ +synctex_node_type_t synctex_node_type(synctex_node_t node) { + if(node) { + return (((node)->class))->type; + } + return synctex_node_type_error; +} + +/* Public node accessor: the human readable type */ +const char * synctex_node_isa(synctex_node_t node) { +static const char * isa[synctex_node_type_last] = + {"Not a node","sheet","vbox","void vbox","hbox","void hbox","kern","glue","math","input"}; + return isa[synctex_node_type(node)]; +} + +# pragma mark - +# pragma mark SYNCTEX_LOG + +# define SYNCTEX_LOG(NODE) SYNCTEX_MSG_SEND(NODE,log) + +/* Public node logger */ +void synctex_node_log(synctex_node_t node) { + SYNCTEX_LOG(node); +} + +void _synctex_log_sheet(synctex_node_t sheet) { + if(sheet) { + printf("%s:%i\n",synctex_node_isa(sheet),SYNCTEX_PAGE(sheet)); + printf("SELF:%p",sheet); + printf(" SYNCTEX_PARENT:%p",SYNCTEX_PARENT(sheet)); + printf(" SYNCTEX_CHILD:%p",SYNCTEX_CHILD(sheet)); + printf(" SYNCTEX_SIBLING:%p",SYNCTEX_SIBLING(sheet)); + printf(" SYNCTEX_FRIEND:%p\n",SYNCTEX_FRIEND(sheet)); + } +} + +void _synctex_log_medium_node(synctex_node_t node) { + printf("%s:%i,%i:%i,%i:%i\n", + synctex_node_isa(node), + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node)); + printf("SELF:%p",node); + printf(" SYNCTEX_PARENT:%p",SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",SYNCTEX_FRIEND(node)); +} + +void _synctex_log_void_box(synctex_node_t node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%i",SYNCTEX_LINE(node)); + printf(",%i",0); + printf(":%i",SYNCTEX_HORIZ(node)); + printf(",%i",SYNCTEX_VERT(node)); + printf(":%i",SYNCTEX_WIDTH(node)); + printf(",%i",SYNCTEX_HEIGHT(node)); + printf(",%i",SYNCTEX_DEPTH(node)); + printf("\nSELF:%p",node); + printf(" SYNCTEX_PARENT:%p",SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",SYNCTEX_FRIEND(node)); +} + +void _synctex_log_box(synctex_node_t node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%i",SYNCTEX_LINE(node)); + printf(",%i",0); + printf(":%i",SYNCTEX_HORIZ(node)); + printf(",%i",SYNCTEX_VERT(node)); + printf(":%i",SYNCTEX_WIDTH(node)); + printf(",%i",SYNCTEX_HEIGHT(node)); + printf(",%i",SYNCTEX_DEPTH(node)); + printf("\nSELF:%p",node); + printf(" SYNCTEX_PARENT:%p",SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",SYNCTEX_FRIEND(node)); +} + +void _synctex_log_horiz_box(synctex_node_t node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%i",SYNCTEX_LINE(node)); + printf(",%i",0); + printf(":%i",SYNCTEX_HORIZ(node)); + printf(",%i",SYNCTEX_VERT(node)); + printf(":%i",SYNCTEX_WIDTH(node)); + printf(",%i",SYNCTEX_HEIGHT(node)); + printf(",%i",SYNCTEX_DEPTH(node)); + printf(":%i",SYNCTEX_HORIZ_V(node)); + printf(",%i",SYNCTEX_VERT_V(node)); + printf(":%i",SYNCTEX_WIDTH_V(node)); + printf(",%i",SYNCTEX_HEIGHT_V(node)); + printf(",%i",SYNCTEX_DEPTH_V(node)); + printf("\nSELF:%p",node); + printf(" SYNCTEX_PARENT:%p",SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",SYNCTEX_FRIEND(node)); +} + +void _synctex_log_input(synctex_node_t node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%s",SYNCTEX_NAME(node)); + printf(" SYNCTEX_SIBLING:%p",SYNCTEX_SIBLING(node)); +} + +# define SYNCTEX_DISPLAY(NODE) SYNCTEX_MSG_SEND(NODE,display) + +void _synctex_display_sheet(synctex_node_t sheet) { + if(sheet) { + printf("....{%i\n",SYNCTEX_PAGE(sheet)); + SYNCTEX_DISPLAY(SYNCTEX_CHILD(sheet)); + printf("....}\n"); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(sheet)); + } +} + +void _synctex_display_vbox(synctex_node_t node) { + printf("....[%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_DISPLAY(SYNCTEX_CHILD(node)); + printf("....]\n"); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_hbox(synctex_node_t node) { + printf("....(%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_DISPLAY(SYNCTEX_CHILD(node)); + printf("....)\n"); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_void_vbox(synctex_node_t node) { + printf("....v%i,%i;%i,%i:%i,%i,%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_void_hbox(synctex_node_t node) { + printf("....h%i,%i:%i,%i:%i,%i,%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_glue(synctex_node_t node) { + printf("....glue:%i,%i:%i,%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node)); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_math(synctex_node_t node) { + printf("....math:%i,%i:%i,%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node)); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_kern(synctex_node_t node) { + printf("....kern:%i,%i:%i,%i:%i\n", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node)); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +void _synctex_display_input(synctex_node_t node) { + printf("....Input:%i:%s\n", + SYNCTEX_TAG(node), + SYNCTEX_NAME(node)); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); +} + +# pragma mark - +# pragma mark SCANNER + +/* Here are gathered all the possible status that the next scanning functions will return. + * All these functions return a status, and pass their result through pointers. + * Negative values correspond to errors. + * The management of the buffer is causing some significant overhead. + * Every function that may access the buffer returns a status related to the buffer and file state. + * status >= SYNCTEX_STATUS_OK means the function worked as expected + * status < SYNCTEX_STATUS_OK means the function did not work as expected + * status == SYNCTEX_STATUS_NOT_OK means the function did not work as expected but there is still some material to parse. + * status == SYNCTEX_STATUS_EOF means the function did not work as expected and there is no more material. + * status<SYNCTEX_STATUS_EOF means an error + */ +typedef int synctex_status_t; +/* When the end of the synctex file has been reached: */ +# define SYNCTEX_STATUS_EOF 0 +/* When the function could not return the value it was asked for: */ +# define SYNCTEX_STATUS_NOT_OK (SYNCTEX_STATUS_EOF+1) +/* When the function returns the value it was asked for: */ +# define SYNCTEX_STATUS_OK (SYNCTEX_STATUS_NOT_OK+1) +/* Generic error: */ +# define SYNCTEX_STATUS_ERROR -1 +/* Parameter error: */ +# define SYNCTEX_STATUS_BAD_ARGUMENT -2 + +# define SYNCTEX_FILE scanner->file + +/* Actually, the minimum buffer size is driven by integer and float parsing. + * ±0.123456789e123 + */ +# define SYNCTEX_BUFFER_MIN_SIZE 16 +# define SYNCTEX_BUFFER_SIZE 32768 + +#include <stdarg.h> + +inline static int _synctex_error(char * reason,...) { + va_list arg; + int result; + result = fprintf(stderr,"SyncTeX ERROR: "); + va_start (arg, reason); + result += vfprintf(stderr, reason, arg); + va_end (arg); + result = fprintf(stderr,"\n"); + return result; +} + +/* Try to ensure that the buffer contains at least size bytes. + * Passing a huge size argument means the whole buffer length. + * Passing a null size argument means return the available buffer length, without reading the file. + * In that case, the return status is always SYNCTEX_STATUS_OK unless the given scanner is NULL, + * in which case, SYNCTEX_STATUS_BAD_ARGUMENT is returned. + * The value returned in size_ptr is the number of bytes now available in the buffer. + * This is a nonnegative integer, it may take the value 0. + * It is the responsibility of the caller to test whether this size is conforming to its needs. + * Negative values may returned in case of error, actually + * when there was an error reading the synctex file. */ +synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr) { + size_t available = 0; + if(NULL == scanner || NULL == size_ptr) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +# define size (* size_ptr) + if(size>SYNCTEX_BUFFER_SIZE){ + size = SYNCTEX_BUFFER_SIZE; + } + available = SYNCTEX_END - SYNCTEX_CUR; /* available is the number of unparsed chars in the buffer */ + if(size<=available) { + /* There are already sufficiently many characters in the buffer */ + size = available; + return SYNCTEX_STATUS_OK; + } + if(SYNCTEX_FILE) { + /* Copy the remaining part of the buffer to the beginning, + * then read the next part of the file */ + int read = 0; + if(available) { + memmove(SYNCTEX_START, SYNCTEX_CUR, available); + } + SYNCTEX_CUR = SYNCTEX_START + available; /* the next character after the move, will change. */ + /* Fill the buffer up to its end */ + read = gzread(SYNCTEX_FILE,(void *)SYNCTEX_CUR,SYNCTEX_BUFFER_SIZE - available); + if(read>0) { + /* We assume that 0<read<=SYNCTEX_BUFFER_SIZE - available, such that + * SYNCTEX_CUR + read = SYNCTEX_START + available + read <= SYNCTEX_START + SYNCTEX_BUFFER_SIZE */ + SYNCTEX_END = SYNCTEX_CUR + read; + /* If the end of the file was reached, all the required SYNCTEX_BUFFER_SIZE - available + * may not be filled with values from the file. + * In that case, the buffer should stop properly after read characters. */ + * SYNCTEX_END = '\0'; + SYNCTEX_CUR = SYNCTEX_START; + size = SYNCTEX_END - SYNCTEX_CUR; /* == old available + read*/ + return SYNCTEX_STATUS_OK; /* May be available is less than size, the caller will have to test. */ + } else if(read<0) { + /*There is an error */ + _synctex_error("gzread error (1:%i)",read); + return SYNCTEX_STATUS_ERROR; + } else { + /* Nothing was read, we are at the end of the file. */ + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + SYNCTEX_END = SYNCTEX_CUR; + SYNCTEX_CUR = SYNCTEX_START; + * SYNCTEX_END = '\0';/* Terminate the string properly.*/ + size = SYNCTEX_END - SYNCTEX_CUR; + return SYNCTEX_STATUS_EOF; /* there might be a bit of text left */ + } + /* At this point, the function has already returned from above */ + } + /* We cannot enlarge the buffer because the end of the file was reached. */ + size = available; + return SYNCTEX_STATUS_EOF; +# undef size +} + +/* Used when parsing the synctex file. + * Advance to the next character starting a line. + * Actually, only '\n' is recognized as end of line marker. + * On normal completion, the returned value is the number of unparsed characters available in the buffer. + * In general, it is a positive value, 0 meaning that the end of file was reached. + * -1 is returned in case of error, actually because there was an error while feeding the buffer. + * When the function returns with no error, SYNCTEX_CUR points to the first character of the next line, if any. + * J. Laurens: Sat May 10 07:52:31 UTC 2008 + */ +synctex_status_t _synctex_next_line(synctex_scanner_t scanner) { + synctex_status_t status = SYNCTEX_STATUS_OK; + size_t available = 0; + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +infinite_loop: + while(SYNCTEX_CUR<SYNCTEX_END) { + if(*SYNCTEX_CUR == '\n') { + ++SYNCTEX_CUR; + available = 1; + return _synctex_buffer_get_available_size(scanner, &available); + } + ++SYNCTEX_CUR; + } + /* Here, we have SYNCTEX_CUR == SYNCTEX_END, such that the next call to _synctex_buffer_get_available_size + * will read another bunch of synctex file. Little by little, we advance to the end of the file. */ + available = 1; + status = _synctex_buffer_get_available_size(scanner, &available); + if(status<=0) { + return status; + } + goto infinite_loop; +} + +/* Scan the given string. + * Both scanner and the_string must not be NULL, and the_string must not be 0 length. + * SYNCTEX_STATUS_OK is returned if the string is found, + * SYNCTEX_STATUS_EOF is returned when the EOF is reached, + * SYNCTEX_STATUS_NOT_OK is returned is the string is not found, + * an error status is returned otherwise. + * This is a critical method because buffering renders things more difficult. + * The given string might be as long as the maximum size_t value. + * As side effect, the buffer state may have changed if the given argument string can't fit into the buffer. + */ +synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string) { + size_t tested_len = 0; /* the number of characters at the beginning of the_string that match */ + size_t remaining_len = 0; /* the number of remaining characters of the_string that should match */ + size_t available = 0; + synctex_status_t status = 0; + if(NULL == scanner || NULL == the_string) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + remaining_len = strlen(the_string); /* All the_string should match */ + if(0 == remaining_len) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* How many characters available in the buffer? */ + available = remaining_len; + status = _synctex_buffer_get_available_size(scanner,&available); + if(status<SYNCTEX_STATUS_EOF) { + return status; + } + /* Maybe we have less characters than expected because the buffer is too small. */ + if(available>=remaining_len) { + /* The buffer is sufficiently big to hold the expected number of characters. */ + if(strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) { + return SYNCTEX_STATUS_NOT_OK; + } +return_OK: + /* Advance SYNCTEX_CUR to the next character after the_string. */ + SYNCTEX_CUR += remaining_len; + return SYNCTEX_STATUS_OK; + } else if(strncmp((char *)SYNCTEX_CUR,the_string,available)) { + /* No need to goo further, this is not the expected string in the buffer. */ + return SYNCTEX_STATUS_NOT_OK; + } else if(SYNCTEX_FILE) { + /* The buffer was too small to contain remaining_len characters. + * We have to cut the string into pieces. */ + z_off_t offset = 0L; + /* the first part of the string is found, advance the_string to the next untested character. */ + the_string += available; + /* update the remaining length and the parsed length. */ + remaining_len -= available; + tested_len += available; + SYNCTEX_CUR += available; /* We validate the tested characters. */ + if(0 == remaining_len) { + /* Nothing left to test, we have found the given string, we return the length. */ + return tested_len; + } + /* We also have to record the current state of the file cursor because + * if the_string does not match, all this should be a totally blank operation, + * for which the file and buffer states should not be modified at all. + * In fact, the states of the buffer before and after this function are in general different + * but they are totally equivalent as long as the values of the buffer before SYNCTEX_CUR + * can be safely discarded. */ + offset = gztell(SYNCTEX_FILE); + /* offset now corresponds to the first character of the file that was not buffered. */ + available = SYNCTEX_CUR - SYNCTEX_START; /* available can be used as temporary placeholder. */ + /* available now corresponds to the number of chars that where already buffered and + * that match the head of the_string. If in fine the_string does not match, all these chars must be recovered + * because the buffer contents is completely replaced by _synctex_buffer_get_available_size. + * They were buffered from offset-len location in the file. */ + offset -= available; +more_characters: + /* There is still some work to be done, so read another bunch of file. + * This is the second call to _synctex_buffer_get_available_size, + * which means that the actual contents of the buffer will be discarded. + * We will definitely have to recover the previous state in case we do not find the expected string. */ + available = remaining_len; + status = _synctex_buffer_get_available_size(scanner,&available); + if(status<SYNCTEX_STATUS_EOF) { + return status; /* This is an error, no need to go further. */ + } + if(available==0) { + /* Missing characters: recover the initial state of the file and return. */ +return_NOT_OK: + if(offset != gzseek(SYNCTEX_FILE,offset,SEEK_SET)) { + /* This is a critical error, we could not recover the previous state. */ + fprintf(stderr,"SyncTeX critical ERROR: can't seek file\n"); + return SYNCTEX_STATUS_ERROR; + } + /* Next time we are asked to fill the buffer, + * we will read a complete bunch of text from the file. */ + SYNCTEX_CUR = SYNCTEX_END; + return SYNCTEX_STATUS_NOT_OK; + } + if(available<remaining_len) { + /* We'll have to loop one more time. */ + if(strncmp((char *)SYNCTEX_CUR,the_string,available)) { + /* This is not the expected string, recover the previous state and return. */ + goto return_NOT_OK; + } + /* Advance the_string to the first untested character. */ + the_string += available; + /* update the remaining length and the parsed length. */ + remaining_len -= available; + tested_len += available; + SYNCTEX_CUR += available; /* We validate the tested characters. */ + if(0 == remaining_len) { + /* Nothing left to test, we have found the given string. */ + return SYNCTEX_STATUS_OK; + } + goto more_characters; + } + /* This is the last step. */ + if(strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) { + /* This is not the expected string, recover the previous state and return. */ + goto return_NOT_OK; + } + goto return_OK; + } else { + /* The buffer can't contain the given string argument, and the EOF was reached */ + return SYNCTEX_STATUS_EOF; + } +} + +/* Used when parsing the synctex file. + * Decode an integer. + * First, field separators, namely ':' and ',' characters are skipped + * The returned value is -1 if there is an unrecoverable error. + * It is 0 if an integer could not be parsed, for example + * if the characters at the current cursor position are not digits or + * if the end of the file has been reached. + * It is SYNCTEX_STATUS_OK if an int has been successfully parsed. + * The given scanner argument must not be NULL, on the contrary, value_ref may be NULL. + */ +synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref) { + unsigned char * ptr = NULL; + unsigned char * end = NULL; + int result = 0; + size_t available = 0; + synctex_status_t status = 0; + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + available = SYNCTEX_BUFFER_MIN_SIZE; + status = _synctex_buffer_get_available_size(scanner, &available); + if(status<SYNCTEX_STATUS_EOF) { + return status;/* Forward error. */ + } + if(available==0) { + return SYNCTEX_STATUS_EOF;/* it is the end of file. */ + } + ptr = SYNCTEX_CUR; + if(*ptr==':' || *ptr==',') { + ++ptr; + --available; + if(available==0) { + return SYNCTEX_STATUS_NOT_OK;/* It is not possible to scan an int */ + } + } + result = (int)strtol((char *)ptr, (char **)&end, 10); + if(end>ptr) { + SYNCTEX_CUR = end; + if(value_ref) { + * value_ref = result; + } + return SYNCTEX_STATUS_OK;/* Successfully scanned an int */ + } + return SYNCTEX_STATUS_NOT_OK;/* Could not scan an int */ +} + +/* The purpose of this function is to read a string. + * A string is an array of characters from the current parser location + * and before the next '\n' character. + * If a string was properly decoded, it is returned in value_ref and + * the cursor points to the new line marker. + * The returned string was alloced on the heap, the caller is the owner and + * is responsible to free it in due time. + * If no string is parsed, * value_ref is undefined. + * The maximum length of a string that a scanner can decode is platform dependent, namely UINT_MAX. + * If you just want to blindly parse the file up to the end of the current line, + * use _synctex_next_line instead. + * On return, the scanner cursor is unchanged if a string could not be scanned or + * points to the terminating '\n' character otherwise. As a consequence, + * _synctex_next_line is necessary after. + * If either scanner or value_ref is NULL, it is considered as an error and + * SYNCTEX_STATUS_BAD_ARGUMENT is returned. + */ +synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref) { + unsigned char * end = NULL; + size_t current_size = 0; + size_t new_size = 0; + size_t len = 0;/* The number of bytes to copy */ + size_t available = 0; + synctex_status_t status = 0; + if(NULL == scanner || NULL == value_ref) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* The buffer must at least contain one character: the '\n' end of line marker */ + if(SYNCTEX_CUR>=SYNCTEX_END) { + available = 1; + status = _synctex_buffer_get_available_size(scanner,&available); + if(status < 0) { + return status; + } + if(0 == available) { + return SYNCTEX_STATUS_EOF; + } + } + /* Now we are sure that there is at least one available character, either because + * SYNCTEX_CUR was already < SYNCTEX_END, or because the buffer has been properly filled. */ + /* end will point to the next unparsed '\n' character in the file, when mapped to the buffer. */ + end = SYNCTEX_CUR; + * value_ref = NULL;/* Initialize, it will be realloc'ed */ + /* We scan all the characters up to the next '\n' */ +next_character: + if(end<SYNCTEX_END) { + if(*end == '\n') { + /* OK, we found where to stop */ + len = end - SYNCTEX_CUR; + if(current_size>UINT_MAX-len-1) { + /* But we have reached the limit: we do not have current_size+len+1>UINT_MAX. + * We return the missing amount of memory. + * This will never occur in practice. */ + return UINT_MAX-len-1 - current_size; + } + new_size = current_size+len; + /* We have current_size+len+1<=UINT_MAX + * or equivalently new_size<UINT_MAX, + * where we have assumed that len<UINT_MAX */ + if((* value_ref = realloc(* value_ref,new_size+1)) != NULL) { + if(memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) { + (* value_ref)[new_size]='\0'; /* Terminate the string */ + SYNCTEX_CUR += len;/* Advance to the terminating '\n' */ + return SYNCTEX_STATUS_OK; + } + free(* value_ref); + * value_ref = NULL; + _synctex_error("could not copy memory (1)."); + return SYNCTEX_STATUS_ERROR; + } + _synctex_error("could not allocate memory (1)."); + return SYNCTEX_STATUS_ERROR; + } else { + ++end; + goto next_character; + } + } else { + /* end == SYNCTEX_END */ + len = SYNCTEX_END - SYNCTEX_CUR; + if(current_size>UINT_MAX-len-1) { + /* We have reached the limit. */ + _synctex_error("limit reached (missing %i).",current_size-(UINT_MAX-len-1)); + return SYNCTEX_STATUS_ERROR; + } + new_size = current_size+len; + if((* value_ref = realloc(* value_ref,new_size+1)) != NULL) { + if(memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) { + (* value_ref)[new_size]='\0'; /* Terminate the string */ + SYNCTEX_CUR = SYNCTEX_END;/* Advance the cursor to the end of the bufer */ + return SYNCTEX_STATUS_OK; + } + free(* value_ref); + * value_ref = NULL; + _synctex_error("could not copy memory (2)."); + return SYNCTEX_STATUS_ERROR; + } + /* Huge memory problem */ + _synctex_error("could not allocate memory (2)."); + return SYNCTEX_STATUS_ERROR; + } +} + +/* Used when parsing the synctex file. + * Read an Input record. + */ +synctex_status_t _synctex_scan_input(synctex_scanner_t scanner) { + synctex_status_t status = 0; + size_t available = 0; + synctex_node_t input = NULL; + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + status = _synctex_match_string(scanner,"Input:"); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + /* Create a node */ + input = _synctex_new_input(scanner); + if(NULL == input) { + _synctex_error("could not create an input node."); + return SYNCTEX_STATUS_ERROR; + } + /* Decode the synctag */ + status = _synctex_decode_int(scanner,&(SYNCTEX_TAG(input))); + if(status<SYNCTEX_STATUS_OK) { + _synctex_error("bad format of input node."); + SYNCTEX_FREE(input); + return status; + } + /* The next character is a field separator, we expect one character in the buffer. */ + available = 1; + status = _synctex_buffer_get_available_size(scanner, &available); + if(status<=SYNCTEX_STATUS_ERROR) { + return status; + } + if(0 == available) { + return SYNCTEX_STATUS_EOF; + } + /* We can now safely advance to the next character, stepping over the field separator. */ + ++SYNCTEX_CUR; + --available; + /* Then we scan the file name */ + status = _synctex_decode_string(scanner,&(SYNCTEX_NAME(input))); + if(status<SYNCTEX_STATUS_OK) { + SYNCTEX_FREE(input); + return status; + } + /* Prepend this input node to the input linked list of the scanner */ + SYNCTEX_SET_SIBLING(input,scanner->input); + scanner->input = input; + return _synctex_next_line(scanner);/* read the line termination character, if any */ + /* Now, set up the path */ +} + +typedef synctex_status_t (*synctex_decoder_t)(synctex_scanner_t,void *); + +/* Used when parsing the synctex file. + * Read one of the settings. + * On normal completion, returns SYNCTEX_STATUS_OK. + * On error, returns SYNCTEX_STATUS_ERROR. + * Both arguments must not be NULL. + * On return, the scanner points to the next character after the decoded object whatever it is. + * It is the responsibility of the caller to prepare the scanner for the next line. + */ +synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,char * name,void * value_ref,synctex_decoder_t decoder) { + synctex_status_t status = 0; + if(NULL == scanner || NULL == name || NULL == value_ref || NULL == decoder) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +not_found: + status = _synctex_match_string(scanner,name); + if(status<SYNCTEX_STATUS_NOT_OK) { + return status; + } else if(status == SYNCTEX_STATUS_NOT_OK) { + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + goto not_found; + } + /* A line is found, scan the value */ + return (*decoder)(scanner,value_ref); +} + +/* Used when parsing the synctex file. + * Read the preamble. + */ +synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner) { + synctex_status_t status = 0; + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + status = _synctex_scan_named(scanner,"SyncTeX Version:",&(scanner->version),(synctex_decoder_t)&_synctex_decode_int); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + /* Read all the input records */ + do { + status = _synctex_scan_input(scanner); + if(status<SYNCTEX_STATUS_NOT_OK) { + return status; + } + } while(status == SYNCTEX_STATUS_OK); + /* the loop exits when status == SYNCTEX_STATUS_NOT_OK */ + /* Now read all the required settings. */ + status = _synctex_scan_named(scanner,"Output:",&(scanner->output_fmt),(synctex_decoder_t)&_synctex_decode_string); + if(status<SYNCTEX_STATUS_NOT_OK) { + return status; + } + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_scan_named(scanner,"Magnification:",&(scanner->pre_magnification),(synctex_decoder_t)&_synctex_decode_int); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_scan_named(scanner,"Unit:",&(scanner->pre_unit),(synctex_decoder_t)&_synctex_decode_int); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_scan_named(scanner,"X Offset:",&(scanner->pre_x_offset),(synctex_decoder_t)&_synctex_decode_int); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_scan_named(scanner,"Y Offset:",&(scanner->pre_y_offset),(synctex_decoder_t)&_synctex_decode_int); + if(status<SYNCTEX_STATUS_OK) { + return status; + } + return _synctex_next_line(scanner); +} + +/* parse a float with a dimension */ +synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref) { + synctex_status_t status = 0; + unsigned char * endptr = NULL; + float f = 0; +#ifdef HAVE_SETLOCALE + char * loc = setlocale(LC_NUMERIC, NULL); +#endif + size_t available = 0; + if(NULL == scanner || NULL == value_ref) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + available = SYNCTEX_BUFFER_MIN_SIZE; + status = _synctex_buffer_get_available_size(scanner, &available); + if(status<SYNCTEX_STATUS_EOF) { + _synctex_error("problem with float."); + return status; + } +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, "C"); +#endif + f = strtod((char *)SYNCTEX_CUR,(char **)&endptr); +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, loc); +#endif + if(endptr == SYNCTEX_CUR) { + _synctex_error("a float was expected."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_CUR = endptr; + if((status = _synctex_match_string(scanner,"in")) >= SYNCTEX_STATUS_OK) { + f *= 72.27*65536; + } else if(status<SYNCTEX_STATUS_EOF) { +report_unit_error: + _synctex_error("problem with unit."); + return status; + } else if((status = _synctex_match_string(scanner,"cm")) >= SYNCTEX_STATUS_OK) { + f *= 72.27*65536/2.54; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) { + f *= 72.27*65536/25.4; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) { + f *= 65536.0; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) { + f *= 72.27/72*65536; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) { + f *= 12.0*65536; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) { + f *= 1.0; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) { + f *= 1238.0/1157*65536; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) { + f *= 14856.0/1157*65536; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) { + f *= 685.0/642*65536; + } else if(status<0) { + goto report_unit_error; + } else if((status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) { + f *= 1370.0/107*65536; + } else if(status<0) { + goto report_unit_error; + } + *value_ref = f; + return SYNCTEX_STATUS_OK; +} + +/* parse the post scriptum + * SYNCTEX_STATUS_OK is returned on completion + * a negative error is returned otherwise */ +synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner) { + synctex_status_t status = 0; + unsigned char * endptr = NULL; +#ifdef HAVE_SETLOCALE + char * loc = setlocale(LC_NUMERIC, NULL); +#endif + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* Scan the file until a post scriptum line is found */ +post_scriptum_not_found: + status = _synctex_match_string(scanner,"Post scriptum:"); + if(status<SYNCTEX_STATUS_NOT_OK) { + return status; + } + if(status == SYNCTEX_STATUS_NOT_OK) { + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_EOF) { + return status; + } else if(status<SYNCTEX_STATUS_OK) { + return SYNCTEX_STATUS_OK;/* The EOF is found, we have properly scanned the file */ + } + goto post_scriptum_not_found; + } + /* We found the name, advance to the next line. */ +next_line: + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_EOF) { + return status; + } else if(status<SYNCTEX_STATUS_OK) { + return SYNCTEX_STATUS_OK;/* The EOF is found, we have properly scanned the file */ + } + /* Scanning the information */ + status = _synctex_match_string(scanner,"Magnification:"); + if(status == SYNCTEX_STATUS_OK ) { +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, "C"); +#endif + scanner->unit = strtod((char *)SYNCTEX_CUR,(char **)&endptr); +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, loc); +#endif + if(endptr == SYNCTEX_CUR) { + _synctex_error("bad magnification in the post scriptum, a float was expected."); + return SYNCTEX_STATUS_ERROR; + } + if(scanner->unit<=0) { + _synctex_error("bad magnification in the post scriptum, a positive float was expected."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_CUR = endptr; + goto next_line; + } + if(status<SYNCTEX_STATUS_EOF){ +report_record_problem: + _synctex_error("Problem reading the Post Scriptum records"); + return status; /* echo the error. */ + } + status = _synctex_match_string(scanner,"X Offset:"); + if(status == SYNCTEX_STATUS_OK) { + status = _synctex_scan_float_and_dimension(scanner, &(scanner->x_offset)); + if(status<SYNCTEX_STATUS_OK) { + _synctex_error("problem with X offset in the Post Scriptum."); + return status; + } + goto next_line; + } else if(status<SYNCTEX_STATUS_EOF){ + goto report_record_problem; + } + status = _synctex_match_string(scanner,"Y Offset:"); + if(status==SYNCTEX_STATUS_OK) { + status = _synctex_scan_float_and_dimension(scanner, &(scanner->y_offset)); + if(status<SYNCTEX_STATUS_OK) { + _synctex_error("problem with Y offset in the Post Scriptum."); + return status; + } + goto next_line; + } else if(status<SYNCTEX_STATUS_EOF){ + goto report_record_problem; + } + goto next_line; +} + +/* SYNCTEX_STATUS_OK is returned if the postamble is read + * SYNCTEX_STATUS_NOT_OK is returned if the postamble is not at the current location + * a negative error otherwise + * The postamble comprises the post scriptum section. + */ +int _synctex_scan_postamble(synctex_scanner_t scanner) { + int status = 0; + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + status = _synctex_match_string(scanner,"Postamble:"); + if(status < SYNCTEX_STATUS_OK) { + return status; + } +count_again: + status = _synctex_next_line(scanner); + if(status < SYNCTEX_STATUS_OK) { + return status; + } + status = _synctex_scan_named(scanner,"Count:",&(scanner->count),(synctex_decoder_t)&_synctex_decode_int); + if(status < SYNCTEX_STATUS_EOF) { + return status; /* forward the error */ + } else if(status < SYNCTEX_STATUS_OK) { /* No Count record found */ + status = _synctex_next_line(scanner); /* Advance one more line */ + if(status<SYNCTEX_STATUS_OK) { + return status; + } + goto count_again; + } + /* Now we scan the last part of the SyncTeX file: the Post Scriptum section. */ + return _synctex_scan_post_scriptum(scanner); +} + +/* Horizontal boxes also have visible size. + * Visible size are bigger than real size. + * For example 0 width boxes may contain text. + * At creation time, the visible size is set to the values of the real size. + */ +synctex_status_t _synctex_setup_visible_box(synctex_node_t box) { + if(NULL == box || box->class->type != synctex_node_type_hbox) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + if(SYNCTEX_INFO(box) != NULL) { + SYNCTEX_HORIZ_V(box) = SYNCTEX_HORIZ(box); + SYNCTEX_VERT_V(box) = SYNCTEX_VERT(box); + SYNCTEX_WIDTH_V(box) = SYNCTEX_WIDTH(box); + SYNCTEX_HEIGHT_V(box) = SYNCTEX_HEIGHT(box); + SYNCTEX_DEPTH_V(box) = SYNCTEX_DEPTH(box); + return SYNCTEX_STATUS_OK; + } + return SYNCTEX_STATUS_ERROR; +} + +/* This method is sent to an horizontal box to setup the visible size + * Some box have 0 width but do contain text material. + * With this method, one can enlarge the box to contain the given point (h,v). + */ +synctex_status_t _synctex_horiz_box_setup_visible(synctex_node_t node,int h, int v) { +#pragma unused(v) + int itsBtm, itsTop; + if(NULL == node || node->class->type != synctex_node_type_hbox) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + if(SYNCTEX_WIDTH_V(node)<0) { + itsBtm = SYNCTEX_HORIZ_V(node)+SYNCTEX_WIDTH_V(node); + itsTop = SYNCTEX_HORIZ_V(node); + if(h<itsBtm) { + itsBtm -= h; + SYNCTEX_WIDTH_V(node) -= itsBtm; + } + if(h>itsTop) { + h -= itsTop; + SYNCTEX_WIDTH_V(node) -= h; + SYNCTEX_HORIZ_V(node) += h; + } + } else { + itsBtm = SYNCTEX_HORIZ_V(node); + itsTop = SYNCTEX_HORIZ_V(node)+SYNCTEX_WIDTH_V(node); + if(h<itsBtm) { + itsBtm -= h; + SYNCTEX_HORIZ_V(node) -= itsBtm; + SYNCTEX_WIDTH_V(node) += itsBtm; + } + if(h>itsTop) { + h -= itsTop; + SYNCTEX_WIDTH_V(node) += h; + } + } + return SYNCTEX_STATUS_OK; +} + +int synctex_bail(void); + +/* Used when parsing the synctex file. + * The parent is a newly created sheet node that will hold the contents. + * Something is returned in case of error. + */ +synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t parent) { + synctex_node_t child = NULL; + synctex_node_t sibling = NULL; + int friend_index = 0; + synctex_info_t * info = NULL; + int curh, curv; + synctex_status_t status = 0; + size_t available = 0; + if((NULL == scanner) || (NULL == parent)) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +vertical_loop: + if(SYNCTEX_CUR<SYNCTEX_END) { + if(*SYNCTEX_CUR == '[') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_vbox(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { +# define SYNCTEX_DECODE_FAILED(WHAT) \ + (_synctex_decode_int(scanner,&(info[WHAT].INT))<SYNCTEX_STATUS_OK) + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad vbox record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + parent = child; + child = NULL; + goto vertical_loop; + } else { + _synctex_error("Can't create vbox record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == ']') { + ++SYNCTEX_CUR; + if(NULL != parent && parent->class->type == synctex_node_type_vbox) { + #define SYNCTEX_UPDATE_BOX_FRIEND(NODE)\ + friend_index = ((SYNCTEX_INFO(NODE))[SYNCTEX_TAG_IDX].INT+(SYNCTEX_INFO(NODE))[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\ + SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\ + (scanner->lists_of_friends)[friend_index] = NODE; + if(NULL == SYNCTEX_CHILD(parent)) { + /* only void boxes are friends */ + SYNCTEX_UPDATE_BOX_FRIEND(parent); + } + child = parent; + parent = SYNCTEX_PARENT(child); + } else { + _synctex_error("Unexpected ']', ignored."); + } + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Uncomplete sheet."); + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } else if(*SYNCTEX_CUR == '(') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_hbox(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || _synctex_setup_visible_box(child)<SYNCTEX_STATUS_OK + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad hbox record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + parent = child; + child = NULL; + goto vertical_loop; + } else { + _synctex_error("Can't create hbox record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == ')') { + ++SYNCTEX_CUR; + if(NULL != parent && parent->class->type == synctex_node_type_hbox) { + if(NULL == child) { + SYNCTEX_UPDATE_BOX_FRIEND(parent); + } + child = parent; + parent = SYNCTEX_PARENT(child); + } else { + _synctex_error("Unexpected ')', ignored."); + } + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Uncomplete sheet."); + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } else if(*SYNCTEX_CUR == 'v') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_void_vbox(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad void vbox record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + #define SYNCTEX_UPDATE_FRIEND(NODE)\ + friend_index = (info[SYNCTEX_TAG_IDX].INT+info[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\ + SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\ + (scanner->lists_of_friends)[friend_index] = NODE; + SYNCTEX_UPDATE_FRIEND(child); + goto horizontal_loop; + } else { + _synctex_error("Can't create vbox record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == 'h') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_void_hbox(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad void hbox record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + SYNCTEX_UPDATE_FRIEND(child); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child)+synctex_node_width(child),synctex_node_v(child)); + goto horizontal_loop; + } else { + _synctex_error("Can't create void hbox record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == 'k') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_kern(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad kern record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + SYNCTEX_UPDATE_FRIEND(child); + if(NULL == parent) { + _synctex_error("Missing parent for Child"); + synctex_node_log(child); + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } else { + _synctex_error("Can't create kern record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == 'x') { + ++SYNCTEX_CUR; + if(_synctex_decode_int(scanner,NULL)<SYNCTEX_STATUS_OK + || _synctex_decode_int(scanner,NULL)<SYNCTEX_STATUS_OK + || _synctex_decode_int(scanner,&curh)<SYNCTEX_STATUS_OK + || _synctex_decode_int(scanner,&curv)<SYNCTEX_STATUS_OK + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad x record."); + return SYNCTEX_STATUS_ERROR; + } + _synctex_horiz_box_setup_visible(parent,curh,curv); + goto vertical_loop; + } else if(*SYNCTEX_CUR == 'g') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_glue(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad glue record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + SYNCTEX_UPDATE_FRIEND(child); + goto horizontal_loop; + } else { + _synctex_error("Can't create glue record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == '$') { + ++SYNCTEX_CUR; + if(NULL != (child = _synctex_new_math(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad math record."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_CHILD(parent,child); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + SYNCTEX_UPDATE_FRIEND(child); + goto horizontal_loop; + } else { + _synctex_error("Can't create math record."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == '}') { + ++SYNCTEX_CUR; + if(NULL == parent || parent->class->type != synctex_node_type_sheet + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Unexpected end of sheet."); + return SYNCTEX_STATUS_ERROR; + } + return SYNCTEX_STATUS_OK; + } else if(*SYNCTEX_CUR == '!') { + ++SYNCTEX_CUR; + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing anchor."); + return SYNCTEX_STATUS_ERROR; + } + goto vertical_loop; + } else { + /* _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */ + ++SYNCTEX_CUR; + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Unexpected end."); + return SYNCTEX_STATUS_ERROR; + } + goto vertical_loop; + } + } else { + available = 1; + status = _synctex_buffer_get_available_size(scanner,&available); + if(status<SYNCTEX_STATUS_OK && available>0){ + _synctex_error("Uncomplete sheet(0)"); + return SYNCTEX_STATUS_ERROR; + } else { + goto vertical_loop; + } + } + synctex_bail(); +horizontal_loop: + if(SYNCTEX_CUR<SYNCTEX_END) { + if(*SYNCTEX_CUR == '[') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_vbox(scanner)) + && NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad vbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + parent = sibling; + child = NULL; + goto vertical_loop; + } else { + _synctex_error("Can't create vbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == ']') { + ++SYNCTEX_CUR; + if(NULL != parent && parent->class->type == synctex_node_type_vbox) { + if(NULL == child) { + SYNCTEX_UPDATE_BOX_FRIEND(parent); + } + child = parent; + parent = SYNCTEX_PARENT(child); + } else { + _synctex_error("Unexpected end of vbox record (2), ignored."); + } + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Unexpected end of file (2)."); + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } else if(*SYNCTEX_CUR == '(') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_hbox(scanner)) && + NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || _synctex_setup_visible_box(sibling)<SYNCTEX_STATUS_OK + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad hbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + parent = sibling; + child = NULL; + goto vertical_loop; + } else { + _synctex_error("Can't create hbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == ')') { + ++SYNCTEX_CUR; + if(NULL != parent && parent->class->type == synctex_node_type_hbox) { + if(NULL == child) { + SYNCTEX_UPDATE_BOX_FRIEND(parent); + } + child = parent; + parent = SYNCTEX_PARENT(child); + } else { + _synctex_error("Unexpected end of hbox record (2)."); + } + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Unexpected end of file (2,')')."); + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } else if(*SYNCTEX_CUR == 'v') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_void_vbox(scanner)) && + NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad void vbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + SYNCTEX_UPDATE_FRIEND(sibling); + child = sibling; + goto horizontal_loop; + } else { + _synctex_error("can't create void vbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == 'h') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_void_hbox(scanner)) && + NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad void hbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + SYNCTEX_UPDATE_FRIEND(sibling); + child = sibling; + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child)+synctex_node_width(child),synctex_node_v(child)); + goto horizontal_loop; + } else { + _synctex_error("can't create void hbox record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == 'x') { + ++SYNCTEX_CUR; + if(_synctex_decode_int(scanner,NULL)<SYNCTEX_STATUS_OK + || _synctex_decode_int(scanner,NULL)<SYNCTEX_STATUS_OK + || _synctex_decode_int(scanner,&curh)<SYNCTEX_STATUS_OK + || _synctex_decode_int(scanner,&curv)<SYNCTEX_STATUS_OK + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad x record (2)."); + return SYNCTEX_STATUS_ERROR; + } + _synctex_horiz_box_setup_visible(parent,curh,curv); + goto horizontal_loop; + } else if(*SYNCTEX_CUR == 'k') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_kern(scanner)) + && NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad kern record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + SYNCTEX_UPDATE_FRIEND(sibling); + child = sibling; + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + goto horizontal_loop; + } else { + _synctex_error("Can't create kern record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == 'g') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_glue(scanner)) + && NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad glue record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + SYNCTEX_UPDATE_FRIEND(sibling); + child = sibling; + _synctex_horiz_box_setup_visible(parent,synctex_node_h(child),synctex_node_v(child)); + goto horizontal_loop; + } else { + _synctex_error("Can't create glue record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == '$') { + ++SYNCTEX_CUR; + if(NULL != (sibling = _synctex_new_math(scanner)) + && NULL != (info = SYNCTEX_INFO(sibling))) { + if(SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Bad math record (2)."); + return SYNCTEX_STATUS_ERROR; + } + SYNCTEX_SET_SIBLING(child,sibling); + _synctex_horiz_box_setup_visible(parent,synctex_node_h(sibling),synctex_node_v(sibling)); + SYNCTEX_UPDATE_FRIEND(sibling); + child = sibling; + goto horizontal_loop; + } else { + _synctex_error("Can't create math record (2)."); + return SYNCTEX_STATUS_ERROR; + } + } else if(*SYNCTEX_CUR == '}') { + ++SYNCTEX_CUR; + if(NULL == parent || parent->class->type != synctex_node_type_sheet + || (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK)) { + _synctex_error("Unexpected end of sheet (2)."); + return SYNCTEX_STATUS_ERROR; + } + return SYNCTEX_STATUS_OK;/* This is where we exit normally */ + } else if(*SYNCTEX_CUR == '!') { + ++SYNCTEX_CUR; + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Missing anchor (2)."); + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } else { + ++SYNCTEX_CUR; + /* _synctex_error("Ignored record %c(2)\n",*SYNCTEX_CUR); */ + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + return SYNCTEX_STATUS_ERROR; + } + goto horizontal_loop; + } + } else { + available = 1; + status = _synctex_buffer_get_available_size(scanner,&available); + if(status<SYNCTEX_STATUS_OK && available>0){ + goto horizontal_loop; + } else { + _synctex_error("Uncomplete sheet(2)"); + return SYNCTEX_STATUS_ERROR; + } + } +# undef SYNCTEX_DECODE_FAILED +} + +/* Used when parsing the synctex file + */ +synctex_status_t _synctex_scan_content(synctex_scanner_t scanner) { + synctex_node_t sheet = NULL; + synctex_status_t status = 0; + if(NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* set up the lists of friends */ + if(NULL == scanner->lists_of_friends) { + scanner->number_of_lists = 1024; + scanner->lists_of_friends = (synctex_node_t *)_synctex_malloc(scanner->number_of_lists*sizeof(synctex_node_t)); + if(NULL == scanner->lists_of_friends) { + _synctex_error("malloc:2"); + return SYNCTEX_STATUS_ERROR; + } + } + /* Find where this section starts */ +content_not_found: + status = _synctex_match_string(scanner,"Content:"); + if(status<SYNCTEX_STATUS_EOF) { + return status; + } + if(_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { + _synctex_error("Uncomplete Content."); + return SYNCTEX_STATUS_ERROR; + } + if(status == SYNCTEX_STATUS_NOT_OK) { + goto content_not_found; + } +next_sheet: + if(*SYNCTEX_CUR != '{') { + status = _synctex_scan_postamble(scanner); + if(status < SYNCTEX_STATUS_EOF) { + _synctex_error("Bad content."); + return status; + } + if(status<SYNCTEX_STATUS_OK) { + status = _synctex_next_line(scanner); + if(status < SYNCTEX_STATUS_OK) { + _synctex_error("Bad content."); + return status; + } + goto next_sheet; + } + return SYNCTEX_STATUS_OK; + } + ++SYNCTEX_CUR; + /* Create a new sheet node */ + sheet = _synctex_new_sheet(scanner); + status = _synctex_decode_int(scanner,&(SYNCTEX_PAGE(sheet))); + if(status<SYNCTEX_STATUS_OK) { + _synctex_error("Missing sheet number."); +bail: + SYNCTEX_FREE(sheet); + return SYNCTEX_STATUS_ERROR; + } + status = _synctex_next_line(scanner); + if(status<SYNCTEX_STATUS_OK) { + _synctex_error("Uncomplete file."); + goto bail; + } + status = _synctex_scan_sheet(scanner,sheet); + if(status<SYNCTEX_STATUS_OK) { + _synctex_error("Bad sheet content."); + goto bail; + } + SYNCTEX_SET_SIBLING(sheet,scanner->sheet); + scanner->sheet = sheet; + sheet = NULL; + /* Now read the list of Inputs between 2 sheets. */ + do { + status = _synctex_scan_input(scanner); + if(status<SYNCTEX_STATUS_EOF) { + _synctex_error("Bad input section."); + goto bail; + } + } + while(status >= SYNCTEX_STATUS_OK); + goto next_sheet; +} + +/* These are the possible extensions of the synctex file */ +static const char * synctex_suffix = ".synctex"; +static const char * synctex_suffix_gz = ".gz"; + +/* strip the last extension of the given string */ +void synctex_strip_last_path_extension(char * string) { + if(NULL != string){ + char * last_component = NULL; + char * last_extension = NULL; + char * next = NULL; + /* first we find the last path component */ + if(NULL == (last_component = strstr(string,"/"))){ + last_component = string; + } else { + ++last_component; + while(NULL != (next = strstr(last_component,"/"))){ + last_component = next+1; + } + } + /* then we find the last path extension */ + if(NULL != (last_extension = strstr(last_component,"."))){ + ++last_extension; + while(NULL != (next = strstr(last_extension,"."))){ + last_extension = next+1; + } + --last_extension;/* back to the "."*/ + if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/ + last_extension[0] = '\0'; + } + } + } +} + +synctex_scanner_t synctex_scanner_new_with_contents_of_file(const char * name); + +/* Where the synctex scanner is created. + * output is the full or relative path of the uncompressed or compressed synctex file. + * On error, NULL is returned. + * This can be due to allocation error, or an internal inconsistency (bad SYNCTEX_BUFFER_SIZE). */ +synctex_scanner_t synctex_scanner_new_with_output_file(const char * output) { + synctex_scanner_t scanner = NULL; + char * synctex = NULL; + size_t size = 0; + /* Here we assume that int are smaller than void * */ + if(sizeof(int)>sizeof(void*)) { + fputs("SyncTeX INTERNAL INCONSISTENCY: int's are unexpectedly bigger than pointers, bailing out.",stderr); + return NULL; + } + /* now create the synctex file name */ + size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1; + synctex = (char *)malloc(size); + if(NULL == synctex) { + fprintf(stderr,"! synctex_scanner_new_with_output_file: Memory problem (1)\n"); + return NULL; + } + /* we have reserved for synctex enough memory to copy output and both suffices, + * including the terminating character */ + if(synctex != strcpy(synctex,output)) { + fprintf(stderr,"! synctex_scanner_new_with_output_file: Copy problem\n"); +return_on_error: + free(synctex); + return NULL; + } + /* remove the last path extension if any */ + synctex_strip_last_path_extension(synctex); + if(synctex != strcat(synctex,synctex_suffix)){ + fprintf(stderr,"! synctex_scanner_new_with_output_file: Concatenation problem (can't add suffix '%s')\n",synctex_suffix); + goto return_on_error; + } + scanner = synctex_scanner_new_with_contents_of_file(synctex); + if(NULL == scanner) { + if(synctex != strcat(synctex,synctex_suffix_gz)){ + fprintf(stderr,"! synctex_scanner_new_with_output_file: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz); + goto return_on_error; + } + scanner = synctex_scanner_new_with_contents_of_file(synctex); + } + /* make a private copy of output for the scanner */ + if(NULL != scanner) { + if(NULL == (scanner->output = (char *)malloc(strlen(output)+1))){ + fputs("! synctex_scanner_new_with_output_file: Memory problem (2)",stderr); + goto return_on_error; + } + if(scanner->output != strcpy(scanner->output,output)) { + fprintf(stderr,"! synctex_scanner_new_with_output_file: Copy problem\n"); + goto return_on_error; + } + } + free(synctex); + return scanner; +} + +/* Where the synctex scanner is created. + * name is the full path of the uncompressed or compressed synctex file. + * It is not the designated initializer because the scanner's core_path field is not initialized here. + * On error, NULL is returned. + * This can be due to allocation error, or an internal inconsistency (bad SYNCTEX_BUFFER_SIZE). */ +synctex_scanner_t synctex_scanner_new_with_contents_of_file(const char * name) { + synctex_scanner_t scanner = NULL; + synctex_status_t status = 0; + /* We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */ + if(SYNCTEX_BUFFER_SIZE >= UINT_MAX) { + fprintf(stderr,"SyncTeX BUG: Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (1)"); + return NULL; + } + /* for integers: */ + if(SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE) { + fprintf(stderr,"SyncTeX BUG: Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (2)"); + return NULL; + } + scanner = (synctex_scanner_t)_synctex_malloc(sizeof(_synctex_scanner_t)); + if(NULL == scanner) { + fprintf(stderr,"SyncTeX: malloc problem"); + return NULL; + } + scanner->pre_magnification = 1000; + scanner->pre_unit = 8192; + scanner->pre_x_offset = scanner->pre_y_offset = 578; + /* initialize the offset with a fake unprobable value, + * If there is a post scriptum section, this value will be overriden by the real life value */ + scanner->x_offset = scanner->y_offset = 6.027e23; + scanner->class[synctex_node_type_sheet] = synctex_class_sheet; + (scanner->class[synctex_node_type_sheet]).scanner = scanner; + scanner->class[synctex_node_type_vbox] = synctex_class_vbox; + (scanner->class[synctex_node_type_vbox]).scanner = scanner; + scanner->class[synctex_node_type_void_vbox] = synctex_class_void_vbox; + (scanner->class[synctex_node_type_void_vbox]).scanner = scanner; + scanner->class[synctex_node_type_hbox] = synctex_class_hbox; + (scanner->class[synctex_node_type_hbox]).scanner = scanner; + scanner->class[synctex_node_type_void_hbox] = synctex_class_void_hbox; + (scanner->class[synctex_node_type_void_hbox]).scanner = scanner; + scanner->class[synctex_node_type_kern] = synctex_class_kern; + (scanner->class[synctex_node_type_kern]).scanner = scanner; + scanner->class[synctex_node_type_glue] = synctex_class_glue; + (scanner->class[synctex_node_type_glue]).scanner = scanner; + scanner->class[synctex_node_type_math] = synctex_class_math; + (scanner->class[synctex_node_type_math]).scanner = scanner; + scanner->class[synctex_node_type_input] = synctex_class_input; + (scanner->class[synctex_node_type_input]).scanner = scanner; + SYNCTEX_FILE = gzopen(name,"r"); + if(NULL == SYNCTEX_FILE) { + if(errno != ENOENT) { + fprintf(stderr,"SyncTeX: could not open %s, error %i\n",name,errno); + } +bail: + synctex_scanner_free(scanner); + return NULL; + } + SYNCTEX_START = (unsigned char *)malloc(SYNCTEX_BUFFER_SIZE+1); /* one more character for null termination */ + if(NULL == SYNCTEX_START) { + fprintf(stderr,"SyncTeX: malloc error"); + gzclose(SYNCTEX_FILE); + goto bail; + } + SYNCTEX_END = SYNCTEX_START+SYNCTEX_BUFFER_SIZE; + /* SYNCTEX_END always points to a null terminating character. + * Maybe there is another null terminating character between SYNCTEX_CUR and SYNCTEX_END-1. + * At least, we are sure that SYNCTEX_CUR points to a string covering a valid part of the memory. */ + * SYNCTEX_END = '\0'; + SYNCTEX_CUR = SYNCTEX_END; + status = _synctex_scan_preamble(scanner); + if(status<SYNCTEX_STATUS_OK) { + fprintf(stderr,"SyncTeX Error: Bad preamble\n"); +bailey: + gzclose(SYNCTEX_FILE); + goto bail; + } + status = _synctex_scan_content(scanner); + if(status<SYNCTEX_STATUS_OK) { + fprintf(stderr,"SyncTeX Error: Bad content\n"); + goto bailey; + } + /* Everything is finished, free the buffer, close he file */ + free((void *)SYNCTEX_START); + SYNCTEX_START = SYNCTEX_CUR = SYNCTEX_END = NULL; + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + /* Final tuning: set the default values for various parameters */ + /* 1 pre_unit = (scanner->pre_unit)/65536 pt = (scanner->pre_unit)/65781.76 bp + * 1 pt = 65536 sp */ + if(scanner->pre_unit<=0) { + scanner->pre_unit = 8192; + } + if(scanner->pre_magnification<=0) { + scanner->pre_magnification = 1000; + } + if(scanner->unit <= 0) { + /* no post magnification */ + scanner->unit = scanner->pre_unit / 65781.76;/* 65781.76 or 65536.0*/ + } else { + /* post magnification */ + scanner->unit *= scanner->pre_unit / 65781.76; + } + scanner->unit *= scanner->pre_magnification / 1000.0; + if(scanner->x_offset > 6e23) { + /* no post offset */ + scanner->x_offset = scanner->pre_x_offset * (scanner->pre_unit / 65781.76); + scanner->y_offset = scanner->pre_y_offset * (scanner->pre_unit / 65781.76); + } else { + /* post offset */ + scanner->x_offset /= 65781.76; + scanner->y_offset /= 65781.76; + } + return scanner; + #undef SYNCTEX_FILE +} + +/* The scanner destructor + */ +void synctex_scanner_free(synctex_scanner_t scanner) { + if(NULL == scanner) { + return; + } + SYNCTEX_FREE(scanner->sheet); + SYNCTEX_FREE(scanner->input); + free(SYNCTEX_START); + free(scanner->output_fmt); + free(scanner->output); + free(scanner->lists_of_friends); + free(scanner); +} + +/* Scanner accessors. + */ +int synctex_scanner_pre_x_offset(synctex_scanner_t scanner){ + return scanner?scanner->pre_x_offset:0; +} +int synctex_scanner_pre_y_offset(synctex_scanner_t scanner){ + return scanner?scanner->pre_y_offset:0; +} +int synctex_scanner_x_offset(synctex_scanner_t scanner){ + return scanner?scanner->x_offset:0; +} +int synctex_scanner_y_offset(synctex_scanner_t scanner){ + return scanner?scanner->y_offset:0; +} +float synctex_scanner_magnification(synctex_scanner_t scanner){ + return scanner?scanner->unit:1; +} +void synctex_scanner_display(synctex_scanner_t scanner) { + if(NULL == scanner) { + return; + } + printf("The scanner:\noutput:%s\noutput_fmt:%s\nversion:%i\n",scanner->output,scanner->output_fmt,scanner->version); + printf("pre_unit:%i\nx_offset:%i\ny_offset:%i\n",scanner->pre_unit,scanner->pre_x_offset,scanner->pre_y_offset); + printf("count:%i\npost_magnification:%f\npost_x_offset:%f\npost_y_offset:%f\n", + scanner->count,scanner->unit,scanner->x_offset,scanner->y_offset); + printf("The input:\n"); + SYNCTEX_DISPLAY(scanner->input); + if(scanner->count<1000) { + printf("The sheets:\n"); + SYNCTEX_DISPLAY(scanner->sheet); + printf("The friends:\n"); + if(scanner->lists_of_friends) { + int i = scanner->number_of_lists; + synctex_node_t node; + while(i--) { + printf("Friend index:%i\n",i); + node = (scanner->lists_of_friends)[i]; + while(node) { + printf("%s:%i,%i\n", + synctex_node_isa(node), + SYNCTEX_TAG(node), + SYNCTEX_LINE(node) + ); + node = SYNCTEX_FRIEND(node); + } + } + } + } else { + printf("SyncTeX Warning: Too many objects\n"); + } +} +/* Public*/ +const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag) { + synctex_node_t input = NULL; + if(NULL == scanner) { + return NULL; + } + input = scanner->input; + do { + if(tag == SYNCTEX_TAG(input)) { + return (SYNCTEX_NAME(input)); + } + } while((input = SYNCTEX_SIBLING(input)) != NULL); + return NULL; +} +int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) { + synctex_node_t input = NULL; + if(NULL == scanner) { + return 0; + } + input = scanner->input; + do { + if((strlen(name) == strlen((SYNCTEX_NAME(input)))) && + (0 == strncmp(name,(SYNCTEX_NAME(input)),strlen(name)))) { + return SYNCTEX_TAG(input); + } + } while((input = SYNCTEX_SIBLING(input)) != NULL); + return 0; +} +synctex_node_t synctex_scanner_input(synctex_scanner_t scanner) { + return scanner?scanner->input:NULL; +} +const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner) { + return NULL != scanner && scanner->output_fmt?scanner->output_fmt:""; +} +const char * synctex_scanner_get_output(synctex_scanner_t scanner) { + return NULL != scanner && scanner->output?scanner->output:""; +} +#pragma mark - +#pragma mark Public node attributes +float synctex_node_h(synctex_node_t node){ + if(!node) { + return 0; + } + return (float)SYNCTEX_HORIZ(node); +} +float synctex_node_v(synctex_node_t node){ + if(!node) { + return 0; + } + return (float)SYNCTEX_VERT(node); +} +float synctex_node_width(synctex_node_t node){ + if(!node) { + return 0; + } + return (float)SYNCTEX_WIDTH(node); +} +float synctex_node_box_h(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type != synctex_node_type_vbox) + && (node->class->type != synctex_node_type_void_vbox) + && (node->class->type != synctex_node_type_hbox) + && (node->class->type != synctex_node_type_void_hbox)) { + node = SYNCTEX_PARENT(node); + } + return (node->class->type == synctex_node_type_sheet)?0:(float)(SYNCTEX_HORIZ(node)); +} +float synctex_node_box_v(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type != synctex_node_type_vbox) + && (node->class->type != synctex_node_type_void_vbox) + && (node->class->type != synctex_node_type_hbox) + && (node->class->type != synctex_node_type_void_hbox)) { + node = SYNCTEX_PARENT(node); + } + return (node->class->type == synctex_node_type_sheet)?0:(float)(SYNCTEX_VERT(node)); +} +float synctex_node_box_width(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type != synctex_node_type_vbox) + && (node->class->type != synctex_node_type_void_vbox) + && (node->class->type != synctex_node_type_hbox) + && (node->class->type != synctex_node_type_void_hbox)) { + node = SYNCTEX_PARENT(node); + } + return (node->class->type == synctex_node_type_sheet)?0:(float)(SYNCTEX_WIDTH(node)); +} +float synctex_node_box_height(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type != synctex_node_type_vbox) + && (node->class->type != synctex_node_type_void_vbox) + && (node->class->type != synctex_node_type_hbox) + && (node->class->type != synctex_node_type_void_hbox)) { + node = SYNCTEX_PARENT(node); + } + return (node->class->type == synctex_node_type_sheet)?0:(float)(SYNCTEX_HEIGHT(node)); +} +float synctex_node_box_depth(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type != synctex_node_type_vbox) + && (node->class->type != synctex_node_type_void_vbox) + && (node->class->type != synctex_node_type_hbox) + && (node->class->type != synctex_node_type_void_hbox)) { + node = SYNCTEX_PARENT(node); + } + return (node->class->type == synctex_node_type_sheet)?0:(float)(SYNCTEX_DEPTH(node)); +} +#pragma mark - +#pragma mark Public node visible attributes +float synctex_node_visible_h(synctex_node_t node){ + if(!node) { + return 0; + } + return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset; +} +float synctex_node_visible_v(synctex_node_t node){ + if(!node) { + return 0; + } + return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset; +} +float synctex_node_visible_width(synctex_node_t node){ + if(!node) { + return 0; + } + return SYNCTEX_WIDTH(node)*node->class->scanner->unit; +} +float synctex_node_box_visible_h(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type == synctex_node_type_vbox) + || (node->class->type == synctex_node_type_void_hbox) + || (node->class->type == synctex_node_type_void_vbox)) { +result: + return SYNCTEX_WIDTH(node)<0? + (SYNCTEX_HORIZ(node)+SYNCTEX_WIDTH(node))*node->class->scanner->unit+node->class->scanner->x_offset: + SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset; + } + if(node->class->type != synctex_node_type_hbox) { + node = SYNCTEX_PARENT(node); + } + if(node->class->type == synctex_node_type_sheet) { + return 0; + } + if(node->class->type == synctex_node_type_vbox) { + goto result; + } + return SYNCTEX_INFO(node)[SYNCTEX_WIDTH_V_IDX].INT<0? + (SYNCTEX_INFO(node)[SYNCTEX_HORIZ_V_IDX].INT+SYNCTEX_INFO(node)[SYNCTEX_WIDTH_V_IDX].INT)*node->class->scanner->unit+node->class->scanner->x_offset: + SYNCTEX_INFO(node)[SYNCTEX_HORIZ_V_IDX].INT*node->class->scanner->unit+node->class->scanner->x_offset; +} +float synctex_node_box_visible_v(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type == synctex_node_type_vbox) + || (node->class->type == synctex_node_type_void_hbox) + || (node->class->type == synctex_node_type_void_vbox)) { +result: + return (float)(SYNCTEX_VERT(node))*node->class->scanner->unit+node->class->scanner->y_offset; + } + if((node->class->type != synctex_node_type_vbox) + && (node->class->type != synctex_node_type_hbox)) { + node = SYNCTEX_PARENT(node); + } + if(node->class->type == synctex_node_type_sheet) { + return 0; + } + if(node->class->type == synctex_node_type_vbox) { + goto result; + } + return SYNCTEX_INFO(node)[SYNCTEX_VERT_V_IDX].INT*node->class->scanner->unit+node->class->scanner->y_offset; +} +float synctex_node_box_visible_width(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type == synctex_node_type_vbox) + || (node->class->type == synctex_node_type_void_hbox) + || (node->class->type == synctex_node_type_void_vbox)) { +result: + return SYNCTEX_WIDTH(node)<0? + -SYNCTEX_WIDTH(node)*node->class->scanner->unit: + SYNCTEX_WIDTH(node)*node->class->scanner->unit; + } + if(node->class->type != synctex_node_type_hbox) { + node = SYNCTEX_PARENT(node); + } + if(node->class->type == synctex_node_type_sheet) { + return 0; + } + if(node->class->type == synctex_node_type_vbox) { + goto result; + } + return SYNCTEX_INFO(node)[SYNCTEX_WIDTH_V_IDX].INT<0? + -SYNCTEX_INFO(node)[SYNCTEX_WIDTH_V_IDX].INT*node->class->scanner->unit: + SYNCTEX_INFO(node)[SYNCTEX_WIDTH_V_IDX].INT*node->class->scanner->unit; +} +float synctex_node_box_visible_height(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type == synctex_node_type_vbox) + || (node->class->type == synctex_node_type_void_hbox) + || (node->class->type == synctex_node_type_void_vbox)) { +result: + return (float)(SYNCTEX_HEIGHT(node))*node->class->scanner->unit; + } + if(node->class->type != synctex_node_type_hbox) { + node = SYNCTEX_PARENT(node); + } + if(node->class->type == synctex_node_type_sheet) { + return 0; + } + if(node->class->type == synctex_node_type_vbox) { + goto result; + } + return SYNCTEX_INFO(node)[SYNCTEX_HEIGHT_V_IDX].INT*node->class->scanner->unit; +} +float synctex_node_box_visible_depth(synctex_node_t node){ + if(!node) { + return 0; + } + if((node->class->type == synctex_node_type_vbox) + || (node->class->type == synctex_node_type_void_hbox) + || (node->class->type == synctex_node_type_void_vbox)) { +result: + return (float)(SYNCTEX_DEPTH(node))*node->class->scanner->unit; + } + if(node->class->type != synctex_node_type_hbox) { + node = SYNCTEX_PARENT(node); + } + if(node->class->type == synctex_node_type_sheet) { + return 0; + } + if(node->class->type == synctex_node_type_vbox) { + goto result; + } + return SYNCTEX_INFO(node)[SYNCTEX_DEPTH_V_IDX].INT*node->class->scanner->unit; +} +#pragma mark - +#pragma mark Other public node attributes +int synctex_node_page(synctex_node_t node){ + synctex_node_t parent = NULL; + if(!node) { + return -1; + } + parent = SYNCTEX_PARENT(node); + while(parent) { + node = parent; + parent = SYNCTEX_PARENT(node); + } + if(node->class->type == synctex_node_type_sheet) { + return SYNCTEX_PAGE(node); + } + return -1; +} +int synctex_node_tag(synctex_node_t node) { + return node?SYNCTEX_TAG(node):-1; +} +int synctex_node_line(synctex_node_t node) { + return node?SYNCTEX_LINE(node):-1; +} +int synctex_node_column(synctex_node_t node) { +#pragma unused(node) + return -1; +} +#pragma mark - +#pragma mark Query + +synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page) { + if(scanner) { + synctex_node_t sheet = scanner->sheet; + while(sheet) { + if(page == SYNCTEX_PAGE(sheet)) { + return SYNCTEX_CHILD(sheet); + } + sheet = SYNCTEX_SIBLING(sheet); + } + } + return NULL; +} + +int synctex_display_query(synctex_scanner_t scanner,const char * name,int line,int column) { +#pragma unused(column) + int tag = synctex_scanner_get_tag(scanner,name); + size_t size = 0; + int friend_index = 0; + synctex_node_t node = NULL; + if(tag == 0) { + printf("SyncTeX Warning: No tag for %s\n",name); + return -1; + } + free(SYNCTEX_START); + SYNCTEX_CUR = SYNCTEX_END = SYNCTEX_START = NULL; + friend_index = (tag+line)%(scanner->number_of_lists); + node = (scanner->lists_of_friends)[friend_index]; + while(node) { + if((tag == SYNCTEX_TAG(node)) && (line == SYNCTEX_LINE(node))) { + if(SYNCTEX_CUR == SYNCTEX_END) { + size += 16; + SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *)); + SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START; + SYNCTEX_START = SYNCTEX_END; + SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *); + } + *(synctex_node_t *)SYNCTEX_CUR = node; + SYNCTEX_CUR += sizeof(synctex_node_t); + } + node = SYNCTEX_FRIEND(node); + } + SYNCTEX_END = SYNCTEX_CUR; + SYNCTEX_CUR = NULL; + return SYNCTEX_END-SYNCTEX_START; +} + +int _synctex_node_is_box(synctex_node_t node) { + switch(synctex_node_type(node)) { + case synctex_node_type_hbox: + case synctex_node_type_void_hbox: + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + return -1; + default: + return 0; + } +} + +int _synctex_point_in_visible_box(float h, float v, synctex_node_t node) { + if(_synctex_node_is_box(node)) { + v -= synctex_node_box_visible_v(node); + if((v<=-synctex_node_box_visible_height(node) + && v>=synctex_node_box_visible_depth(node)) + || (v>=-synctex_node_box_visible_height(node) + && v<= synctex_node_box_visible_depth(node))) { + h -= synctex_node_box_visible_h(node); + if((h<=0 && h>=synctex_node_box_visible_width(node)) + || (h>=0 && h<=synctex_node_box_visible_width(node))) { + return -1; + } + } + } + return 0; +} + +int synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v) { + synctex_node_t sheet = NULL; + synctex_node_t * start = NULL; + synctex_node_t * end = NULL; + synctex_node_t * ptr = NULL; + size_t size = 0; + synctex_node_t node = NULL; + synctex_node_t next = NULL; + if(NULL == scanner) { + return 0; + } + free(SYNCTEX_START); + SYNCTEX_START = SYNCTEX_END = SYNCTEX_CUR = NULL; + sheet = scanner->sheet; + while(sheet != NULL && SYNCTEX_PAGE(sheet) != page) { + sheet = SYNCTEX_SIBLING(sheet); + } + if(NULL == sheet) { + return -1; + } + /* Now sheet points to the sheet node with proper page number */ + /* Declare memory storage, a buffer to hold found nodes */ + node = SYNCTEX_CHILD(sheet); /* start with the child of the sheet */ +has_node_any_child: + if((next = SYNCTEX_CHILD(node)) != NULL) { + /* node is a non void box */ + if(_synctex_point_in_visible_box(h,v,node)) { + /* we found a non void box containing the point */ + if(ptr == end) { + /* not enough room to store the result, add 16 more node records */ + size += 16; + end = realloc(start,size*sizeof(synctex_node_t)); + if(end == NULL) { + return -1; + } + ptr += end - start; + start = end; + end = start + size; + } + *ptr = node; + /* Does an included box also contain the hit point? + * If this is the case, ptr will be overriden later + * This is why we do not increment ptr yet. + * ptr will be incremented (registered) later if + * no enclosing box contains the hit point */ + } + node = next; + goto has_node_any_child; + } + /* node has no child */ + if(_synctex_point_in_visible_box(h,v,node)) { + /* we found a void box containing the hit point */ + if(ptr == end) { + /* not enough room to store the result, add 16 more node records */ + size += 16; + end = realloc(start,size*sizeof(synctex_node_t)); + if(end == NULL) { + return -1; + } + ptr += end - start; + start = end; + end = start + size*sizeof(synctex_node_t); + } + *ptr = node; + /* Increment ptr to definitely register the node */ + ++ptr; + /* Ensure that it is the last node */ + *ptr = NULL; + } +next_sibling: + if((next = SYNCTEX_SIBLING(node)) != NULL) { + node = next; + goto has_node_any_child; + } + /* This is the last node at this level + * The next step is the parent's sibling */ + next = SYNCTEX_PARENT(node); + if(ptr && *ptr == next) { + /* No included box does contain the point + * next was already tagged to contain the hit point + * but was not fully registered at that time, now we can increment ptr */ + ++ptr; + *ptr = NULL; + } else if(next == sheet) { + float best; + float candidate; + synctex_node_t * best_node_ref = NULL; +we_are_done: + end = ptr; + ptr = NULL; + /* Up to now, we have found a list of boxes enclosing the hit point. */ + if(end == start) { + /* No match found */ + return 0; + } + /* If there are many different boxes containing the hit point, put the smallest one front. + * This is in general the expected box in LaTeX picture environment. */ + ptr = start; + node = *ptr; + best = synctex_node_box_visible_width(node); + while((node = *(++ptr)) != NULL) { + candidate = synctex_node_box_visible_width(node); + if(candidate<best) { + best = candidate; + best_node_ref = ptr; + } + } + if(best_node_ref) { + node = *best_node_ref; + *best_node_ref = *start; + *start = node; + } + /* We do need to check children to find out the node closest to the hit point. + * Working with boxes is not very accurate because in general boxes are created asynchronously. + * The glue, kern, math are more appropriate for synchronization. */ + if((node = SYNCTEX_CHILD(*start)) != NULL) { + synctex_node_t best_node = NULL; + best = HUGE_VAL; + do { + switch((node->class)->type) { + default: + candidate = fabs(synctex_node_visible_h(node)-h); + if(candidate<best) { + best = candidate; + best_node = node; + } + case synctex_node_type_hbox: + case synctex_node_type_vbox: + break; + } + } while((node = SYNCTEX_SIBLING(node)) != NULL); + if(best_node) { + if((SYNCTEX_START = malloc(sizeof(synctex_node_t))) != NULL) { + * (synctex_node_t *)SYNCTEX_START = best_node; + SYNCTEX_END = SYNCTEX_START + sizeof(synctex_node_t); + SYNCTEX_CUR = NULL; + free(start); + return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); + } + } + } + SYNCTEX_START = (unsigned char *)start; + SYNCTEX_END = (unsigned char *)end; + SYNCTEX_CUR = NULL; + return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); + } else if(NULL == next) { + /* What? a node with no parent? */ + _synctex_error("Internal inconsistency, an unexpected node with no parent"); + goto we_are_done; + } + node = next; + goto next_sibling; +} + +synctex_node_t synctex_next_result(synctex_scanner_t scanner) { + if(NULL == SYNCTEX_CUR) { + SYNCTEX_CUR = SYNCTEX_START; + } else { + SYNCTEX_CUR+=sizeof(synctex_node_t); + } + if(SYNCTEX_CUR<SYNCTEX_END) { + return *(synctex_node_t*)SYNCTEX_CUR; + } else { + return NULL; + } +} + +int synctex_bail(void) { + fprintf(stderr,"SyncTeX ERROR\n"); + return -1; +} + +#pragma mark ===== updater + +typedef int (*synctex_fprintf_t)(void *, const char * , ...); /* print formatted to either FILE * or gzFile */ + +# define SYNCTEX_BITS_PER_BYTE 8 + +struct __synctex_updater_t { + void *file; /* the foo.synctex or foo.synctex.gz I/O identifier */ + synctex_fprintf_t fprintf; /* either fprintf or gzprintf */ + int length; /* the number of chars appended */ + struct _flags { + unsigned int no_gz:1; /* Whether zlib is used or not */ + unsigned int reserved:SYNCTEX_BITS_PER_BYTE*sizeof(int)-1; /* Align */ + } flags; +}; +# define SYNCTEX_FILE updater->file +# define SYNCTEX_NO_GZ ((updater->flags).no_gz) +# define SYNCTEX_fprintf (*(updater->fprintf)) +# define SYNCTEX_YES (-1) +# define SYNCTEX_NO (0) + +synctex_updater_t synctex_updater_new_with_output_file(const char * output){ + synctex_updater_t updater = NULL; + char * synctex = NULL; + size_t size = 0; + /* prepare the updater */ + updater = (synctex_updater_t)_synctex_malloc(sizeof(synctex_updater_t)); + if(NULL == updater) { + fprintf(stderr,"! synctex_updater_new_with_file: malloc problem"); + return NULL; + } + size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1; + synctex = (char *)malloc(size); + if(NULL == synctex) { + fprintf(stderr,"! synctex_updater_new_with_output_file: Memory problem (1)\n"); +return_on_error1: + free(updater); + return NULL; + } + /* we have reserved for synctex enough memory to copy output and both suffices, + * including the terminating character */ + if(synctex != strcpy(synctex,output)) { + fprintf(stderr,"! synctex_updater_new_with_output_file: Copy problem\n"); +return_on_error2: + free(synctex); + goto return_on_error1; + } + /* remove the last path extension if any */ + synctex_strip_last_path_extension(synctex); + /* append the synctex suffix */ + if(synctex != strcat(synctex,synctex_suffix)){ + fprintf(stderr,"! synctex_scanner_new_with_output_file: Concatenation problem (can't add suffix '%s')\n",synctex_suffix); + goto return_on_error2; + } + if(NULL != (SYNCTEX_FILE = fopen(synctex,"r"))){ + /* OK, the file exists */ + fclose(SYNCTEX_FILE); + if(NULL == (SYNCTEX_FILE = (void *)fopen(synctex,"a"))) { +no_write_error: + fprintf(stderr,"! synctex_updater_new_with_file: Can't append to %s",synctex); + goto return_on_error2; + } + SYNCTEX_NO_GZ = SYNCTEX_YES; + updater->fprintf = (synctex_fprintf_t)(&fprintf); +return_updater: + printf("SyncTeX: updating %s...",synctex); + free(synctex); + return updater; + } + /* append the gz suffix */ + if(synctex != strcat(synctex,synctex_suffix_gz)){ + fprintf(stderr,"! synctex_scanner_new_with_output_file: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz); + goto return_on_error2; + } + if(NULL != (SYNCTEX_FILE = gzopen(synctex,"r"))){ + gzclose(SYNCTEX_FILE); + if(NULL == (SYNCTEX_FILE = gzopen(synctex,"a"))) { + goto no_write_error; + } + SYNCTEX_NO_GZ = SYNCTEX_NO; + updater->fprintf = (synctex_fprintf_t)(&gzprintf); + goto return_updater; + } + goto return_on_error2; +} + +void synctex_updater_append_magnification(synctex_updater_t updater, char * magnification){ + if(NULL==updater) { + return; + } + if(magnification && strlen(magnification)) { + updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Magnification:%s\n",magnification); + } +} + +void synctex_updater_append_x_offset(synctex_updater_t updater, char * x_offset){ + if(NULL==updater) { + return; + } + if(x_offset && strlen(x_offset)) { + updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"X Offset:%s\n",x_offset); + } +} + +void synctex_updater_append_y_offset(synctex_updater_t updater, char * y_offset){ + if(NULL==updater) { + return; + } + if(y_offset && strlen(y_offset)) { + updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Y Offset:%s\n",y_offset); + } +} + +void synctex_updater_free(synctex_updater_t updater){ + if(NULL==updater) { + return; + } + if(updater->length>0) { + SYNCTEX_fprintf(SYNCTEX_FILE,"!%i\n",updater->length); + } + if (SYNCTEX_NO_GZ) { + fclose((FILE *)SYNCTEX_FILE); + } else { + gzclose((gzFile)SYNCTEX_FILE); + } + free(updater); + printf("... done.\n"); + return; +} + + |