qpdfviewsb/synctex/synctex_parser.c

4413 řádky
162 KiB
C
Surový Normální zobrazení Historie

/*
Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr
This file is part of the SyncTeX package.
Latest Revision: Tue Jun 14 08:23:30 UTC 2011
Version: 1.18
See synctex_parser_readme.txt for more details
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.
Acknowledgments:
----------------
The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
and significant help from XeTeX developer Jonathan Kew
Nota Bene:
----------
If you include or use a significant part of the synctex package into a software,
I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
Version 1
Thu Jun 19 09:39:21 UTC 2008
*/
/* We assume that high level application like pdf viewers will want
* to embed this code as is. We assume that they also have locale.h and setlocale.
* For other tools such as TeXLive tools, you must define SYNCTEX_USE_LOCAL_HEADER,
* when building. You also have to create and customize synctex_parser_local.h to fit your system.
* In particular, the HAVE_LOCALE_H and HAVE_SETLOCALE macros should be properly defined.
* With this design, you should not need to edit this file. */
# if defined(SYNCTEX_USE_LOCAL_HEADER)
# include "synctex_parser_local.h"
# else
# define HAVE_LOCALE_H 1
# define HAVE_SETLOCALE 1
# if defined(_MSC_VER)
# define SYNCTEX_INLINE __inline
# else
# define SYNCTEX_INLINE inline
# endif
# endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#if defined(HAVE_LOCALE_H)
#include <locale.h>
#endif
/* 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"
#include "synctex_parser_utils.h"
/* These are the possible extensions of the synctex file */
const char * synctex_suffix = ".synctex";
const char * synctex_suffix_gz = ".gz";
/* 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;
# if defined(SYNCTEX_USE_CHARINDEX)
# define SYNCTEX_DECLARE_CHARINDEX synctex_charindex_t char_index;
# define SYNCTEX_CHARINDEX(NODE) (NODE->char_index)
# define SYNCTEX_PRINT_CHARINDEX printf("#%i\n",SYNCTEX_CHARINDEX(node))
# define SYNCTEX_DECLARE_CHAR_OFFSET synctex_charindex_t charindex_offset
# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION) NODE->char_index = (synctex_charindex_t)(scanner->charindex_offset+SYNCTEX_CUR-SYNCTEX_START+(CORRECTION));
# else
# define SYNCTEX_DECLARE_CHARINDEX
# define SYNCTEX_CHARINDEX(NODE) 0
# define SYNCTEX_PRINT_CHARINDEX printf("\n")
# define SYNCTEX_DECLARE_CHAR_OFFSET synctex_charindex_t charindex_offset
# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION)
# endif
struct _synctex_node {
SYNCTEX_DECLARE_CHARINDEX
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_node_getter_t next_hbox;
_synctex_info_getter_t info;
};
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Abstract OBJECTS and METHODS
# endif
/* 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. A friend is a kern, math, glue or void box node which tag and line numbers are similar.
* This is a first filter on the nodes that avoids testing all of them.
* Friends are used mainly in forward synchronization aka from source to output.
*/
# 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;\
}
/* Next box getter and setter. The box tree can be traversed from one horizontal box to the other.
* Navigation starts with the deeper boxes.
*/
# define SYNCTEX_NEXT_hbox(NODE) SYNCTEX_GET(NODE,next_hbox)
# define SYNCTEX_SET_NEXT_hbox(NODE,NEXT_HBOX) if (NODE && NEXT_HBOX){\
SYNCTEX_GETTER(NODE,next_hbox)[0]=NEXT_HBOX;\
}
/* 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.
*/
static 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.
* The first sheet is onwned by the scanner.
*/
static void _synctex_free_leaf(synctex_node_t node) {
if (node) {
SYNCTEX_FREE(SYNCTEX_SIBLING(node));
free(node);
}
return;
}
# ifdef __SYNCTEX_WORK__
# include "/usr/include/zlib.h"
# else
# include <zlib.h>
# endif
/* The synctex scanner is the root object.
* Is is initialized with the contents of a text file or a gzipped file.
* The buffer_? are first used to parse the text.
*/
struct __synctex_scanner_t {
gzFile file; /* The (possibly compressed) file */
SYNCTEX_DECLARE_CHAR_OFFSET;
char * buffer_cur; /* current location in the buffer */
char * buffer_start; /* start of the buffer */
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 */
char * synctex; /* the .synctex or .synctex.gz name used to create the scanner */
int version; /* 1, not yet used */
struct {
unsigned has_parsed:1; /* Whether the scanner has parsed its underlying synctex file. */
unsigned reserved:sizeof(unsigned)-1; /* alignment */
} flags;
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_number_of_types]; /* 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)
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark OBJECTS, their creators and destructors.
# endif
/* 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
*/
/* 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
/* This macro defines implementation offsets
* It is only used for pointer values
*/
# define SYNCTEX_MAKE_GET(SYNCTEX_GETTER,OFFSET)\
static 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_MAKE_GET(_synctex_implementation_5,5)
typedef struct {
SYNCTEX_DECLARE_CHARINDEX
synctex_class_t class;
synctex_info_t implementation[3+SYNCTEX_PAGE_IDX+1];/* child, sibling, next box,
* SYNCTEX_PAGE_IDX */
} synctex_node_sheet_t;
/* sheet node creator */
#define DEFINE_synctex_new_NODE(NAME)\
static synctex_node_t _synctex_new_##NAME(synctex_scanner_t scanner) {\
if (scanner) {\
synctex_node_t node = _synctex_malloc(sizeof(synctex_node_##NAME##_t));\
if (node) {\
SYNCTEX_IMPLEMENT_CHARINDEX(node,0);\
++SYNCTEX_CUR;\
node->class = scanner->class+synctex_node_type_##NAME;\
}\
return node;\
}\
return NULL;\
}
DEFINE_synctex_new_NODE(sheet)
static void _synctex_display_sheet(synctex_node_t node);
static void _synctex_log_sheet(synctex_node_t node);
static _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_implementation_2, /* Next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3 /* info */
};
/* 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
# define SYNCTEX_ABS_WIDTH(NODE) ((SYNCTEX_WIDTH(NODE)>0?SYNCTEX_WIDTH(NODE):-SYNCTEX_WIDTH(NODE)))
# define SYNCTEX_ABS_HEIGHT(NODE) ((SYNCTEX_HEIGHT(NODE)>0?SYNCTEX_HEIGHT(NODE):-SYNCTEX_HEIGHT(NODE)))
# define SYNCTEX_ABS_DEPTH(NODE) ((SYNCTEX_DEPTH(NODE)>0?SYNCTEX_DEPTH(NODE):-SYNCTEX_DEPTH(NODE)))
typedef struct {
SYNCTEX_DECLARE_CHARINDEX
synctex_class_t class;
synctex_info_t implementation[5+SYNCTEX_DEPTH_IDX+1]; /* parent,child,sibling,friend,next box,
* SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
* SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH */
} synctex_node_vbox_t;
/* vertical box node creator */
DEFINE_synctex_new_NODE(vbox)
static void _synctex_log_vbox(synctex_node_t node);
static 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 _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_vbox, /* log */
&_synctex_display_vbox, /* display */
&_synctex_implementation_0, /* parent */
&_synctex_implementation_1, /* child */
&_synctex_implementation_2, /* sibling */
&_synctex_implementation_3, /* friend */
&_synctex_implementation_4, /* next hbox */
(_synctex_info_getter_t)&_synctex_implementation_5
};
/* Horizontal boxes must contain visible size, because 0 width does not mean emptiness.
* They also contain an average of the line numbers of the containing nodes. */
# define SYNCTEX_MEAN_LINE_IDX (SYNCTEX_DEPTH_IDX+1)
# define SYNCTEX_NODE_WEIGHT_IDX (SYNCTEX_MEAN_LINE_IDX+1)
# define SYNCTEX_HORIZ_V_IDX (SYNCTEX_NODE_WEIGHT_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_MEAN_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_MEAN_LINE_IDX].INT
# define SYNCTEX_NODE_WEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NODE_WEIGHT_IDX].INT
# 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
# define SYNCTEX_ABS_WIDTH_V(NODE) ((SYNCTEX_WIDTH_V(NODE)>0?SYNCTEX_WIDTH_V(NODE):-SYNCTEX_WIDTH_V(NODE)))
# define SYNCTEX_ABS_HEIGHT_V(NODE) ((SYNCTEX_HEIGHT_V(NODE)>0?SYNCTEX_HEIGHT_V(NODE):-SYNCTEX_HEIGHT_V(NODE)))
# define SYNCTEX_ABS_DEPTH_V(NODE) ((SYNCTEX_DEPTH_V(NODE)>0?SYNCTEX_DEPTH_V(NODE):-SYNCTEX_DEPTH_V(NODE)))
typedef struct {
SYNCTEX_DECLARE_CHARINDEX
synctex_class_t class;
synctex_info_t implementation[5+SYNCTEX_DEPTH_V_IDX+1]; /*parent,child,sibling,friend,next box,
* SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
* SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH,
* SYNCTEX_MEAN_LINE,SYNCTEX_NODE_WEIGHT,
* SYNCTEX_HORIZ_V,SYNCTEX_VERT_V,SYNCTEX_WIDTH_V,SYNCTEX_HEIGHT_V,SYNCTEX_DEPTH_V*/
} synctex_node_hbox_t;
/* horizontal box node creator */
DEFINE_synctex_new_NODE(hbox)
static void _synctex_display_hbox(synctex_node_t node);
static void _synctex_log_hbox(synctex_node_t node);
static _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_hbox, /* log */
&_synctex_display_hbox, /* display */
&_synctex_implementation_0, /* parent */
&_synctex_implementation_1, /* child */
&_synctex_implementation_2, /* sibling */
&_synctex_implementation_3, /* friend */
&_synctex_implementation_4, /* next hbox */
(_synctex_info_getter_t)&_synctex_implementation_5
};
/* This void box node implementation is either horizontal or vertical
* It does not contain a child field.
*/
typedef struct {
SYNCTEX_DECLARE_CHARINDEX
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_node_void_vbox_t;
/* vertical void box node creator */
DEFINE_synctex_new_NODE(void_vbox)
static void _synctex_log_void_box(synctex_node_t node);
static void _synctex_display_void_vbox(synctex_node_t node);
static _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 */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3
};
typedef synctex_node_void_vbox_t synctex_node_void_hbox_t;
/* horizontal void box node creator */
DEFINE_synctex_new_NODE(void_hbox)
static void _synctex_display_void_hbox(synctex_node_t node);
static _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 */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3
};
/* The medium nodes correspond to kern, glue, penalty and math nodes.
* In LuaTeX, the size of the nodes may have changed. */
typedef struct {
SYNCTEX_DECLARE_CHARINDEX
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_node_medium_t;
#define SYNCTEX_IS_BOX(NODE)\
((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))
#define SYNCTEX_HAS_CHILDREN(NODE) (NODE && SYNCTEX_CHILD(NODE))
static void _synctex_log_medium_node(synctex_node_t node);
typedef synctex_node_medium_t synctex_node_math_t;
/* math node creator */
DEFINE_synctex_new_NODE(math)
static void _synctex_display_math(synctex_node_t node);
static _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 */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3
};
typedef synctex_node_medium_t synctex_node_kern_t;
/* kern node creator */
DEFINE_synctex_new_NODE(kern)
static void _synctex_display_kern(synctex_node_t node);
static _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 */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3
};
/* The small nodes correspond to glue and boundary nodes. */
typedef struct {
SYNCTEX_DECLARE_CHARINDEX
synctex_class_t class;
synctex_info_t implementation[3+SYNCTEX_VERT_IDX+1]; /* parent,sibling,friend,
* SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN,
* SYNCTEX_HORIZ,SYNCTEX_VERT */
} synctex_node_small_t;
static void _synctex_log_small_node(synctex_node_t node);
/* glue node creator */
typedef synctex_node_small_t synctex_node_glue_t;
DEFINE_synctex_new_NODE(glue)
static void _synctex_display_glue(synctex_node_t node);
static _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 */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3
};
/* boundary node creator */
typedef synctex_node_small_t synctex_node_boundary_t;
DEFINE_synctex_new_NODE(boundary)
static void _synctex_display_boundary(synctex_node_t node);
static _synctex_class_t synctex_class_boundary = {
NULL, /* No scanner yet */
synctex_node_type_boundary, /* Node type */
&_synctex_new_boundary, /* creator */
&_synctex_free_leaf, /* destructor */
&_synctex_log_small_node, /* log */
&_synctex_display_boundary, /* display */
&_synctex_implementation_0, /* parent */
NULL, /* No child */
&_synctex_implementation_1, /* sibling */
&_synctex_implementation_2, /* friend */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_3
};
# 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_DECLARE_CHARINDEX
synctex_class_t class;
synctex_info_t implementation[1+SYNCTEX_NAME_IDX+1]; /* sibling,
* SYNCTEX_TAG,SYNCTEX_NAME */
} synctex_input_t;
# define SYNCTEX_INPUT_MARK "Input:"
static synctex_node_t _synctex_new_input(synctex_scanner_t scanner) {
if (scanner) {
synctex_node_t node = _synctex_malloc(sizeof(synctex_input_t));
if (node) {
SYNCTEX_IMPLEMENT_CHARINDEX(node,strlen(SYNCTEX_INPUT_MARK));
node->class = scanner->class+synctex_node_type_input;
}
return node;
}
return NULL;
}
static void _synctex_free_input(synctex_node_t node){
if (node) {
SYNCTEX_FREE(SYNCTEX_SIBLING(node));
free(SYNCTEX_NAME(node));
free(node);
}
}
static void _synctex_display_input(synctex_node_t node);
static void _synctex_log_input(synctex_node_t node);
static _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 */
NULL, /* No next hbox */
(_synctex_info_getter_t)&_synctex_implementation_1
};
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Navigation
# endif
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))) {
if (node->class->type == synctex_node_type_sheet) {/* EXC_BAD_ACCESS? */
return NULL;
}
goto sibling;
}
return NULL;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark CLASS
# endif
/* 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_number_of_types] =
{"Not a node","input","sheet","vbox","void vbox","hbox","void hbox","kern","glue","math","boundary"};
return isa[synctex_node_type(node)];
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark SYNCTEX_LOG
# endif
# define SYNCTEX_LOG(NODE) SYNCTEX_MSG_SEND(NODE,log)
/* Public node logger */
void synctex_node_log(synctex_node_t node) {
SYNCTEX_LOG(node);
}
static void _synctex_log_input(synctex_node_t node) {
if (node) {
printf("%s:%i,%s",synctex_node_isa(node),SYNCTEX_TAG(node),SYNCTEX_NAME(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
}
}
static void _synctex_log_sheet(synctex_node_t node) {
if (node) {
printf("%s:%i",synctex_node_isa(node),SYNCTEX_PAGE(node));
SYNCTEX_PRINT_CHARINDEX;
printf("SELF:%p",(void *)node);
printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node));
printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node));
}
}
static void _synctex_log_small_node(synctex_node_t node) {
if (node) {
printf("%s:%i,%i:%i,%i",
synctex_node_isa(node),
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node));
SYNCTEX_PRINT_CHARINDEX;
printf("SELF:%p",(void *)node);
printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
}
}
static void _synctex_log_medium_node(synctex_node_t node) {
if (node) {
printf("%s:%i,%i:%i,%i:%i",
synctex_node_isa(node),
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node),
SYNCTEX_WIDTH(node));
SYNCTEX_PRINT_CHARINDEX;
printf("SELF:%p",(void *)node);
printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
}
}
static void _synctex_log_void_box(synctex_node_t node) {
if (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));
SYNCTEX_PRINT_CHARINDEX;
printf("SELF:%p",(void *)node);
printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node));
}
}
static void _synctex_log_vbox(synctex_node_t node) {
if (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));
SYNCTEX_PRINT_CHARINDEX;
printf("SELF:%p",(void *)node);
printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node));
printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node));
}
}
static void _synctex_log_hbox(synctex_node_t node) {
if (node) {
printf("%s",synctex_node_isa(node));
printf(":%i",SYNCTEX_TAG(node));
printf(",%i~%i*%i",SYNCTEX_LINE(node),SYNCTEX_MEAN_LINE(node),SYNCTEX_NODE_WEIGHT(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));
SYNCTEX_PRINT_CHARINDEX;
printf("SELF:%p",(void *)node);
printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node));
printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node));
printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node));
printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node));
printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node));
}
}
# define SYNCTEX_DISPLAY(NODE) SYNCTEX_MSG_SEND(NODE,display)
void synctex_node_display(synctex_node_t node) {
SYNCTEX_DISPLAY(node);
}
static void _synctex_display_input(synctex_node_t node) {
if (node) {
printf("....Input:%i:%s",
SYNCTEX_TAG(node),
SYNCTEX_NAME(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_sheet(synctex_node_t node) {
if (node) {
printf("....{%i",SYNCTEX_PAGE(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
printf("....}\n");
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_vbox(synctex_node_t node) {
if (node) {
printf("....[%i,%i:%i,%i:%i,%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node),
SYNCTEX_WIDTH(node),
SYNCTEX_HEIGHT(node),
SYNCTEX_DEPTH(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
printf("....]\n");
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_hbox(synctex_node_t node) {
if (node) {
printf("....(%i,%i~%i*%i:%i,%i:%i,%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_MEAN_LINE(node),
SYNCTEX_NODE_WEIGHT(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node),
SYNCTEX_WIDTH(node),
SYNCTEX_HEIGHT(node),
SYNCTEX_DEPTH(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_CHILD(node));
printf("....)\n");
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_void_vbox(synctex_node_t node) {
if (node) {
printf("....v%i,%i;%i,%i:%i,%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node),
SYNCTEX_WIDTH(node),
SYNCTEX_HEIGHT(node),
SYNCTEX_DEPTH(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_void_hbox(synctex_node_t node) {
if (node) {
printf("....h%i,%i:%i,%i:%i,%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node),
SYNCTEX_WIDTH(node),
SYNCTEX_HEIGHT(node),
SYNCTEX_DEPTH(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_glue(synctex_node_t node) {
if (node) {
printf("....glue:%i,%i:%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_math(synctex_node_t node) {
if (node) {
printf("....math:%i,%i:%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_kern(synctex_node_t node) {
if (node) {
printf("....kern:%i,%i:%i,%i:%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node),
SYNCTEX_WIDTH(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
static void _synctex_display_boundary(synctex_node_t node) {
if (node) {
printf("....boundary:%i,%i:%i,%i",
SYNCTEX_TAG(node),
SYNCTEX_LINE(node),
SYNCTEX_HORIZ(node),
SYNCTEX_VERT(node));
SYNCTEX_PRINT_CHARINDEX;
SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node));
}
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark SCANNER
# endif
/* 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
*/
/* 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.
* <EFBFBD>0.123456789e123
*/
# define SYNCTEX_BUFFER_MIN_SIZE 16
# define SYNCTEX_BUFFER_SIZE 32768
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Prototypes
# endif
synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr);
synctex_status_t _synctex_next_line(synctex_scanner_t scanner);
synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string);
synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref);
synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref);
synctex_status_t _synctex_scan_input(synctex_scanner_t scanner);
synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner);
synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref);
synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner);
synctex_status_t _synctex_scan_postamble(synctex_scanner_t scanner);
synctex_status_t _synctex_setup_visible_box(synctex_node_t box);
synctex_status_t _synctex_hbox_setup_visible(synctex_node_t node,int h, int v);
synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t parent);
synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner);
synctex_status_t _synctex_scan_content(synctex_scanner_t scanner);
int synctex_scanner_pre_x_offset(synctex_scanner_t scanner);
int synctex_scanner_pre_y_offset(synctex_scanner_t scanner);
const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner);
int _synctex_node_is_box(synctex_node_t node);
/* 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 return 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 already_read = 0;
# if defined(SYNCTEX_USE_CHARINDEX)
scanner->charindex_offset += SYNCTEX_CUR - SYNCTEX_START;
# endif
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 */
already_read = gzread(SYNCTEX_FILE,(void *)SYNCTEX_CUR,(int)(SYNCTEX_BUFFER_SIZE - available));
if (already_read>0) {
/* We assume that 0<already_read<=SYNCTEX_BUFFER_SIZE - available, such that
* SYNCTEX_CUR + already_read = SYNCTEX_START + available + already_read <= SYNCTEX_START + SYNCTEX_BUFFER_SIZE */
SYNCTEX_END = SYNCTEX_CUR + already_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 already_read characters. */
* SYNCTEX_END = '\0';
SYNCTEX_CUR = SYNCTEX_START;
size = SYNCTEX_END - SYNCTEX_CUR; /* == old available + already_read*/
return SYNCTEX_STATUS_OK; /* May be available is less than size, the caller will have to test. */
} else if (0>already_read) {
/* There is a possible error in reading the file */
int errnum = 0;
const char * error_string = gzerror(SYNCTEX_FILE, &errnum);
if (Z_ERRNO == errnum) {
/* There is an error in zlib caused by the file system */
_synctex_error("gzread error from the file system (%i)",errno);
return SYNCTEX_STATUS_ERROR;
} else if (errnum) {
_synctex_error("gzread error (%i:%i,%s)",already_read,errnum,error_string);
return SYNCTEX_STATUS_ERROR;
}
}
/* 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 */
}
/* 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 go 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. */
_synctex_error("can't seek file");
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 negative if there is an unrecoverable error.
* It is SYNCTEX_STATUS_NOT_OK 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) {
char * ptr = NULL;
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(ptr, &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) {
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,SYNCTEX_INPUT_MARK);
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;
# if SYNCTEX_VERBOSE
synctex_node_log(input);
# endif
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 *);
synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder);
/* 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,const 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;
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(SYNCTEX_CUR,&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.27f*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.27f*65536/2.54f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) {
f *= 72.27f*65536/25.4f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) {
f *= 65536.0f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) {
f *= 72.27f/72*65536.0f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) {
f *= 12.0*65536.0f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) {
f *= 1.0f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) {
f *= 1238.0f/1157*65536.0f;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) {
f *= 14856.0f/1157*65536;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) {
f *= 685.0f/642*65536;
} else if (status<0) {
goto report_unit_error;
} else if ((status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) {
f *= 1370.0f/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;
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(SYNCTEX_CUR,&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.
*/
synctex_status_t _synctex_scan_postamble(synctex_scanner_t scanner) {
synctex_status_t 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 (box) {
switch(box->class->type) {
case synctex_node_type_hbox:
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;
}
}
return SYNCTEX_STATUS_BAD_ARGUMENT;
}
/* 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_hbox_setup_visible(synctex_node_t node,int h, int v) {
# ifdef __DARWIN_UNIX03
# pragma unused(v)
# endif
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);
itsTop = SYNCTEX_HORIZ_V(node)-SYNCTEX_WIDTH_V(node);
if (h<itsBtm) {
SYNCTEX_HORIZ_V(node) = h;
SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - itsTop;
} else if (h>itsTop) {
SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - h;
}
} else {
itsBtm = SYNCTEX_HORIZ_V(node);
itsTop = SYNCTEX_HORIZ_V(node)+SYNCTEX_WIDTH_V(node);
if (h<itsBtm) {
SYNCTEX_HORIZ_V(node) = h;
SYNCTEX_WIDTH_V(node) = itsTop - SYNCTEX_HORIZ_V(node);
} else if (h>itsTop) {
SYNCTEX_WIDTH_V(node) = h - SYNCTEX_HORIZ_V(node);
}
}
return SYNCTEX_STATUS_OK;
}
/* Here are the control characters that strat each line of the synctex output file.
* Their values define the meaning of the line.
*/
# define SYNCTEX_CHAR_BEGIN_SHEET '{'
# define SYNCTEX_CHAR_END_SHEET '}'
# define SYNCTEX_CHAR_BEGIN_VBOX '['
# define SYNCTEX_CHAR_END_VBOX ']'
# define SYNCTEX_CHAR_BEGIN_HBOX '('
# define SYNCTEX_CHAR_END_HBOX ')'
# define SYNCTEX_CHAR_ANCHOR '!'
# define SYNCTEX_CHAR_VOID_VBOX 'v'
# define SYNCTEX_CHAR_VOID_HBOX 'h'
# define SYNCTEX_CHAR_KERN 'k'
# define SYNCTEX_CHAR_GLUE 'g'
# define SYNCTEX_CHAR_MATH '$'
# define SYNCTEX_CHAR_BOUNDARY 'x'
# define SYNCTEX_RETURN(STATUS) return STATUS;
/* Used when parsing the synctex file. A '{' character has just been parsed.
* The purpose is to gobble everything until the closing '}'.
* Actually only one nesting depth has been observed when using the clip option
* of \includegraphics option. Here we use arbitrary level of depth.
*/
synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner) {
unsigned int depth = 0;
deeper:
++depth;
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected end of nested sheet (1).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
scan_next_line:
if (SYNCTEX_CUR<SYNCTEX_END) {
if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
++SYNCTEX_CUR;
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected end of nested sheet (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
if (--depth>0) {
goto scan_next_line;
} else {
SYNCTEX_RETURN(SYNCTEX_STATUS_OK);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
++SYNCTEX_CUR;
goto deeper;
} else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected end of nested sheet (3).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
}
_synctex_error("Unexpected end of nested sheet (4).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
/* Used when parsing the synctex file.
* The sheet argument 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 sheet) {
synctex_node_t parent = sheet;
synctex_node_t child = NULL;
synctex_node_t sibling = NULL;
synctex_node_t box = sheet;
int friend_index = 0;
synctex_info_t * info = NULL;
synctex_status_t status = 0;
size_t available = 0;
if ((NULL == scanner) || (NULL == sheet)) {
return SYNCTEX_STATUS_BAD_ARGUMENT;
}
/* We MUST start with a box, so at this level, the unique possibility is '[', '(' or "}". */
prepare_loop:
if (SYNCTEX_CUR<SYNCTEX_END) {
if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
scan_vbox:
if ((child = _synctex_new_vbox(scanner)) && (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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_CHILD(parent,child);
parent = child;
child = NULL;
goto child_loop;/* next created node will be a child */
} else {
_synctex_error("Can't create vbox record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
scan_hbox:
if ((child = _synctex_new_hbox(scanner)) && (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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
SYNCTEX_SET_CHILD(parent,child);
parent = child;
child = NULL;
goto child_loop;/* next created node will be a child */
} else {
_synctex_error("Can't create hbox record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
scan_teehs:
++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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
# if SYNCTEX_VERBOSE
synctex_node_log(parent);
# endif
SYNCTEX_RETURN(SYNCTEX_STATUS_OK);
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
/* Addendum to version 1.10 to manage nested sheets */
++SYNCTEX_CUR;
if (_synctex_scan_nested_sheet(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected nested sheet.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto prepare_loop;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
scan_anchor:
++SYNCTEX_CUR;
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Missing anchor.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto prepare_loop;
} else {
/* _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */
++SYNCTEX_CUR;
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected end.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto prepare_loop;
}
} else {
available = 1;
status = _synctex_buffer_get_available_size(scanner,&available);
if (status<SYNCTEX_STATUS_OK && available>0){
_synctex_error("Uncomplete sheet(0)");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto prepare_loop;
}
/* The child loop means that we go down one level, when we just created a box node,
* the next node created is a child of this box. */
child_loop:
if (SYNCTEX_CUR<SYNCTEX_END) {
if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
goto scan_vbox;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_VBOX) {
scan_xobv:
++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 end of vbox, ignored.");
}
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Uncomplete sheet.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
goto scan_hbox;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_HBOX) {
scan_xobh:
++SYNCTEX_CUR;
if ((parent) && parent->class->type == synctex_node_type_hbox) {
/* Update the mean line number */
synctex_node_t node = SYNCTEX_CHILD(parent);
if (node) {
unsigned int node_weight = 0;
unsigned int cumulated_line_numbers = 0;
do {
if (synctex_node_type(node)==synctex_node_type_hbox) {
if (SYNCTEX_NODE_WEIGHT(node)) {
node_weight += SYNCTEX_NODE_WEIGHT(node);
cumulated_line_numbers += SYNCTEX_MEAN_LINE(node)*SYNCTEX_NODE_WEIGHT(node);
} else {
++node_weight;
cumulated_line_numbers += SYNCTEX_MEAN_LINE(node);
}
} else {
++node_weight;
cumulated_line_numbers += SYNCTEX_LINE(node);
}
} while ((node = SYNCTEX_SIBLING(node)));
SYNCTEX_MEAN_LINE(parent)=(cumulated_line_numbers + node_weight/2)/node_weight;
SYNCTEX_NODE_WEIGHT(parent)=node_weight;
} else {
SYNCTEX_MEAN_LINE(parent)=SYNCTEX_LINE(parent);
SYNCTEX_NODE_WEIGHT(parent)=1;
}
if (NULL == child) {
/* Only boxes with no children are friends,
* boxes with children are indirectly friends through one of their contained nodes. */
SYNCTEX_UPDATE_BOX_FRIEND(parent);
}
/* setting the next horizontal box at the end ensures that a child is recorded before any of its ancestors. */
SYNCTEX_SET_NEXT_hbox(box,parent);
box = parent;
child = parent;
parent = SYNCTEX_PARENT(child);
} else {
_synctex_error("Unexpected end of hbox, ignored.");
}
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Uncomplete sheet.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_VBOX) {
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.");
SYNCTEX_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);
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else {
_synctex_error("Can't create vbox record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) {
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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_CHILD(parent,child);
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else {
_synctex_error("Can't create void hbox record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_KERN) {
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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_CHILD(parent,child);
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)-SYNCTEX_WIDTH(child),SYNCTEX_VERT(child));
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else {
_synctex_error("Can't create kern record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_GLUE) {
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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_CHILD(parent,child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
SYNCTEX_UPDATE_FRIEND(child);
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else {
_synctex_error("Can't create glue record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_MATH) {
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.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_CHILD(parent,child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
SYNCTEX_UPDATE_FRIEND(child);
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else {
_synctex_error("Can't create math record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BOUNDARY) {
if (NULL != (child = _synctex_new_boundary(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 boundary record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_CHILD(parent,child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
SYNCTEX_UPDATE_FRIEND(child);
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
goto sibling_loop;
} else {
_synctex_error("Can't create math record.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
goto scan_teehs;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) {
/* Addendum to version 1.10 to manage nested sheets */
++SYNCTEX_CUR;
if (_synctex_scan_nested_sheet(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected nested sheet.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto child_loop;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
goto scan_anchor;
} else {
/* _synctex_error("Ignored record %c\n",*SYNCTEX_CUR); */
++SYNCTEX_CUR;
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Unexpected end.");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto child_loop;
}
} else {
available = 1;
status = _synctex_buffer_get_available_size(scanner,&available);
if (status<SYNCTEX_STATUS_OK && available>0){
_synctex_error("Uncomplete sheet(0)");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
} else {
goto child_loop;
}
}
/* The vertical loop means that we are on the same level, for example when we just ended a box.
* If a node is created now, it will be a sibling of the current node, sharing the same parent. */
sibling_loop:
if (SYNCTEX_CUR<SYNCTEX_END) {
if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_VBOX) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
parent = sibling;
child = NULL;
goto child_loop;
} else {
_synctex_error("Can't create vbox record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_VBOX) {
goto scan_xobv;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_HBOX) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
parent = child;
child = NULL;
goto child_loop;
} else {
_synctex_error("Can't create hbox record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_HBOX) {
goto scan_xobh;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_VBOX) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
SYNCTEX_UPDATE_FRIEND(child);
goto sibling_loop;
} else {
_synctex_error("can't create void vbox record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)+SYNCTEX_ABS_WIDTH(child),SYNCTEX_VERT(child));
goto sibling_loop;
} else {
_synctex_error("can't create void hbox record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_KERN) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child)-SYNCTEX_WIDTH(child),SYNCTEX_VERT(child));
goto sibling_loop;
} else {
_synctex_error("Can't create kern record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_GLUE) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
goto sibling_loop;
} else {
_synctex_error("Can't create glue record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_MATH) {
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).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
goto sibling_loop;
} else {
_synctex_error("Can't create math record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BOUNDARY) {
if (NULL != (sibling = _synctex_new_boundary(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 boundary record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
SYNCTEX_SET_SIBLING(child,sibling);
child = sibling;
# if SYNCTEX_VERBOSE
synctex_node_log(child);
# endif
SYNCTEX_UPDATE_FRIEND(child);
_synctex_hbox_setup_visible(parent,SYNCTEX_HORIZ(child),SYNCTEX_VERT(child));
goto sibling_loop;
} else {
_synctex_error("Can't create boundary record (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_END_SHEET) {
goto scan_teehs;
} else if (*SYNCTEX_CUR == SYNCTEX_CHAR_ANCHOR) {
++SYNCTEX_CUR;
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
_synctex_error("Missing anchor (2).");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto sibling_loop;
} else {
++SYNCTEX_CUR;
/* _synctex_error("Ignored record %c(2)\n",*SYNCTEX_CUR); */
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) {
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
goto sibling_loop;
}
} else {
available = 1;
status = _synctex_buffer_get_available_size(scanner,&available);
if (status<SYNCTEX_STATUS_OK && available>0){
goto sibling_loop;
} else {
_synctex_error("Uncomplete sheet(2)");
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR);
}
}
# undef SYNCTEX_DECODE_FAILED
}
# define SYNCTEX_APPEND_SHEET(SCANNER,SHEET) if (SCANNER->sheet) {\
synctex_node_t last_sheet = SCANNER->sheet;\
synctex_node_t next_sheet = NULL;\
while ((next_sheet = SYNCTEX_SIBLING(last_sheet))) {\
last_sheet = next_sheet;\
}\
SYNCTEX_SET_SIBLING(last_sheet,SHEET);\
} else {\
SCANNER->sheet = SHEET;\
}
/* 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 != SYNCTEX_CHAR_BEGIN_SHEET) {
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;
}
/* 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_APPEND_SHEET(scanner,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;
}
int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_modeRef);
/* Where the synctex scanner is created. */
synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse) {
gzFile file = NULL;
char * synctex = NULL;
synctex_scanner_t scanner = NULL;
synctex_io_mode_t io_mode = 0;
/* Here we assume that int are smaller than void * */
if (sizeof(int)>sizeof(void*)) {
_synctex_error("INTERNAL INCONSISTENCY: int's are unexpectedly bigger than pointers, bailing out.");
return NULL;
}
/* We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */
if (SYNCTEX_BUFFER_SIZE >= UINT_MAX) {
_synctex_error("Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (1)");
return NULL;
}
/* for integers: */
if (SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE) {
_synctex_error("Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (2)");
return NULL;
}
/* now open the synctex file */
if (_synctex_open(output,build_directory,&synctex,&file,synctex_ADD_QUOTES,&io_mode) || !file) {
if (_synctex_open(output,build_directory,&synctex,&file,synctex_DONT_ADD_QUOTES,&io_mode) || !file) {
return NULL;
}
}
scanner = (synctex_scanner_t)_synctex_malloc(sizeof(_synctex_scanner_t));
if (NULL == scanner) {
_synctex_error("malloc problem");
free(synctex);
gzclose(file);
return NULL;
}
/* make a private copy of output for the scanner */
if (NULL == (scanner->output = (char *)malloc(strlen(output)+1))){
_synctex_error("! synctex_scanner_new_with_output_file: Memory problem (2), scanner's output is not reliable.");
} else if (scanner->output != strcpy(scanner->output,output)) {
_synctex_error("! synctex_scanner_new_with_output_file: Copy problem, scanner's output is not reliable.");
}
scanner->synctex = synctex;/* Now the scanner owns synctex */
SYNCTEX_FILE = file;
return parse? synctex_scanner_parse(scanner):scanner;
}
/* This functions opens the file at the "output" given location.
* It manages the problem of quoted filenames that appear with pdftex and filenames containing the space character.
* In TeXLive 2008, the synctex file created with pdftex did contain unexpected quotes.
* This function will remove them if possible.
* All the reference arguments will take a value on return. They must be non NULL.
* 0 on success, non 0 on error. */
static int __synctex_open(const char * output, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) {
if (synctex_name_ref && file_ref && io_mode_ref) {
/* 1 local variables that uses dynamic memory */
char * synctex_name = NULL;
gzFile the_file = NULL;
char * quoteless_synctex_name = NULL;
size_t size = 0;
synctex_io_mode_t io_mode = *io_mode_ref;
const char * mode = _synctex_get_io_mode_name(io_mode);
/* now create the synctex file name */
size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1;
synctex_name = (char *)malloc(size);
if (NULL == synctex_name) {
_synctex_error("! __synctex_open: Memory problem (1)\n");
return 1;
}
/* we have reserved for synctex enough memory to copy output (including its 2 eventual quotes), both suffices,
* including the terminating character. size is free now. */
if (synctex_name != strcpy(synctex_name,output)) {
_synctex_error("! __synctex_open: Copy problem\n");
return_on_error:
free(synctex_name);
free(quoteless_synctex_name);
return 2;
}
/* remove the last path extension if any */
_synctex_strip_last_path_extension(synctex_name);
if (!strlen(synctex_name)) {
goto return_on_error;
}
/* now insert quotes. */
if (add_quotes) {
char * quoted = NULL;
if (_synctex_copy_with_quoting_last_path_component(synctex_name,&quoted,size) || (NULL == quoted)) {
/* There was an error or quoting does not make sense: */
goto return_on_error;
}
quoteless_synctex_name = synctex_name;
synctex_name = quoted;
}
/* Now add to synctex_name the first path extension. */
if (synctex_name != strcat(synctex_name,synctex_suffix)){
_synctex_error("! __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix);
goto return_on_error;
}
/* Add to quoteless_synctex_name as well, if relevant. */
if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix))){
free(quoteless_synctex_name);
quoteless_synctex_name = NULL;
}
if (NULL == (the_file = gzopen(synctex_name,mode))) {
/* Could not open this file */
if (errno != ENOENT) {
/* The file does exist, this is a lower level error, I can't do anything. */
_synctex_error("could not open %s, error %i\n",synctex_name,errno);
goto return_on_error;
}
/* Apparently, there is no uncompressed synctex file. Try the compressed version */
if (synctex_name != strcat(synctex_name,synctex_suffix_gz)){
_synctex_error("! __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz);
goto return_on_error;
}
io_mode |= synctex_io_gz_mask;
mode = _synctex_get_io_mode_name(io_mode); /* the file is a compressed and is a binary file, this caused errors on Windows */
/* Add the suffix to the quoteless_synctex_name as well. */
if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix_gz))){
free(quoteless_synctex_name);
quoteless_synctex_name = NULL;
}
if (NULL == (the_file = gzopen(synctex_name,mode))) {
/* Could not open this file */
if (errno != ENOENT) {
/* The file does exist, this is a lower level error, I can't do anything. */
_synctex_error("Could not open %s, error %i\n",synctex_name,errno);
}
goto return_on_error;
}
}
/* At this point, the file is properly open.
* If we are in the add_quotes mode, we change the file name by removing the quotes. */
if (quoteless_synctex_name) {
gzclose(the_file);
if (rename(synctex_name,quoteless_synctex_name)) {
_synctex_error("Could not rename %s to %s, error %i\n",synctex_name,quoteless_synctex_name,errno);
/* We could not rename, reopen the file with the quoted name. */
if (NULL == (the_file = gzopen(synctex_name,mode))) {
/* No luck, could not re open this file, something has happened meanwhile */
if (errno != ENOENT) {
/* The file does not exist any more, it has certainly be removed somehow
* this is a lower level error, I can't do anything. */
_synctex_error("Could not open again %s, error %i\n",synctex_name,errno);
}
goto return_on_error;
}
} else {
/* The file has been successfully renamed */
if (NULL == (the_file = gzopen(quoteless_synctex_name,mode))) {
/* Could not open this file */
if (errno != ENOENT) {
/* The file does exist, this is a lower level error, I can't do anything. */
_synctex_error("Could not open renamed %s, error %i\n",quoteless_synctex_name,errno);
}
goto return_on_error;
}
/* The quote free file name should replace the old one:*/
free(synctex_name);
synctex_name = quoteless_synctex_name;
quoteless_synctex_name = NULL;
}
}
/* The operation is successfull, return the arguments by value. */
* file_ref = the_file;
* io_mode_ref = io_mode;
* synctex_name_ref = synctex_name;
return 0;
}
return 3; /* Bad parameter. */
}
/* Opens the ouput file, taking into account the eventual build_directory.
* 0 on success, non 0 on error. */
int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) {
# define synctex_name (*synctex_name_ref)
# define the_file (*file_ref)
int result = __synctex_open(output,synctex_name_ref,file_ref,add_quotes,io_mode_ref);
if ((result || !*file_ref) && build_directory && strlen(build_directory)) {
char * build_output;
const char *lpc;
size_t size;
synctex_bool_t is_absolute;
build_output = NULL;
lpc = _synctex_last_path_component(output);
size = strlen(build_directory)+strlen(lpc)+2; /* One for the '/' and one for the '\0'. */
is_absolute = _synctex_path_is_absolute(build_directory);
if (!is_absolute) {
size += strlen(output);
}
if ((build_output = (char *)malloc(size))) {
if (is_absolute) {
build_output[0] = '\0';
} else {
if (build_output != strcpy(build_output,output)) {
free(build_output);
return -4;
}
build_output[lpc-output]='\0';
}
if (build_output == strcat(build_output,build_directory)) {
/* Append a path separator if necessary. */
if (!SYNCTEX_IS_PATH_SEPARATOR(build_output[strlen(build_directory)-1])) {
if (build_output != strcat(build_output,"/")) {
free(build_output);
return -2;
}
}
/* Append the last path component of the output. */
if (build_output != strcat(build_output,lpc)) {
free(build_output);
return -3;
}
result = __synctex_open(build_output,synctex_name_ref,file_ref,add_quotes,io_mode_ref);
free(build_output);
return result;
}
free(build_output);
}
return -1;
}
return result;
# undef synctex_name
# undef the_file
}
/* The scanner destructor
*/
void synctex_scanner_free(synctex_scanner_t scanner) {
if (NULL == scanner) {
return;
}
if (SYNCTEX_FILE) {
gzclose(SYNCTEX_FILE);
SYNCTEX_FILE = NULL;
}
SYNCTEX_FREE(scanner->sheet);
SYNCTEX_FREE(scanner->input);
free(SYNCTEX_START);
free(scanner->output_fmt);
free(scanner->output);
free(scanner->synctex);
free(scanner->lists_of_friends);
free(scanner);
}
/* Where the synctex scanner parses the contents of the file. */
synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner) {
synctex_status_t status = 0;
if (!scanner || scanner->flags.has_parsed) {
return scanner;
}
scanner->flags.has_parsed=1;
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.027e23f;
# define DEFINE_synctex_scanner_class(NAME)\
scanner->class[synctex_node_type_##NAME] = synctex_class_##NAME;\
(scanner->class[synctex_node_type_##NAME]).scanner = scanner
DEFINE_synctex_scanner_class(sheet);
DEFINE_synctex_scanner_class(input);
DEFINE_synctex_scanner_class(hbox);
DEFINE_synctex_scanner_class(void_hbox);
DEFINE_synctex_scanner_class(vbox);
DEFINE_synctex_scanner_class(void_vbox);
DEFINE_synctex_scanner_class(kern);
DEFINE_synctex_scanner_class(glue);
DEFINE_synctex_scanner_class(math);
DEFINE_synctex_scanner_class(boundary);
SYNCTEX_START = (char *)malloc(SYNCTEX_BUFFER_SIZE+1); /* one more character for null termination */
if (NULL == SYNCTEX_START) {
_synctex_error("malloc error");
synctex_scanner_free(scanner);
return NULL;
}
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;
# if defined(SYNCTEX_USE_CHARINDEX)
scanner->charindex_offset = -SYNCTEX_BUFFER_SIZE;
# endif
status = _synctex_scan_preamble(scanner);
if (status<SYNCTEX_STATUS_OK) {
_synctex_error("SyncTeX Error: Bad preamble\n");
bailey:
synctex_scanner_free(scanner);
return NULL;
}
status = _synctex_scan_content(scanner);
if (status<SYNCTEX_STATUS_OK) {
_synctex_error("SyncTeX Error: Bad content\n");
goto bailey;
}
/* Everything is finished, free the buffer, close the 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.76f;
scanner->y_offset /= 65781.76f;
}
return scanner;
#undef SYNCTEX_FILE
}
/* 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);
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 (_synctex_is_equivalent_file_name(name,(SYNCTEX_NAME(input)))) {
return SYNCTEX_TAG(input);
}
} while((input = SYNCTEX_SIBLING(input)) != NULL);
// 2011 version
name = _synctex_base_name(name);
input = scanner->input;
do {
if (_synctex_is_equivalent_file_name(name,_synctex_base_name(SYNCTEX_NAME(input)))) {
synctex_node_t other_input = input;
while((other_input = SYNCTEX_SIBLING(other_input)) != NULL) {
if (_synctex_is_equivalent_file_name(name,_synctex_base_name(SYNCTEX_NAME(other_input)))
&& (strlen(SYNCTEX_NAME(input))!=strlen(SYNCTEX_NAME(other_input))
|| strncmp(SYNCTEX_NAME(other_input),SYNCTEX_NAME(input),strlen(SYNCTEX_NAME(input))))) {
// There is a second possible candidate
return 0;
}
}
return SYNCTEX_TAG(input);
}
} while((input = SYNCTEX_SIBLING(input)) != NULL);
return 0;
}
int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) {
size_t char_index = strlen(name);
if ((scanner = synctex_scanner_parse(scanner)) && (0 < char_index)) {
/* the name is not void */
char_index -= 1;
if (!SYNCTEX_IS_PATH_SEPARATOR(name[char_index])) {
/* the last character of name is not a path separator */
int result = _synctex_scanner_get_tag(scanner,name);
if (result) {
return result;
} else {
/* the given name was not the one known by TeX
* try a name relative to the enclosing directory of the scanner->output file */
const char * relative = name;
const char * ptr = scanner->output;
while((strlen(relative) > 0) && (strlen(ptr) > 0) && (*relative == *ptr))
{
relative += 1;
ptr += 1;
}
/* Find the last path separator before relative */
while(relative > name) {
if (SYNCTEX_IS_PATH_SEPARATOR(*(relative-1))) {
break;
}
relative -= 1;
}
if ((relative > name) && (result = _synctex_scanner_get_tag(scanner,relative))) {
return result;
}
if (SYNCTEX_IS_PATH_SEPARATOR(name[0])) {
/* No tag found for the given absolute name,
* Try each relative path starting from the shortest one */
while(0<char_index) {
char_index -= 1;
if (SYNCTEX_IS_PATH_SEPARATOR(name[char_index])
&& (result = _synctex_scanner_get_tag(scanner,name+char_index+1))) {
return result;
}
}
}
}
return result;
}
}
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:"";
}
const char * synctex_scanner_get_synctex(synctex_scanner_t scanner) {
return NULL != scanner && scanner->synctex?scanner->synctex:"";
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Public node attributes
# endif
int synctex_node_h(synctex_node_t node){
if (!node) {
return 0;
}
return SYNCTEX_HORIZ(node);
}
int synctex_node_v(synctex_node_t node){
if (!node) {
return 0;
}
return SYNCTEX_VERT(node);
}
int synctex_node_width(synctex_node_t node){
if (!node) {
return 0;
}
return SYNCTEX_WIDTH(node);
}
int synctex_node_box_h(synctex_node_t node){
if (!node) {
return 0;
}
if (SYNCTEX_IS_BOX(node)) {
result:
return SYNCTEX_HORIZ(node);
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
int synctex_node_box_v(synctex_node_t node){
if (!node) {
return 0;
}
if (SYNCTEX_IS_BOX(node)) {
result:
return SYNCTEX_VERT(node);
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
int synctex_node_box_width(synctex_node_t node){
if (!node) {
return 0;
}
if (SYNCTEX_IS_BOX(node)) {
result:
return SYNCTEX_WIDTH(node);
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
int synctex_node_box_height(synctex_node_t node){
if (!node) {
return 0;
}
if (SYNCTEX_IS_BOX(node)) {
result:
return SYNCTEX_HEIGHT(node);
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
int synctex_node_box_depth(synctex_node_t node){
if (!node) {
return 0;
}
if (SYNCTEX_IS_BOX(node)) {
result:
return SYNCTEX_DEPTH(node);
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Public node visible attributes
# endif
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;
}
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset;
case synctex_node_type_hbox:
result:
return SYNCTEX_HORIZ_V(node)*node->class->scanner->unit+node->class->scanner->x_offset;
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
float synctex_node_box_visible_v(synctex_node_t node){
if (!node) {
return 0;
}
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset;
case synctex_node_type_hbox:
result:
return SYNCTEX_VERT_V(node)*node->class->scanner->unit+node->class->scanner->y_offset;
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
float synctex_node_box_visible_width(synctex_node_t node){
if (!node) {
return 0;
}
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
return SYNCTEX_WIDTH(node)*node->class->scanner->unit;
case synctex_node_type_hbox:
result:
return SYNCTEX_WIDTH_V(node)*node->class->scanner->unit;
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
float synctex_node_box_visible_height(synctex_node_t node){
if (!node) {
return 0;
}
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
return SYNCTEX_HEIGHT(node)*node->class->scanner->unit;
case synctex_node_type_hbox:
result:
return SYNCTEX_HEIGHT_V(node)*node->class->scanner->unit;
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
float synctex_node_box_visible_depth(synctex_node_t node){
if (!node) {
return 0;
}
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
return SYNCTEX_DEPTH(node)*node->class->scanner->unit;
case synctex_node_type_hbox:
result:
return SYNCTEX_DEPTH_V(node)*node->class->scanner->unit;
}
if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) {
goto result;
}
return 0;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Other public node attributes
# endif
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;
}
synctex_charindex_t synctex_node_charindex(synctex_node_t node) {
return node?SYNCTEX_CHARINDEX(node):0;
}
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_mean_line(synctex_node_t node) {
return node?(node->class->type==synctex_node_type_hbox?SYNCTEX_MEAN_LINE(node):SYNCTEX_LINE(node)):-1;
}
int synctex_node_child_count(synctex_node_t node) {
return node?(node->class->type==synctex_node_type_hbox?SYNCTEX_NODE_WEIGHT(node):0):-1;
}
int synctex_node_column(synctex_node_t node) {
# ifdef __DARWIN_UNIX03
# pragma unused(node)
# endif
return -1;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Sheet
# endif
synctex_node_t synctex_sheet(synctex_scanner_t scanner,int page) {
if (scanner) {
synctex_node_t sheet = scanner->sheet;
while(sheet) {
if (page == SYNCTEX_PAGE(sheet)) {
return sheet;
}
sheet = SYNCTEX_SIBLING(sheet);
}
}
return NULL;
}
synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page) {
if (scanner) {
return SYNCTEX_CHILD(synctex_sheet(scanner,page));
}
return NULL;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Query
# endif
synctex_status_t synctex_display_query(synctex_scanner_t scanner,const char * name,int line,int column) {
# ifdef __DARWIN_UNIX03
# pragma unused(column)
# endif
int tag = synctex_scanner_get_tag(scanner,name);
size_t size = 0;
int friend_index = 0;
int max_line = 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;
max_line = line < INT_MAX-scanner->number_of_lists ? line+scanner->number_of_lists:INT_MAX;
while(line<max_line) {
/* This loop will only be performed once for advanced viewers */
friend_index = (tag+line)%(scanner->number_of_lists);
if ((node = (scanner->lists_of_friends)[friend_index])) {
do {
if ((synctex_node_type(node)>=synctex_node_type_boundary)
&& (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);
}
} while ((node = SYNCTEX_FRIEND(node)));
if (SYNCTEX_START == NULL) {
/* We did not find any matching boundary, retry with glue or kern */
node = (scanner->lists_of_friends)[friend_index];/* no need to test it again, already done */
do {
if ((synctex_node_type(node)>=synctex_node_type_kern)
&& (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);
}
} while ((node = SYNCTEX_FRIEND(node)));
if (SYNCTEX_START == NULL) {
/* We did not find any matching glue or kern, retry with boxes */
node = (scanner->lists_of_friends)[friend_index];/* no need to test it again, already done */
do {
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);
}
} while((node = SYNCTEX_FRIEND(node)));
}
}
SYNCTEX_END = SYNCTEX_CUR;
/* Now reverse the order to have nodes in display order, and then keep just a few nodes.
* Order first the best node. */
if ((SYNCTEX_START) && (SYNCTEX_END)) {
unsigned int best_match = -1;
unsigned int next_match = -1;
unsigned int best_weight = 0;
synctex_node_t * best_ref = NULL;
synctex_node_t * start_ref = (synctex_node_t *)SYNCTEX_START;
synctex_node_t * end_ref = (synctex_node_t *)SYNCTEX_END;
--end_ref;
while (start_ref < end_ref) {
node = *start_ref;
*start_ref = *end_ref;
*end_ref = node;
++start_ref;
--end_ref;
}
/* Now reorder the nodes to put first the one which fits best.
* The idea is to walk along the list of nodes and pick up the first one
* which line info is exactly the mean line of its parent, or at least very close.
* Then we choose among all such node the one with the maximum number of child nodes.
* Then we switch with the first node.
*/
best_ref = start_ref = (synctex_node_t *)SYNCTEX_START;
node = *start_ref;
best_match = abs(SYNCTEX_LINE(node)-SYNCTEX_MEAN_LINE(SYNCTEX_PARENT(node)));
end_ref = (synctex_node_t *)SYNCTEX_END;
while (++start_ref<end_ref) {
synctex_node_t parent = NULL;
node = *start_ref;
parent = SYNCTEX_PARENT(node);
next_match = abs(SYNCTEX_LINE(node)-SYNCTEX_MEAN_LINE(parent));
if (next_match < best_match
|| (next_match == best_match && SYNCTEX_NODE_WEIGHT(parent)>best_weight)) {
best_match = next_match;
best_ref = start_ref;
best_weight = SYNCTEX_NODE_WEIGHT(parent);
}
}
node = *best_ref;
*best_ref = *(synctex_node_t *)SYNCTEX_START;
*(synctex_node_t *)SYNCTEX_START = node;
/* Basically, we keep the first node for each parent.
* More precisely, we keep only nodes that are not children of
* their predecessor's parent. */
start_ref = (synctex_node_t *)SYNCTEX_START;
end_ref = (synctex_node_t *)SYNCTEX_START;
next_end:
end_ref += 1; /* we allways have start_ref<= end_ref*/
if (end_ref < (synctex_node_t *)SYNCTEX_END) {
node = *end_ref;
while ((node = SYNCTEX_PARENT(node))) {
if (SYNCTEX_PARENT(*start_ref) == node) {
goto next_end;
}
}
start_ref += 1;
*start_ref = *end_ref;
goto next_end;
}
start_ref += 1;
SYNCTEX_END = (char *)start_ref;
SYNCTEX_CUR = NULL;// added on behalf of Jose Alliste
return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);// added on behalf Jan Sundermeyer
}
SYNCTEX_CUR = NULL;
// return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); removed on behalf Jan Sundermeyer
}
# if defined(__SYNCTEX_STRONG_DISPLAY_QUERY__)
break;
# else
++line;
# endif
}
return 0;
}
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;
}
}
/* This struct records a point in TeX coordinates.*/
typedef struct {
int h;
int v;
} synctex_point_t;
/* This struct records distances, the left one is positive or 0 and the right one is negative or 0.
* When comparing the locations of 2 different graphical objects on the page, we will have to also record the
* horizontal distance as signed to keep track of the typesetting order.*/
typedef struct {
int left;
int right;
} synctex_distances_t;
typedef struct {
synctex_point_t left;
synctex_point_t right;
} synctex_offsets_t;
typedef struct {
synctex_node_t left;
synctex_node_t right;
} synctex_node_set_t;
/* The smallest container between two has the smallest width or height.
* This comparison is used when there are 2 overlapping boxes that contain the hit point.
* For ConTeXt, the problem appears at each page.
* The chosen box is the one with the smallest height, then the smallest width. */
SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node);
/* Returns the distance between the hit point hitPoint=(H,V) and the given node. */
synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
/* The best container is the deeper box that contains the hit point (H,V).
* _synctex_eq_deepest_container starts with node whereas
* _synctex_box_child_deepest starts with node's children, if any
* if node is not a box, or a void box, NULL is returned.
* We traverse the node tree in a deep first manner and stop as soon as a result is found. */
static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible);
/* Once a best container is found, the closest children are the closest nodes to the left or right of the hit point.
* Only horizontal and vertical offsets are used to compare the positions of the nodes. */
SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef, synctex_bool_t visible);
/* The closest container is the box that is the one closest to the given point.
* The "visible" version takes into account the visible dimensions instead of the real ones given by TeX. */
SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible);
#define SYNCTEX_MASK_LEFT 1
#define SYNCTEX_MASK_RIGHT 2
synctex_status_t synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v) {
synctex_node_t sheet = NULL;
synctex_node_t node = NULL; /* placeholder */
synctex_node_t other_node = NULL; /* placeholder */
synctex_point_t hitPoint = {0,0}; /* placeholder */
synctex_node_set_t bestNodes = {NULL,NULL}; /* holds the best node */
synctex_distances_t bestDistances = {INT_MAX,INT_MAX}; /* holds the best distances for the best node */
synctex_node_t bestContainer = NULL; /* placeholder */
if (NULL == (scanner = synctex_scanner_parse(scanner)) || 0 >= scanner->unit) {/* scanner->unit must be >0 */
return 0;
}
/* Convert the given point to scanner integer coordinates */
hitPoint.h = (h-scanner->x_offset)/scanner->unit;
hitPoint.v = (v-scanner->y_offset)/scanner->unit;
/* We will store in the scanner's buffer the result of the query. */
free(SYNCTEX_START);
SYNCTEX_START = SYNCTEX_END = SYNCTEX_CUR = NULL;
/* Find the proper sheet */
sheet = scanner->sheet;
while((sheet) && SYNCTEX_PAGE(sheet) != page) {
sheet = SYNCTEX_SIBLING(sheet);
}
if (NULL == sheet) {
return -1;
}
/* Now sheet points to the sheet node with proper page number */
/* Here is how we work:
* At first we do not consider the visible box dimensions. This will cover the most frequent cases.
* Then we try with the visible box dimensions.
* We try to find a non void box containing the hit point.
* We browse all the horizontal boxes until we find one containing the hit point. */
if ((node = SYNCTEX_NEXT_hbox(sheet))) {
do {
if (_synctex_point_in_box(hitPoint,node,synctex_YES)) {
/* Maybe the hitPoint belongs to a contained vertical box. */
end:
/* This trick is for catching overlapping boxes */
if ((other_node = SYNCTEX_NEXT_hbox(node))) {
do {
if (_synctex_point_in_box(hitPoint,other_node,synctex_YES)) {
node = _synctex_smallest_container(other_node,node);
}
} while((other_node = SYNCTEX_NEXT_hbox(other_node)));
}
/* node is the smallest horizontal box that contains hitPoint. */
if ((bestContainer = _synctex_eq_deepest_container(hitPoint,node,synctex_YES))) {
node = bestContainer;
}
_synctex_eq_get_closest_children_in_box(hitPoint,node,&bestNodes,&bestDistances,synctex_YES);
if (bestNodes.right && bestNodes.left) {
if ((SYNCTEX_TAG(bestNodes.right)!=SYNCTEX_TAG(bestNodes.left))
|| (SYNCTEX_LINE(bestNodes.right)!=SYNCTEX_LINE(bestNodes.left))
|| (SYNCTEX_COLUMN(bestNodes.right)!=SYNCTEX_COLUMN(bestNodes.left))) {
if ((SYNCTEX_START = malloc(2*sizeof(synctex_node_t)))) {
if (bestDistances.left>bestDistances.right) {
((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.right;
((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.left;
} else {
((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.left;
((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.right;
}
SYNCTEX_END = SYNCTEX_START + 2*sizeof(synctex_node_t);
SYNCTEX_CUR = NULL;
return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);
}
return SYNCTEX_STATUS_ERROR;
}
/* both nodes have the same input coordinates
* We choose the one closest to the hit point */
if (bestDistances.left>bestDistances.right) {
bestNodes.left = bestNodes.right;
}
bestNodes.right = NULL;
} else if (bestNodes.right) {
bestNodes.left = bestNodes.right;
} else if (!bestNodes.left){
bestNodes.left = node;
}
if ((SYNCTEX_START = malloc(sizeof(synctex_node_t)))) {
* (synctex_node_t *)SYNCTEX_START = bestNodes.left;
SYNCTEX_END = SYNCTEX_START + sizeof(synctex_node_t);
SYNCTEX_CUR = NULL;
return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);
}
return SYNCTEX_STATUS_ERROR;
}
} while ((node = SYNCTEX_NEXT_hbox(node)));
/* All the horizontal boxes have been tested,
* None of them contains the hit point.
*/
}
/* We are not lucky */
if ((node = SYNCTEX_CHILD(sheet))) {
goto end;
}
return 0;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Utilities
# endif
/* Rougly speaking, this is:
* node's h coordinate - hitPoint's h coordinate.
* If node is to the right of the hit point, then this distance is positive,
* if node is to the left of the hit point, this distance is negative.*/
int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible);
int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
if (node) {
int min,med,max;
switch(node->class->type) {
/* The distance between a point and a box is special.
* It is not the euclidian distance, nor something similar.
* We have to take into account the particular layout,
* and the box hierarchy.
* Given a box, there are 9 regions delimited by the lines of the edges of the box.
* The origin being at the top left corner of the page,
* we also give names to the vertices of the box.
*
* 1 | 2 | 3
* ---A---B--->
* 4 | 5 | 6
* ---C---D--->
* 7 | 8 | 9
* v v
*/
case synctex_node_type_hbox:
/* getting the box bounds, taking into account negative width, height and depth. */
min = visible?SYNCTEX_HORIZ_V(node):SYNCTEX_HORIZ(node);
max = min + (visible?SYNCTEX_ABS_WIDTH_V(node):SYNCTEX_ABS_WIDTH(node));
/* We allways have min <= max */
if (hitPoint.h<min) {
return min - hitPoint.h; /* regions 1+4+7, result is > 0 */
} else if (hitPoint.h>max) {
return max - hitPoint.h; /* regions 3+6+9, result is < 0 */
} else {
return 0; /* regions 2+5+8, inside the box, except for vertical coordinates */
}
break;
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
/* getting the box bounds, taking into account negative width, height and depth.
* For these boxes, no visible dimension available */
min = SYNCTEX_HORIZ(node);
max = min + SYNCTEX_ABS_WIDTH(node);
/* We allways have min <= max */
if (hitPoint.h<min) {
return min - hitPoint.h; /* regions 1+4+7, result is > 0 */
} else if (hitPoint.h>max) {
return max - hitPoint.h; /* regions 3+6+9, result is < 0 */
} else {
return 0; /* regions 2+5+8, inside the box, except for vertical coordinates */
}
break;
case synctex_node_type_kern:
/* IMPORTANT NOTICE: the location of the kern is recorded AFTER the move.
* The distance to the kern is very special,
* in general, there is no text material in the kern,
* this is why we compute the offset relative to the closest edge of the kern.*/
max = SYNCTEX_WIDTH(node);
if (max<0) {
min = SYNCTEX_HORIZ(node);
max = min - max;
} else {
min = -max;
max = SYNCTEX_HORIZ(node);
min += max;
}
med = (min+max)/2;
/* positive kern: '.' means text, '>' means kern offset
* .............
* min>>>>med>>>>max
* ...............
* negative kern: '.' means text, '<' means kern offset
* ............................
* min<<<<med<<<<max
* .................................
* Actually, we do not take into account negative widths.
* There is a problem for such situation when there is efectively overlapping text.
* But this should be extremely rare. I guess that in that case, many different choices
* could be made, one being in contradiction of the other.
* It means that the best choice should be made according to the situation that occurs
* most frequently.
*/
if (hitPoint.h<min) {
return min - hitPoint.h + 1; /* penalty to ensure other nodes are chosen first in case of overlapping ones */
} else if (hitPoint.h>max) {
return max - hitPoint.h - 1; /* same kind of penalty */
} else if (hitPoint.h>med) {
/* do things like if the node had 0 width and was placed at the max edge + 1*/
return max - hitPoint.h + 1; /* positive, the kern is to the right of the hitPoint */
} else {
return min - hitPoint.h - 1; /* negative, the kern is to the left of the hitPoint */
}
case synctex_node_type_glue:
case synctex_node_type_math:
return SYNCTEX_HORIZ(node) - hitPoint.h;
}
}
return INT_MAX;/* We always assume that the node is faraway to the right*/
}
/* Rougly speaking, this is:
* node's v coordinate - hitPoint's v coordinate.
* If node is at the top of the hit point, then this distance is positive,
* if node is at the bottom of the hit point, this distance is negative.*/
int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible);
int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible) {
# ifdef __DARWIN_UNIX03
# pragma unused(visible)
# endif
if (node) {
int min,max;
switch(node->class->type) {
/* The distance between a point and a box is special.
* It is not the euclidian distance, nor something similar.
* We have to take into account the particular layout,
* and the box hierarchy.
* Given a box, there are 9 regions delimited by the lines of the edges of the box.
* The origin being at the top left corner of the page,
* we also give names to the vertices of the box.
*
* 1 | 2 | 3
* ---A---B--->
* 4 | 5 | 6
* ---C---D--->
* 7 | 8 | 9
* v v
*/
case synctex_node_type_hbox:
/* getting the box bounds, taking into account negative width, height and depth. */
min = SYNCTEX_VERT_V(node);
max = min + SYNCTEX_ABS_DEPTH_V(node);
min -= SYNCTEX_ABS_HEIGHT_V(node);
/* We allways have min <= max */
if (hitPoint.v<min) {
return min - hitPoint.v; /* regions 1+2+3, result is > 0 */
} else if (hitPoint.v>max) {
return max - hitPoint.v; /* regions 7+8+9, result is < 0 */
} else {
return 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */
}
break;
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_void_hbox:
/* getting the box bounds, taking into account negative width, height and depth. */
min = SYNCTEX_VERT(node);
max = min + SYNCTEX_ABS_DEPTH(node);
min -= SYNCTEX_ABS_HEIGHT(node);
/* We allways have min <= max */
if (hitPoint.v<min) {
return min - hitPoint.v; /* regions 1+2+3, result is > 0 */
} else if (hitPoint.v>max) {
return max - hitPoint.v; /* regions 7+8+9, result is < 0 */
} else {
return 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */
}
break;
case synctex_node_type_kern:
case synctex_node_type_glue:
case synctex_node_type_math:
return SYNCTEX_VERT(node) - hitPoint.v;
}
}
return INT_MAX;/* We always assume that the node is faraway to the top*/
}
SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node) {
float height, other_height;
if (SYNCTEX_ABS_WIDTH(node)<SYNCTEX_ABS_WIDTH(other_node)) {
return node;
}
if (SYNCTEX_ABS_WIDTH(node)>SYNCTEX_ABS_WIDTH(other_node)) {
return other_node;
}
height = SYNCTEX_ABS_DEPTH(node) + SYNCTEX_ABS_HEIGHT(node);
other_height = SYNCTEX_ABS_DEPTH(other_node) + SYNCTEX_ABS_HEIGHT(other_node);
if (height<other_height) {
return node;
}
if (height>other_height) {
return other_node;
}
return node;
}
synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
if (node) {
if (0 == _synctex_point_h_distance(hitPoint,node,visible)
&& 0 == _synctex_point_v_distance(hitPoint,node,visible)) {
return synctex_YES;
}
}
return synctex_NO;
}
int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) {
# ifdef __DARWIN_UNIX03
# pragma unused(visible)
# endif
int result = INT_MAX; /* when the distance is meaning less (sheet, input...) */
if (node) {
int minH,maxH,minV,maxV;
switch(node->class->type) {
/* The distance between a point and a box is special.
* It is not the euclidian distance, nor something similar.
* We have to take into account the particular layout,
* and the box hierarchy.
* Given a box, there are 9 regions delimited by the lines of the edges of the box.
* The origin being at the top left corner of the page,
* we also give names to the vertices of the box.
*
* 1 | 2 | 3
* ---A---B--->
* 4 | 5 | 6
* ---C---D--->
* 7 | 8 | 9
* v v
* In each region, there is a different formula.
* In the end we have a continuous distance which may not be a mathematical distance but who cares. */
case synctex_node_type_vbox:
case synctex_node_type_void_vbox:
case synctex_node_type_hbox:
case synctex_node_type_void_hbox:
/* getting the box bounds, taking into account negative widths. */
minH = SYNCTEX_HORIZ(node);
maxH = minH + SYNCTEX_ABS_WIDTH(node);
minV = SYNCTEX_VERT(node);
maxV = minV + SYNCTEX_ABS_DEPTH(node);
minV -= SYNCTEX_ABS_HEIGHT(node);
/* In what region is the point hitPoint=(H,V) ? */
if (hitPoint.v<minV) {
if (hitPoint.h<minH) {
/* This is region 1. The distance to the box is the L1 distance PA. */
result = minV - hitPoint.v + minH - hitPoint.h;/* Integer overflow? probability epsilon */
} else if (hitPoint.h<=maxH) {
/* This is region 2. The distance to the box is the geometrical distance to the top edge. */
result = minV - hitPoint.v;
} else {
/* This is region 3. The distance to the box is the L1 distance PB. */
result = minV - hitPoint.v + hitPoint.h - maxH;
}
} else if (hitPoint.v<=maxV) {
if (hitPoint.h<minH) {
/* This is region 4. The distance to the box is the geometrical distance to the left edge. */
result = minH - hitPoint.h;
} else if (hitPoint.h<=maxH) {
/* This is region 4. We are inside the box. */
result = 0;
} else {
/* This is region 6. The distance to the box is the geometrical distance to the right edge. */
result = hitPoint.h - maxH;
}
} else {
if (hitPoint.h<minH) {
/* This is region 7. The distance to the box is the L1 distance PC. */
result = hitPoint.v - maxV + minH - hitPoint.h;
} else if (hitPoint.h<=maxH) {
/* This is region 8. The distance to the box is the geometrical distance to the top edge. */
result = hitPoint.v - maxV;
} else {
/* This is region 9. The distance to the box is the L1 distance PD. */
result = hitPoint.v - maxV + hitPoint.h - maxH;
}
}
break;
case synctex_node_type_kern:
maxH = SYNCTEX_WIDTH(node);
if (maxH<0) {
minH = SYNCTEX_HORIZ(node);
maxH = minH - maxH;
} else {
minH = -maxH;
maxH = SYNCTEX_HORIZ(node);
minH += maxH;
}
minV = SYNCTEX_VERT(node);
if (hitPoint.h<minH) {
if (hitPoint.v>minV) {
result = hitPoint.v - minV + minH - hitPoint.h;
} else {
result = minV - hitPoint.v + minH - hitPoint.h;
}
} else if (hitPoint.h>maxH) {
if (hitPoint.v>minV) {
result = hitPoint.v - minV + hitPoint.h - maxH;
} else {
result = minV - hitPoint.v + hitPoint.h - maxH;
}
} else if (hitPoint.v>minV) {
result = hitPoint.v - minV;
} else {
result = minV - hitPoint.v;
}
break;
case synctex_node_type_glue:
case synctex_node_type_math:
minH = SYNCTEX_HORIZ(node);
minV = SYNCTEX_VERT(node);
if (hitPoint.h<minH) {
if (hitPoint.v>minV) {
result = hitPoint.v - minV + minH - hitPoint.h;
} else {
result = minV - hitPoint.v + minH - hitPoint.h;
}
} else if (hitPoint.v>minV) {
result = hitPoint.v - minV + hitPoint.h - minH;
} else {
result = minV - hitPoint.v + hitPoint.h - minH;
}
break;
}
}
return result;
}
static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) {
if (node) {
synctex_node_t result = NULL;
synctex_node_t child = NULL;
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_hbox:
/* test the deep nodes first */
if ((child = SYNCTEX_CHILD(node))) {
do {
if ((result = _synctex_eq_deepest_container(hitPoint,child,visible))) {
return result;
}
} while((child = SYNCTEX_SIBLING(child)));
}
/* is the hit point inside the box? */
if (_synctex_point_in_box(hitPoint,node,visible)) {
/* for vboxes we try to use some node inside.
* Walk through the list of siblings until we find the closest one.
* Only consider siblings with children. */
if ((node->class->type == synctex_node_type_vbox) && (child = SYNCTEX_CHILD(node))) {
int bestDistance = INT_MAX;
do {
if (SYNCTEX_CHILD(child)) {
int distance = _synctex_node_distance_to_point(hitPoint,child,visible);
if (distance < bestDistance) {
bestDistance = distance;
node = child;
}
}
} while((child = SYNCTEX_SIBLING(child)));
}
return node;
}
}
}
return NULL;
}
/* Compares the locations of the hitPoint with the locations of the various nodes contained in the box.
* As it is an horizontal box, we only compare horizontal coordinates. */
SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef, synctex_bool_t visible);
SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef, synctex_bool_t visible) {
int result = 0;
if ((node = SYNCTEX_CHILD(node))) {
do {
int off7 = _synctex_point_h_distance(hitPoint,node,visible);
if (off7 > 0) {
/* node is to the right of the hit point.
* We compare node and the previously recorded one, through the recorded distance.
* If the nodes have the same tag, prefer the one with the smallest line number,
* if the nodes also have the same line number, prefer the one with the smallest column. */
if (bestDistancesRef->right > off7) {
bestDistancesRef->right = off7;
bestNodesRef->right = node;
result |= SYNCTEX_MASK_RIGHT;
} else if (bestDistancesRef->right == off7 && bestNodesRef->right) {
if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node)
&& (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node)
|| (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node)
&& SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) {
bestNodesRef->right = node;
result |= SYNCTEX_MASK_RIGHT;
}
}
} else if (off7 == 0) {
/* hitPoint is inside node. */
bestDistancesRef->left = bestDistancesRef->right = 0;
bestNodesRef->left = node;
bestNodesRef->right = NULL;
result |= SYNCTEX_MASK_LEFT;
} else { /* here off7 < 0, hitPoint is to the right of node */
off7 = -off7;
if (bestDistancesRef->left > off7) {
bestDistancesRef->left = off7;
bestNodesRef->left = node;
result |= SYNCTEX_MASK_LEFT;
} else if (bestDistancesRef->left == off7 && bestNodesRef->left) {
if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node)
&& (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node)
|| (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node)
&& SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) {
bestNodesRef->left = node;
result |= SYNCTEX_MASK_LEFT;
}
}
}
} while((node = SYNCTEX_SIBLING(node)));
if (result & SYNCTEX_MASK_LEFT) {
/* the left node is new, try to narrow the result */
if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) {
bestNodesRef->left = node;
}
if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) {
bestNodesRef->left = node;
}
}
if (result & SYNCTEX_MASK_RIGHT) {
/* the right node is new, try to narrow the result */
if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) {
bestNodesRef->right = node;
}
if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) {
bestNodesRef->right = node;
}
}
}
return result;
}
SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef,synctex_bool_t visible);
SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef,synctex_bool_t visible) {
int result = 0;
if ((node = SYNCTEX_CHILD(node))) {
do {
int off7 = _synctex_point_v_distance(hitPoint,node,visible);/* this is what makes the difference with the h version above */
if (off7 > 0) {
/* node is to the top of the hit point (below because TeX is oriented from top to bottom.
* We compare node and the previously recorded one, through the recorded distance.
* If the nodes have the same tag, prefer the one with the smallest line number,
* if the nodes also have the same line number, prefer the one with the smallest column. */
if (bestDistancesRef->right > off7) {
bestDistancesRef->right = off7;
bestNodesRef->right = node;
result |= SYNCTEX_MASK_RIGHT;
} else if (bestDistancesRef->right == off7 && bestNodesRef->right) {
if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node)
&& (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node)
|| (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node)
&& SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) {
bestNodesRef->right = node;
result |= SYNCTEX_MASK_RIGHT;
}
}
} else if (off7 == 0) {
bestDistancesRef->left = bestDistancesRef->right = 0;
bestNodesRef->left = node;
bestNodesRef->right = NULL;
result |= SYNCTEX_MASK_LEFT;
} else { /* here off7 < 0 */
off7 = -off7;
if (bestDistancesRef->left > off7) {
bestDistancesRef->left = off7;
bestNodesRef->left = node;
result |= SYNCTEX_MASK_LEFT;
} else if (bestDistancesRef->left == off7 && bestNodesRef->left) {
if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node)
&& (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node)
|| (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node)
&& SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) {
bestNodesRef->left = node;
result |= SYNCTEX_MASK_LEFT;
}
}
}
} while((node = SYNCTEX_SIBLING(node)));
if (result & SYNCTEX_MASK_LEFT) {
/* the left node is new, try to narrow the result */
if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) {
bestNodesRef->left = node;
}
if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) {
bestNodesRef->left = node;
}
}
if (result & SYNCTEX_MASK_RIGHT) {
/* the right node is new, try to narrow the result */
if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) {
bestNodesRef->right = node;
}
if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) {
bestNodesRef->right = node;
}
}
}
return result;
}
SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef,synctex_bool_t visible) {
if (node) {
switch(node->class->type) {
case synctex_node_type_hbox:
return __synctex_eq_get_closest_children_in_hbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible);
case synctex_node_type_vbox:
return __synctex_eq_get_closest_children_in_vbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible);
}
}
return 0;
}
SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int* distanceRef, synctex_bool_t visible);
SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int* distanceRef, synctex_bool_t visible) {
synctex_node_t best_node = NULL;
if ((node = SYNCTEX_CHILD(node))) {
do {
int distance = _synctex_node_distance_to_point(hitPoint,node,visible);
synctex_node_t candidate = NULL;
if (distance<=*distanceRef) {
*distanceRef = distance;
best_node = node;
}
switch(node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_hbox:
if ((candidate = __synctex_eq_closest_child(hitPoint,node,distanceRef,visible))) {
best_node = candidate;
}
}
} while((node = SYNCTEX_SIBLING(node)));
}
return best_node;
}
SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) {
if (node) {
switch(node->class->type) {
case synctex_node_type_hbox:
case synctex_node_type_vbox:
{
int best_distance = INT_MAX;
synctex_node_t best_node = __synctex_eq_closest_child(hitPoint,node,&best_distance,visible);
if ((best_node)) {
synctex_node_t child = NULL;
switch(best_node->class->type) {
case synctex_node_type_vbox:
case synctex_node_type_hbox:
if ((child = SYNCTEX_CHILD(best_node))) {
best_distance = _synctex_node_distance_to_point(hitPoint,child,visible);
while((child = SYNCTEX_SIBLING(child))) {
int distance = _synctex_node_distance_to_point(hitPoint,child,visible);
if (distance<=best_distance) {
best_distance = distance;
best_node = child;
}
}
}
}
}
return best_node;
}
}
}
return NULL;
}
# ifdef SYNCTEX_NOTHING
# pragma mark -
# pragma mark Updater
# endif
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 {
gzFile 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))
synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * build_directory) {
synctex_updater_t updater = NULL;
char * synctex = NULL;
synctex_io_mode_t io_mode = 0;
const char * mode = NULL;
/* prepare the updater, the memory is the only one dynamically allocated */
updater = (synctex_updater_t)_synctex_malloc(sizeof(synctex_updater_t));
if (NULL == updater) {
_synctex_error("! synctex_updater_new_with_file: malloc problem");
return NULL;
}
if (_synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_ADD_QUOTES,&io_mode)
&& _synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_DONT_ADD_QUOTES,&io_mode)) {
return_on_error:
free(updater);
updater = NULL;
return NULL;
}
/* OK, the file exists, we close it and reopen it with the correct mode.
* The receiver is now the owner of the "synctex" variable. */
gzclose(SYNCTEX_FILE);
SYNCTEX_FILE = NULL;
SYNCTEX_NO_GZ = (io_mode&synctex_io_gz_mask)?synctex_NO:synctex_YES;
mode = _synctex_get_io_mode_name(io_mode|synctex_io_append_mask);/* either "a" or "ab", depending on the file extension */
if (SYNCTEX_NO_GZ) {
if (NULL == (SYNCTEX_FILE = (void *)fopen(synctex,mode))) {
no_write_error:
_synctex_error("! synctex_updater_new_with_file: Can't append to %s",synctex);
free(synctex);
goto return_on_error;
}
updater->fprintf = (synctex_fprintf_t)(&fprintf);
} else {
if (NULL == (SYNCTEX_FILE = (void *)gzopen(synctex,mode))) {
goto no_write_error;
}
updater->fprintf = (synctex_fprintf_t)(&gzprintf);
}
printf("SyncTeX: updating %s...",synctex);
free(synctex);
return updater;
}
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;
}