You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8678 lines
322 KiB
8678 lines
322 KiB
/* |
|
Copyright (c) 2008-2017 jerome DOT laurens AT u-bourgogne DOT fr |
|
|
|
This file is part of the __SyncTeX__ package. |
|
|
|
[//]: # (Latest Revision: Fri Jul 14 16:20:41 UTC 2017) |
|
[//]: # (Version: 1.19) |
|
|
|
See `synctex_parser_readme.md` 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. |
|
|
|
*/ |
|
|
|
/* 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. */ |
|
|
|
/** |
|
* \file synctex_parser.c |
|
* \brief SyncTeX file parser and controller. |
|
* - author: Jérôme LAURENS |
|
* \version 1.19 |
|
* \date Mon Apr 24 07:08:56 UTC 2017 |
|
* |
|
* Reads and parse *.synctex[.gz] files, |
|
* performs edit and display queries. |
|
* |
|
* See |
|
* - synctex_scanner_new_with_output_file |
|
* - synctex_scanner_parse |
|
* - synctex_scanner_free |
|
* - synctex_display_query |
|
* - synctex_edit_query |
|
* - synctex_scanner_next_result |
|
* - synctex_scanner_reset_result |
|
* |
|
* 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 node 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 and forms, containing boxes, glues, kerns... |
|
* The third tree allows to browse leaves according to tag and line. |
|
*/ |
|
# 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 |
|
# 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 |
|
|
|
#include "synctex_parser_advanced.h" |
|
|
|
SYNCTEX_INLINE static int _synctex_abs(int x) { |
|
return x>0? x: -x; |
|
} |
|
/* These are the possible extensions of the synctex file */ |
|
const char * synctex_suffix = ".synctex"; |
|
const char * synctex_suffix_gz = ".gz"; |
|
|
|
typedef synctex_node_p(*synctex_node_new_f)(synctex_scanner_p); |
|
typedef void(*synctex_node_fld_f)(synctex_node_p); |
|
typedef char *(*synctex_node_str_f)(synctex_node_p); |
|
|
|
/** |
|
* Pseudo class. |
|
* - author: J. Laurens |
|
* |
|
* Each nodes has a class, it is therefore called an object. |
|
* Each class has a unique scanner. |
|
* Each class has a type which is a unique identifier. |
|
* Each class has a node mask which identifies node's attributes. |
|
* Each class has an info mask which info's attributes. |
|
* The class points to various methods, |
|
* each of them vary amongst objects. |
|
* The navigator records the offsets of the tree members getters. |
|
* The modelator records the offsets of the data members getters, relative to the last navigator getter. |
|
*/ |
|
|
|
/* 8 fields + size: spcflnat */ |
|
typedef struct synctex_tree_model_t { |
|
int sibling; |
|
int parent; |
|
int child; |
|
int friend; |
|
int last; |
|
int next_hbox; |
|
int arg_sibling; |
|
int target; |
|
int size; |
|
} synctex_tree_model_s; |
|
typedef const synctex_tree_model_s * synctex_tree_model_p; |
|
|
|
typedef struct synctex_data_model_t { |
|
int tag; |
|
int line; |
|
int column; |
|
int h; |
|
int v; |
|
int width; |
|
int height; |
|
int depth; |
|
int mean_line; |
|
int weight; |
|
int h_V; |
|
int v_V; |
|
int width_V; |
|
int height_V; |
|
int depth_V; |
|
int name; |
|
int page; |
|
int size; |
|
} synctex_data_model_s; |
|
|
|
static const synctex_data_model_s synctex_data_model_none = { |
|
-1, /* tag */ |
|
-1, /* line */ |
|
-1, /* column */ |
|
-1, /* h */ |
|
-1, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
0 |
|
}; |
|
typedef const synctex_data_model_s * synctex_data_model_p; |
|
|
|
typedef int (*synctex_int_getter_f)(synctex_node_p); |
|
typedef struct synctex_tlcpector_t { |
|
synctex_int_getter_f tag; |
|
synctex_int_getter_f line; |
|
synctex_int_getter_f column; |
|
} synctex_tlcpector_s; |
|
typedef const synctex_tlcpector_s * synctex_tlcpector_p; |
|
static int _synctex_int_none(synctex_node_p node) { |
|
(void)node; /* unused */ |
|
return 0; |
|
} |
|
static const synctex_tlcpector_s synctex_tlcpector_none = { |
|
&_synctex_int_none, /* tag */ |
|
&_synctex_int_none, /* line */ |
|
&_synctex_int_none, /* column */ |
|
}; |
|
|
|
typedef struct synctex_inspector_t { |
|
synctex_int_getter_f h; |
|
synctex_int_getter_f v; |
|
synctex_int_getter_f width; |
|
synctex_int_getter_f height; |
|
synctex_int_getter_f depth; |
|
} synctex_inspector_s; |
|
typedef const synctex_inspector_s * synctex_inspector_p; |
|
static const synctex_inspector_s synctex_inspector_none = { |
|
&_synctex_int_none, /* h */ |
|
&_synctex_int_none, /* v */ |
|
&_synctex_int_none, /* width */ |
|
&_synctex_int_none, /* height */ |
|
&_synctex_int_none, /* depth */ |
|
}; |
|
|
|
typedef float (*synctex_float_getter_f)(synctex_node_p); |
|
typedef struct synctex_vispector_t { |
|
synctex_float_getter_f h; |
|
synctex_float_getter_f v; |
|
synctex_float_getter_f width; |
|
synctex_float_getter_f height; |
|
synctex_float_getter_f depth; |
|
} synctex_vispector_s; |
|
static float _synctex_float_none(synctex_node_p node) { |
|
(void)node; /* unused */ |
|
return 0; |
|
} |
|
static const synctex_vispector_s synctex_vispector_none = { |
|
&_synctex_float_none, /* h */ |
|
&_synctex_float_none, /* v */ |
|
&_synctex_float_none, /* width */ |
|
&_synctex_float_none, /* height */ |
|
&_synctex_float_none, /* depth */ |
|
}; |
|
typedef const synctex_vispector_s * synctex_vispector_p; |
|
|
|
struct synctex_class_t { |
|
synctex_scanner_p scanner; |
|
synctex_node_type_t type; |
|
synctex_node_new_f new; |
|
synctex_node_fld_f free; |
|
synctex_node_fld_f log; |
|
synctex_node_fld_f display; |
|
synctex_node_str_f abstract; |
|
synctex_tree_model_p navigator; |
|
synctex_data_model_p modelator; |
|
synctex_tlcpector_p tlcpector; |
|
synctex_inspector_p inspector; |
|
synctex_vispector_p vispector; |
|
}; |
|
|
|
/** |
|
* Nota bene: naming convention. |
|
* For static API, when the name contains proxy, it applies to proxies. |
|
* When the name contains noxy, it applies to non proxies only. |
|
* When the name contains node, weel it depends... |
|
*/ |
|
|
|
typedef synctex_node_p synctex_proxy_p; |
|
typedef synctex_node_p synctex_noxy_p; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Abstract OBJECTS and METHODS |
|
# endif |
|
|
|
/** |
|
* \def SYNCTEX_MSG_SEND |
|
* \brief Takes care of sending the given message if possible. |
|
* - parameter NODE: of type synctex_node_p |
|
* - parameter SELECTOR: one of the class pointer properties |
|
*/ |
|
# define SYNCTEX_MSG_SEND(NODE,SELECTOR) do {\ |
|
synctex_node_p N__ = NODE;\ |
|
if (N__ && N__->class->SELECTOR) {\ |
|
(*(N__->class->SELECTOR))(N__);\ |
|
}\ |
|
} while (synctex_NO) |
|
|
|
/** |
|
* Free the given node by sending the free message. |
|
* - parameter NODE: of type synctex_node_p |
|
*/ |
|
void synctex_node_free(synctex_node_p node) { |
|
SYNCTEX_MSG_SEND(node,free); |
|
} |
|
# if defined(SYNCTEX_TESTING) |
|
# if !defined(SYNCTEX_USE_HANDLE) |
|
# define SYNCTEX_USE_HANDLE 1 |
|
# endif |
|
# if !defined(SYNCTEX_USE_CHARINDEX) |
|
# define SYNCTEX_USE_CHARINDEX 1 |
|
# endif |
|
# endif |
|
SYNCTEX_INLINE static synctex_node_p _synctex_new_handle_with_target(synctex_node_p target); |
|
# if defined(SYNCTEX_USE_HANDLE) |
|
# define SYNCTEX_SCANNER_FREE_HANDLE(SCANR) \ |
|
__synctex_scanner_free_handle(SCANR) |
|
# define SYNCTEX_SCANNER_REMOVE_HANDLE_TO(WHAT) \ |
|
__synctex_scanner_remove_handle_to(WHAT) |
|
# define SYNCTEX_REGISTER_HANDLE_TO(NODE) \ |
|
__synctex_scanner_register_handle_to(NODE) |
|
# else |
|
# define SYNCTEX_SCANNER_FREE_HANDLE(SCANR) |
|
# define SYNCTEX_SCANNER_REMOVE_HANDLE_TO(WHAT) |
|
# define SYNCTEX_REGISTER_HANDLE_TO(NODE) |
|
# endif |
|
|
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
# define SYNCTEX_CHARINDEX(NODE) (NODE->char_index) |
|
# define SYNCTEX_LINEINDEX(NODE) (NODE->line_index) |
|
# define SYNCTEX_PRINT_CHARINDEX_FMT "#%i" |
|
# define SYNCTEX_PRINT_CHARINDEX_WHAT ,SYNCTEX_CHARINDEX(node) |
|
# define SYNCTEX_PRINT_CHARINDEX \ |
|
printf(SYNCTEX_PRINT_CHARINDEX_FMT SYNCTEX_PRINT_CHARINDEX_WHAT) |
|
# define SYNCTEX_PRINT_LINEINDEX_FMT "L#%i" |
|
# define SYNCTEX_PRINT_LINEINDEX_WHAT ,SYNCTEX_LINEINDEX(node) |
|
# define SYNCTEX_PRINT_LINEINDEX \ |
|
printf(SYNCTEX_PRINT_LINEINDEX_FMT SYNCTEX_PRINT_LINEINDEX_WHAT) |
|
# define SYNCTEX_PRINT_CHARINDEX_NL \ |
|
printf(SYNCTEX_PRINT_CHARINDEX_FMT "\n" SYNCTEX_PRINT_CHARINDEX_WHAT) |
|
# define SYNCTEX_PRINT_LINEINDEX_NL \ |
|
printf(SYNCTEX_PRINT_CHARINDEX_FMT "\n"SYNCTEX_PRINT_LINEINDEX_WHAT) |
|
# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION)\ |
|
NODE->char_index = (synctex_charindex_t)(scanner->reader->charindex_offset+SYNCTEX_CUR-SYNCTEX_START+(CORRECTION)); \ |
|
NODE->line_index = scanner->reader->line_number; |
|
# else |
|
# define SYNCTEX_CHARINDEX(NODE) 0 |
|
# define SYNCTEX_LINEINDEX(NODE) 0 |
|
# define SYNCTEX_PRINT_CHARINDEX_FMT |
|
# define SYNCTEX_PRINT_CHARINDEX_WHAT |
|
# define SYNCTEX_PRINT_CHARINDEX |
|
# define SYNCTEX_PRINT_CHARINDEX |
|
# define SYNCTEX_PRINT_LINEINDEX_FMT |
|
# define SYNCTEX_PRINT_LINEINDEX_WHAT |
|
# define SYNCTEX_PRINT_LINEINDEX |
|
# define SYNCTEX_PRINT_CHARINDEX_NL printf("\n") |
|
# define SYNCTEX_PRINT_LINEINDEX_NL printf("\n") |
|
# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION) |
|
# endif |
|
|
|
/** |
|
* The next macros are used to access the node tree info |
|
* SYNCTEX_DATA(node) points to the first synctex integer or pointer data of node |
|
* SYNCTEX_DATA(node)[index] is the information at index |
|
* for example, the page of a sheet is stored in SYNCTEX_DATA(sheet)[_synctex_data_page_idx] |
|
* - parameter NODE: of type synctex_node_p |
|
* If the name starts with "__", the argument is nonullable |
|
*/ |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Tree SETGET |
|
# endif |
|
|
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG > 1000 |
|
#define SYNCTEX_PARAMETER_ASSERT(WHAT) \ |
|
do { \ |
|
if (!(WHAT)) { \ |
|
printf("! Parameter failure: %s\n",#WHAT); \ |
|
} \ |
|
} while (synctex_NO) |
|
#define DEFINE_SYNCTEX_TREE_HAS(WHAT)\ |
|
static synctex_bool_t _synctex_tree_has_##WHAT(synctex_node_p node) {\ |
|
if (node) {\ |
|
if (node->class->navigator->WHAT>=0) {\ |
|
return synctex_YES; \ |
|
} else {\ |
|
printf("WARNING: NO tree %s for %s\n", #WHAT, synctex_node_isa(node));\ |
|
}\ |
|
}\ |
|
return synctex_NO;\ |
|
} |
|
#else |
|
#define SYNCTEX_PARAMETER_ASSERT(WHAT) |
|
#define DEFINE_SYNCTEX_TREE_HAS(WHAT) \ |
|
static synctex_bool_t _synctex_tree_has_##WHAT(synctex_node_p node) {\ |
|
return (node && (node->class->navigator->WHAT>=0));\ |
|
} |
|
#endif |
|
|
|
# define DEFINE_SYNCTEX_TREE__GET(WHAT) \ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_tree_##WHAT(synctex_non_null_node_p node) {\ |
|
return node->data[node->class->navigator->WHAT].as_node;\ |
|
} |
|
# define DEFINE_SYNCTEX_TREE_GET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE__GET(WHAT) \ |
|
static synctex_node_p _synctex_tree_##WHAT(synctex_node_p node) {\ |
|
if (_synctex_tree_has_##WHAT(node)) {\ |
|
return __synctex_tree_##WHAT(node);\ |
|
}\ |
|
return 0;\ |
|
} |
|
# define DEFINE_SYNCTEX_TREE__RESET(WHAT) \ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_tree_reset_##WHAT(synctex_non_null_node_p node) {\ |
|
synctex_node_p old = node->data[node->class->navigator->WHAT].as_node;\ |
|
node->data[node->class->navigator->WHAT].as_node=NULL;\ |
|
return old;\ |
|
} |
|
# define DEFINE_SYNCTEX_TREE_RESET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE__RESET(WHAT) \ |
|
SYNCTEX_INLINE static synctex_node_p _synctex_tree_reset_##WHAT(synctex_node_p node) {\ |
|
return _synctex_tree_has_##WHAT(node)? \ |
|
__synctex_tree_reset_##WHAT(node): NULL; \ |
|
} |
|
# define DEFINE_SYNCTEX_TREE__SET(WHAT) \ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_tree_set_##WHAT(synctex_non_null_node_p node, synctex_node_p new_value) {\ |
|
synctex_node_p old = __synctex_tree_##WHAT(node);\ |
|
node->data[node->class->navigator->WHAT].as_node=new_value;\ |
|
return old;\ |
|
} |
|
# define DEFINE_SYNCTEX_TREE_SET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE__SET(WHAT) \ |
|
SYNCTEX_INLINE static synctex_node_p _synctex_tree_set_##WHAT(synctex_node_p node, synctex_node_p new_value) {\ |
|
return _synctex_tree_has_##WHAT(node)?\ |
|
__synctex_tree_set_##WHAT(node,new_value):NULL;\ |
|
} |
|
# define DEFINE_SYNCTEX_TREE__GETSETRESET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE__GET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE__SET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE__RESET(WHAT) |
|
|
|
# define DEFINE_SYNCTEX_TREE_GETSET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_HAS(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_GET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_SET(WHAT) |
|
|
|
# define DEFINE_SYNCTEX_TREE_GETRESET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_HAS(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_GET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_RESET(WHAT) |
|
|
|
# define DEFINE_SYNCTEX_TREE_GETSETRESET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_HAS(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_GET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_SET(WHAT) \ |
|
DEFINE_SYNCTEX_TREE_RESET(WHAT) |
|
|
|
/* |
|
* _synctex_tree_set_... methods return the old value. |
|
* The return value of _synctex_tree_set_child and |
|
* _synctex_tree_set_sibling must be released somehown. |
|
*/ |
|
DEFINE_SYNCTEX_TREE__GETSETRESET(sibling) |
|
DEFINE_SYNCTEX_TREE_GETSETRESET(parent) |
|
DEFINE_SYNCTEX_TREE_GETSETRESET(child) |
|
DEFINE_SYNCTEX_TREE_GETSETRESET(friend) |
|
DEFINE_SYNCTEX_TREE_GETSET(last) |
|
DEFINE_SYNCTEX_TREE_GETSET(next_hbox) |
|
DEFINE_SYNCTEX_TREE_GETSET(arg_sibling) |
|
DEFINE_SYNCTEX_TREE_GETSET(target) |
|
|
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>1000 |
|
# undef SYNCTEX_USE_NODE_COUNT |
|
# define SYNCTEX_USE_NODE_COUNT 1 |
|
#endif |
|
#if defined SYNCTEX_USE_NODE_COUNT && SYNCTEX_USE_NODE_COUNT>0 |
|
# define SYNCTEX_DECLARE_NODE_COUNT int node_count; |
|
# define SYNCTEX_INIT_NODE_COUNT \ |
|
do { node_count = 0; } while(synctex_NO) |
|
#else |
|
# define SYNCTEX_DECLARE_NODE_COUNT |
|
# define SYNCTEX_INIT_NODE_COUNT |
|
#endif |
|
|
|
#if defined SYNCTEX_USE_NODE_COUNT && SYNCTEX_USE_NODE_COUNT>10 |
|
# define SYNCTEX_DID_NEW(N) _synctex_did_new(N) |
|
# define SYNCTEX_WILL_FREE(N) _synctex_will_free(N) |
|
#else |
|
# define SYNCTEX_DID_NEW(N) |
|
# define SYNCTEX_WILL_FREE(N) |
|
#endif |
|
|
|
#define SYNCTEX_HAS_CHILDREN(NODE) (NODE && _synctex_tree_child(NODE)) |
|
# ifdef __SYNCTEX_WORK__ |
|
# include "/usr/include/zlib.h" |
|
# else |
|
# include <zlib.h> |
|
# endif |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark STATUS |
|
# endif |
|
/* 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: |
|
It must be the biggest one */ |
|
# define SYNCTEX_STATUS_OK (SYNCTEX_STATUS_NOT_OK+1) |
|
/* Generic error: */ |
|
# define SYNCTEX_STATUS_ERROR (SYNCTEX_STATUS_EOF-1) |
|
/* Parameter error: */ |
|
# define SYNCTEX_STATUS_BAD_ARGUMENT (SYNCTEX_STATUS_ERROR-1) |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark File reader |
|
# endif |
|
|
|
/* We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */ |
|
/* Actually, the minimum buffer size is driven by integer and float parsing, including the unit. |
|
* ±0.123456789e123?? |
|
*/ |
|
# define SYNCTEX_BUFFER_MIN_SIZE 32 |
|
# define SYNCTEX_BUFFER_SIZE 32768 |
|
|
|
#if SYNCTEX_BUFFER_SIZE >= UINT_MAX |
|
# error BAD BUFFER SIZE(1) |
|
#endif |
|
#if SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE |
|
# error BAD BUFFER SIZE(2) |
|
#endif |
|
|
|
typedef struct synctex_reader_t { |
|
gzFile file; /* The (possibly compressed) file */ |
|
char * output; |
|
char * synctex; |
|
char * current; /* current location in the buffer */ |
|
char * start; /* start of the buffer */ |
|
char * end; /* end of the buffer */ |
|
size_t min_size; |
|
size_t size; |
|
int lastv; |
|
int line_number; |
|
SYNCTEX_DECLARE_CHAR_OFFSET |
|
} synctex_reader_s; |
|
|
|
typedef synctex_reader_s * synctex_reader_p; |
|
|
|
typedef struct { |
|
synctex_status_t status; |
|
char * synctex; |
|
gzFile file; |
|
synctex_io_mode_t io_mode; |
|
} synctex_open_s; |
|
|
|
/* 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. |
|
* - returns: an open structure which status is |
|
* SYNCTEX_STATUS_OK on success, |
|
* SYNCTEX_STATUS_ERROR on failure. |
|
* - note: on success, the caller is the owner |
|
* of the fields of the returned open structure. |
|
*/ |
|
static synctex_open_s __synctex_open_v2(const char * output, synctex_io_mode_t io_mode, synctex_bool_t add_quotes) { |
|
synctex_open_s open = {SYNCTEX_STATUS_ERROR, NULL, NULL, io_mode}; |
|
char * quoteless_synctex_name = NULL; |
|
const char * mode = _synctex_get_io_mode_name(open.io_mode); |
|
size_t size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1; |
|
if (NULL == (open.synctex = (char *)malloc(size))) { |
|
_synctex_error("! __synctex_open_v2: Memory problem (1)\n"); |
|
return open; |
|
} |
|
/* 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 (open.synctex != strcpy(open.synctex,output)) { |
|
_synctex_error("! __synctex_open_v2: Copy problem\n"); |
|
return_on_error: |
|
free(open.synctex); |
|
open.synctex = NULL; |
|
free(quoteless_synctex_name);/* We MUST have quoteless_synctex_name<>synctex_name */ |
|
return open; |
|
} |
|
/* remove the last path extension if any */ |
|
_synctex_strip_last_path_extension(open.synctex); |
|
if (!strlen(open.synctex)) { |
|
goto return_on_error; |
|
} |
|
/* now insert quotes. */ |
|
if (add_quotes) { |
|
char * quoted = NULL; |
|
if (_synctex_copy_with_quoting_last_path_component(open.synctex,"ed,size) || quoted == NULL) { |
|
/* There was an error or quoting does not make sense: */ |
|
goto return_on_error; |
|
} |
|
quoteless_synctex_name = open.synctex; |
|
open.synctex = quoted; |
|
} |
|
/* Now add to open.synctex the first path extension. */ |
|
if (open.synctex != strcat(open.synctex,synctex_suffix)){ |
|
_synctex_error("! __synctex_open_v2: 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 == (open.file = gzopen(open.synctex,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",open.synctex,errno); |
|
goto return_on_error; |
|
} |
|
/* Apparently, there is no uncompressed synctex file. Try the compressed version */ |
|
if (open.synctex != strcat(open.synctex,synctex_suffix_gz)){ |
|
_synctex_error("! __synctex_open_v2: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz); |
|
goto return_on_error; |
|
} |
|
open.io_mode |= synctex_io_gz_mask; |
|
mode = _synctex_get_io_mode_name(open.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 == (open.file = gzopen(open.synctex,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",open.synctex,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(open.file); |
|
if (rename(open.synctex,quoteless_synctex_name)) { |
|
_synctex_error("Could not rename %s to %s, error %i\n",open.synctex,quoteless_synctex_name,errno); |
|
/* We could not rename, reopen the file with the quoted name. */ |
|
if (NULL == (open.file = gzopen(open.synctex,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",open.synctex,errno); |
|
} |
|
goto return_on_error; |
|
} |
|
} else { |
|
/* The file has been successfully renamed */ |
|
if (NULL == (open.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(open.synctex); |
|
open.synctex = quoteless_synctex_name; |
|
quoteless_synctex_name = NULL; |
|
} |
|
} |
|
/* The operation is successful, return the arguments by value. */ |
|
open.status = SYNCTEX_STATUS_OK; |
|
return open; |
|
} |
|
|
|
/* Opens the output file, taking into account the eventual build_directory. |
|
* - returns: an open structure which status is |
|
* SYNCTEX_STATUS_OK on success, |
|
* SYNCTEX_STATUS_ERROR on failure. |
|
* - note: on success, the caller is the owner |
|
* of the fields of the returned open structure. |
|
*/ |
|
static synctex_open_s _synctex_open_v2(const char * output, const char * build_directory, synctex_io_mode_t io_mode, synctex_bool_t add_quotes) { |
|
synctex_open_s open = __synctex_open_v2(output,io_mode,add_quotes); |
|
if (open.status == SYNCTEX_STATUS_OK) { |
|
return open; |
|
} |
|
if (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 *)_synctex_malloc(size))) { |
|
if (is_absolute) { |
|
build_output[0] = '\0'; |
|
} else { |
|
if (build_output != strcpy(build_output,output)) { |
|
_synctex_free(build_output); |
|
return open; |
|
} |
|
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,"/")) { |
|
_synctex_free(build_output); |
|
return open; |
|
} |
|
} |
|
/* Append the last path component of the output. */ |
|
if (build_output != strcat(build_output,lpc)) { |
|
_synctex_free(build_output); |
|
return open; |
|
} |
|
open = __synctex_open_v2(build_output,io_mode,add_quotes); |
|
} |
|
_synctex_free(build_output); |
|
} /* if ((build_output... */ |
|
} /* if (build_directory...) */ |
|
return open; |
|
} |
|
void synctex_reader_free(synctex_reader_p reader) { |
|
if (reader) { |
|
_synctex_free(reader->output); |
|
_synctex_free(reader->synctex); |
|
_synctex_free(reader->start); |
|
gzclose(reader->file); |
|
_synctex_free(reader); |
|
} |
|
} |
|
/* |
|
* Return reader on success. |
|
* Deallocate reader and return NULL on failure. |
|
*/ |
|
synctex_reader_p synctex_reader_init_with_output_file(synctex_reader_p reader, const char * output, const char * build_directory) { |
|
if (reader) { |
|
/* now open the synctex file */ |
|
synctex_open_s open = _synctex_open_v2(output,build_directory,0,synctex_ADD_QUOTES); |
|
if (open.status<SYNCTEX_STATUS_OK) { |
|
open = _synctex_open_v2(output,build_directory,0,synctex_DONT_ADD_QUOTES); |
|
if (open.status<SYNCTEX_STATUS_OK) { |
|
synctex_reader_free(reader); |
|
return NULL; |
|
} |
|
} |
|
reader->synctex = open.synctex; |
|
reader->file = open.file; |
|
/* make a private copy of output */ |
|
if (NULL == (reader->output = (char *)_synctex_malloc(strlen(output)+1))){ |
|
_synctex_error("! synctex_scanner_new_with_output_file: Memory problem (2), reader's output is not reliable."); |
|
} else if (reader->output != strcpy(reader->output,output)) { |
|
_synctex_free(reader->output); |
|
reader->output = NULL; |
|
_synctex_error("! synctex_scanner_new_with_output_file: Copy problem, reader's output is not reliable."); |
|
} |
|
reader->start = reader->end = reader->current = NULL; |
|
reader->min_size = SYNCTEX_BUFFER_MIN_SIZE; |
|
reader->size = SYNCTEX_BUFFER_SIZE; |
|
reader->start = reader->current = |
|
(char *)_synctex_malloc(reader->size+1); /* one more character for null termination */ |
|
if (NULL == reader->start) { |
|
_synctex_error("! malloc error in synctex_reader_init_with_output_file."); |
|
#ifdef SYNCTEX_DEBUG |
|
return reader; |
|
#else |
|
synctex_reader_free(reader); |
|
return NULL; |
|
#endif |
|
} |
|
reader->end = reader->start+reader->size; |
|
/* reader->end always points to a null terminating character. |
|
* Maybe there is another null terminating character between reader->current and reader->end-1. |
|
* At least, we are sure that reader->current points to a string covering a valid part of the memory. */ |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
reader->charindex_offset = -reader->size; |
|
# endif |
|
} |
|
return reader; |
|
} |
|
|
|
# if defined(SYNCTEX_USE_HANDLE) |
|
# define SYNCTEX_DECLARE_HANDLE synctex_node_p handle; |
|
# else |
|
# define SYNCTEX_DECLARE_HANDLE |
|
# endif |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark SCANNER |
|
# 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 { |
|
synctex_reader_p reader; |
|
SYNCTEX_DECLARE_NODE_COUNT |
|
SYNCTEX_DECLARE_HANDLE |
|
char * output_fmt; /* dvi or pdf, not yet used */ |
|
synctex_iterator_p iterator;/* result iterator */ |
|
int version; /* 1, not yet used */ |
|
struct { |
|
unsigned has_parsed:1; /* Whether the scanner has parsed its underlying synctex file. */ |
|
unsigned postamble:1; /* Whether the scanner has parsed its underlying synctex file. */ |
|
unsigned reserved:sizeof(unsigned)-2; /* alignment */ |
|
} flags; |
|
int pre_magnification; /* magnification from the synctex preamble */ |
|
int pre_unit; /* unit from the synctex preamble */ |
|
int pre_x_offset; /* X offset 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_p input; /* The first input node, its siblings are the other input nodes */ |
|
synctex_node_p sheet; /* The first sheet node, its siblings are the other sheet nodes */ |
|
synctex_node_p form; /* The first form, its siblings are the other forms */ |
|
synctex_node_p ref_in_sheet; /* The first form ref node in sheet, its friends are the other form ref nodes */ |
|
synctex_node_p ref_in_form; /* The first form ref node, its friends are the other form ref nodes in sheet */ |
|
int number_of_lists; /* The number of friend lists */ |
|
synctex_node_r lists_of_friends;/* The friend lists */ |
|
synctex_class_s class[synctex_node_number_of_types]; /* The classes of the nodes of the scanner */ |
|
int display_switcher; |
|
char * display_prompt; |
|
}; |
|
|
|
/** |
|
* Create a new node of the given type. |
|
* - parameter scanner: of type synctex_node_p |
|
* - parameter type: a type, the client is responsible |
|
* to ask for an acceptable type. |
|
*/ |
|
synctex_node_p synctex_node_new(synctex_scanner_p scanner, synctex_node_type_t type) { |
|
return scanner? scanner->class[type].new(scanner):NULL; |
|
} |
|
# if defined(SYNCTEX_USE_HANDLE) |
|
SYNCTEX_INLINE static void __synctex_scanner_free_handle(synctex_scanner_p scanner) { |
|
synctex_node_free(scanner->handle); |
|
} |
|
SYNCTEX_INLINE static void __synctex_scanner_remove_handle_to(synctex_node_p node) { |
|
synctex_node_p arg_sibling = NULL; |
|
synctex_node_p handle = node->class->scanner->handle; |
|
while (handle) { |
|
synctex_node_p sibling; |
|
if (node == _synctex_tree_target(handle)) { |
|
sibling = __synctex_tree_reset_sibling(handle); |
|
if (arg_sibling) { |
|
__synctex_tree_set_sibling(arg_sibling, sibling); |
|
} else { |
|
node->class->scanner->handle = sibling; |
|
} |
|
synctex_node_free(handle); |
|
break; |
|
} else { |
|
sibling = __synctex_tree_sibling(handle); |
|
} |
|
arg_sibling = handle; |
|
handle = sibling; |
|
} |
|
} |
|
SYNCTEX_INLINE static void __synctex_scanner_register_handle_to(synctex_node_p node) { |
|
synctex_node_p NNN = _synctex_new_handle_with_target(node); |
|
__synctex_tree_set_sibling(NNN,node->class->scanner->handle); |
|
node->class->scanner->handle = NNN; |
|
} |
|
#endif |
|
#if defined SYNCTEX_USE_NODE_COUNT && SYNCTEX_USE_NODE_COUNT>10 |
|
SYNCTEX_INLINE static void _synctex_did_new(synctex_node_p node) { |
|
printf("NODE CREATED # %i, %s, %p\n", |
|
(node->class->scanner->node_count)++, |
|
synctex_node_isa(node), |
|
node); |
|
} |
|
SYNCTEX_INLINE static void _synctex_will_free(synctex_node_p node) { |
|
printf("NODE DELETED # %i, %s, %p\n", |
|
--(node->class->scanner->node_count), |
|
synctex_node_isa(node), |
|
node); |
|
} |
|
#endif |
|
|
|
/** |
|
* Free the given node. |
|
* - parameter node: of type synctex_node_p |
|
* - note: 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_p node) { |
|
if (node) { |
|
SYNCTEX_SCANNER_REMOVE_HANDLE_TO(node); |
|
SYNCTEX_WILL_FREE(node); |
|
synctex_node_free(__synctex_tree_sibling(node)); |
|
synctex_node_free(_synctex_tree_child(node)); |
|
_synctex_free(node); |
|
} |
|
return; |
|
} |
|
|
|
/** |
|
* Free the given leaf node. |
|
* - parameter node: of type synctex_node_p, with no child nor sibling. |
|
* - note: 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 no children. |
|
*/ |
|
static void _synctex_free_leaf(synctex_node_p node) { |
|
if (node) { |
|
SYNCTEX_SCANNER_REMOVE_HANDLE_TO(node); |
|
SYNCTEX_WILL_FREE(node); |
|
synctex_node_free(__synctex_tree_sibling(node)); |
|
_synctex_free(node); |
|
} |
|
return; |
|
} |
|
|
|
/** |
|
SYNCTEX_CUR, SYNCTEX_START and SYNCTEX_END are convenient shortcuts |
|
*/ |
|
# define SYNCTEX_CUR (scanner->reader->current) |
|
# define SYNCTEX_START (scanner->reader->start) |
|
# define SYNCTEX_END (scanner->reader->end) |
|
|
|
/* 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 |
|
*/ |
|
#if defined(SYNCTEX_USE_CHARINDEX) |
|
synctex_node_p synctex_scanner_handle(synctex_scanner_p scanner) { |
|
return scanner? scanner->handle:NULL; |
|
} |
|
#endif |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Decoding prototypes |
|
# endif |
|
|
|
typedef struct { |
|
int integer; |
|
synctex_status_t status; |
|
} synctex_is_s; |
|
|
|
static synctex_is_s _synctex_decode_int(synctex_scanner_p scanner); |
|
static synctex_is_s _synctex_decode_int_opt(synctex_scanner_p scanner, int default_value); |
|
static synctex_is_s _synctex_decode_int_v(synctex_scanner_p scanner); |
|
|
|
typedef struct { |
|
char * string; |
|
synctex_status_t status; |
|
} synctex_ss_s; |
|
|
|
static synctex_ss_s _synctex_decode_string(synctex_scanner_p scanner); |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Data SETGET |
|
# endif |
|
|
|
/** |
|
* The next macros are used to access the node data info |
|
* through the class modelator integer fields. |
|
* - parameter NODE: of type synctex_node_p |
|
*/ |
|
# define SYNCTEX_DATA(NODE) ((*((((NODE)->class))->info))(NODE)) |
|
#if defined SYNCTEX_DEBUG > 1000 |
|
# define DEFINE_SYNCTEX_DATA_HAS(WHAT) \ |
|
SYNCTEX_INLINE static synctex_bool_t __synctex_data_has_##WHAT(synctex_node_p node) {\ |
|
return (node && (node->class->modelator->WHAT>=0));\ |
|
}\ |
|
SYNCTEX_INLINE static synctex_bool_t _synctex_data_has_##WHAT(synctex_node_p node) {\ |
|
if (node && (node->class->modelator->WHAT<0)) {\ |
|
printf("WARNING: NO %s for %s\n", #WHAT, synctex_node_isa(node));\ |
|
}\ |
|
return __synctex_data_has_##WHAT(node);\ |
|
} |
|
#else |
|
# define DEFINE_SYNCTEX_DATA_HAS(WHAT) \ |
|
SYNCTEX_INLINE static synctex_bool_t __synctex_data_has_##WHAT(synctex_node_p node) {\ |
|
return (node && (node->class->modelator->WHAT>=0));\ |
|
}\ |
|
SYNCTEX_INLINE static synctex_bool_t _synctex_data_has_##WHAT(synctex_node_p node) {\ |
|
return __synctex_data_has_##WHAT(node);\ |
|
} |
|
#endif |
|
|
|
SYNCTEX_INLINE static synctex_data_p __synctex_data(synctex_node_p node) { |
|
return node->data+node->class->navigator->size; |
|
} |
|
# define DEFINE_SYNCTEX_DATA_INT_GETSET(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_HAS(WHAT)\ |
|
static int _synctex_data_##WHAT(synctex_node_p node) {\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
return __synctex_data(node)[node->class->modelator->WHAT].as_integer;\ |
|
}\ |
|
return 0;\ |
|
}\ |
|
static int _synctex_data_set_##WHAT(synctex_node_p node, int new_value) {\ |
|
int old = 0;\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
old = __synctex_data(node)[node->class->modelator->WHAT].as_integer;\ |
|
__synctex_data(node)[node->class->modelator->WHAT].as_integer=new_value;\ |
|
}\ |
|
return old;\ |
|
} |
|
#define DEFINE_SYNCTEX_DATA_INT_DECODE(WHAT) \ |
|
static synctex_status_t _synctex_data_decode_##WHAT(synctex_node_p node) {\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
synctex_is_s is = _synctex_decode_int(node->class->scanner);\ |
|
if (is.status == SYNCTEX_STATUS_OK) {\ |
|
_synctex_data_set_##WHAT(node,is.integer);\ |
|
} \ |
|
return is.status;\ |
|
}\ |
|
return SYNCTEX_STATUS_BAD_ARGUMENT;\ |
|
} |
|
# define DEFINE_SYNCTEX_DATA_INT_DECODE_v(WHAT) \ |
|
static synctex_status_t _synctex_data_decode_##WHAT##_v(synctex_node_p node) {\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
synctex_is_s is = _synctex_decode_int_v(node->class->scanner);\ |
|
if (is.status == SYNCTEX_STATUS_OK) {\ |
|
_synctex_data_set_##WHAT(node,is.integer);\ |
|
} \ |
|
return is.status;\ |
|
}\ |
|
return SYNCTEX_STATUS_BAD_ARGUMENT;\ |
|
} |
|
#define DEFINE_SYNCTEX_DATA_STR_GETSET(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_HAS(WHAT)\ |
|
static char * _synctex_data_##WHAT(synctex_node_p node) {\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
return node->data[node->class->navigator->size+node->class->modelator->WHAT].as_string;\ |
|
}\ |
|
return NULL;\ |
|
}\ |
|
static char * _synctex_data_set_##WHAT(synctex_node_p node, char * new_value) {\ |
|
char * old = "";\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
old = node->data[node->class->navigator->size+node->class->modelator->WHAT].as_string;\ |
|
node->data[node->class->navigator->size+node->class->modelator->WHAT].as_string =new_value;\ |
|
}\ |
|
return old;\ |
|
} |
|
#define DEFINE_SYNCTEX_DATA_STR_DECODE(WHAT) \ |
|
static synctex_status_t _synctex_data_decode_##WHAT(synctex_node_p node) {\ |
|
if (_synctex_data_has_##WHAT(node)) {\ |
|
synctex_ss_s ss = _synctex_decode_string(node->class->scanner);\ |
|
if (ss.status == SYNCTEX_STATUS_OK) {\ |
|
_synctex_data_set_##WHAT(node,ss.string);\ |
|
} \ |
|
return ss.status;\ |
|
}\ |
|
return SYNCTEX_STATUS_BAD_ARGUMENT;\ |
|
} |
|
#define DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_INT_DECODE(WHAT) |
|
#define DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE_v(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_INT_DECODE_v(WHAT) |
|
#define DEFINE_SYNCTEX_DATA_STR_GETSET_DECODE(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_STR_GETSET(WHAT) \ |
|
DEFINE_SYNCTEX_DATA_STR_DECODE(WHAT) |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark OBJECTS, their creators and destructors. |
|
# endif |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark input. |
|
# endif |
|
|
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(tag) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(line) |
|
DEFINE_SYNCTEX_DATA_STR_GETSET_DECODE(name) |
|
|
|
/* Input nodes only know about their sibling, which is another input node. |
|
* The synctex information is the _synctex_data_tag and _synctex_data_name |
|
* note: the input owns its name. */ |
|
|
|
# define SYNCTEX_INPUT_MARK "Input:" |
|
|
|
static const synctex_tree_model_s synctex_tree_model_input = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
-1, /* parent */ |
|
-1, /* child */ |
|
-1, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
-1, /* target */ |
|
synctex_tree_s_input_max |
|
}; |
|
static const synctex_data_model_s synctex_data_model_input = { |
|
synctex_data_input_tag_idx, /* tag */ |
|
synctex_data_input_line_idx,/* line */ |
|
-1, /* column */ |
|
-1, /* h */ |
|
-1, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
synctex_data_input_name_idx, /* name */ |
|
-1, /* page */ |
|
synctex_data_input_tln_max |
|
}; |
|
|
|
#define SYNCTEX_INSPECTOR_GETTER_F(WHAT)\ |
|
&_synctex_data_##WHAT, &_synctex_data_set_##WHAT |
|
|
|
static synctex_node_p _synctex_new_input(synctex_scanner_p scanner); |
|
static void _synctex_free_input(synctex_node_p node); |
|
static void _synctex_log_input(synctex_node_p node); |
|
static char * _synctex_abstract_input(synctex_node_p node); |
|
static void _synctex_display_input(synctex_node_p node); |
|
|
|
static const synctex_tlcpector_s synctex_tlcpector_input = { |
|
&_synctex_data_tag, /* tag */ |
|
&_synctex_int_none, /* line */ |
|
&_synctex_int_none, /* column */ |
|
}; |
|
|
|
static synctex_class_s 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 */ |
|
&_synctex_abstract_input, /* abstract */ |
|
&synctex_tree_model_input, /* tree model */ |
|
&synctex_data_model_input, /* data model */ |
|
&synctex_tlcpector_input, /* inspector */ |
|
&synctex_inspector_none, /* inspector */ |
|
&synctex_vispector_none, /* vispector */ |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_s_input_max+synctex_data_input_tln_max]; |
|
} synctex_input_s; |
|
|
|
static synctex_node_p _synctex_new_input(synctex_scanner_p scanner) { |
|
if (scanner) { |
|
synctex_node_p node = _synctex_malloc(sizeof(synctex_input_s)); |
|
if (node) { |
|
node->class = scanner->class+synctex_node_type_input; |
|
SYNCTEX_DID_NEW(node); |
|
SYNCTEX_IMPLEMENT_CHARINDEX(node,0); |
|
SYNCTEX_REGISTER_HANDLE_TO(node); |
|
} |
|
return node; |
|
} |
|
return NULL; |
|
} |
|
|
|
static void _synctex_free_input(synctex_node_p node){ |
|
if (node) { |
|
SYNCTEX_SCANNER_REMOVE_HANDLE_TO(node); |
|
SYNCTEX_WILL_FREE(node); |
|
synctex_node_free(__synctex_tree_sibling(node)); |
|
_synctex_free(_synctex_data_name(node)); |
|
_synctex_free(node); |
|
} |
|
} |
|
|
|
/* The sheet is a first level node. |
|
* It has no parent (the owner 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. |
|
*/ |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark sheet. |
|
# endif |
|
/** |
|
* Every node has the same structure, but not the same size. |
|
*/ |
|
|
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(page) |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_scn_sheet_max+synctex_data_p_sheet_max]; |
|
} synctex_node_sheet_s; |
|
|
|
/* sheet node creator */ |
|
|
|
#define DEFINE_synctex_new_scanned_NODE(NAME)\ |
|
static synctex_node_p _synctex_new_##NAME(synctex_scanner_p scanner) {\ |
|
if (scanner) {\ |
|
++SYNCTEX_CUR;\ |
|
synctex_node_p node = _synctex_malloc(sizeof(synctex_node_##NAME##_s));\ |
|
if (node) {\ |
|
node->class = scanner->class+synctex_node_type_##NAME;\ |
|
SYNCTEX_DID_NEW(node); \ |
|
SYNCTEX_IMPLEMENT_CHARINDEX(node,-1);\ |
|
SYNCTEX_REGISTER_HANDLE_TO(node); \ |
|
}\ |
|
return node;\ |
|
}\ |
|
return NULL;\ |
|
} |
|
/* NB: -1 in SYNCTEX_IMPLEMENT_CHARINDEX above because |
|
* the first char of the line has been scanned |
|
*/ |
|
DEFINE_synctex_new_scanned_NODE(sheet) |
|
static void _synctex_log_sheet(synctex_node_p node); |
|
static char * _synctex_abstract_sheet(synctex_node_p node); |
|
static void _synctex_display_sheet(synctex_node_p node); |
|
|
|
static const synctex_tree_model_s synctex_tree_model_sheet = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
-1, /* parent */ |
|
synctex_tree_s_child_idx, /* child */ |
|
-1, /* friend */ |
|
-1, /* last */ |
|
synctex_tree_sc_next_hbox_idx, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
-1, /* target */ |
|
synctex_tree_scn_sheet_max |
|
}; |
|
static const synctex_data_model_s synctex_data_model_sheet = { |
|
-1, /* tag */ |
|
-1, /* line */ |
|
-1, /* column */ |
|
-1, /* h */ |
|
-1, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
synctex_data_sheet_page_idx, /* page */ |
|
synctex_data_p_sheet_max |
|
}; |
|
static synctex_class_s 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 */ |
|
&_synctex_abstract_sheet, /* abstract */ |
|
&synctex_tree_model_sheet, /* tree model */ |
|
&synctex_data_model_sheet, /* data model */ |
|
&synctex_tlcpector_none, /* tlcpector */ |
|
&synctex_inspector_none, /* inspector */ |
|
&synctex_vispector_none, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark form. |
|
# endif |
|
/** |
|
* Every node has the same structure, but not the same size. |
|
*/ |
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_sct_form_max+synctex_data_t_form_max]; |
|
} synctex_node_form_s; |
|
|
|
DEFINE_synctex_new_scanned_NODE(form) |
|
|
|
static char * _synctex_abstract_form(synctex_node_p node); |
|
static void _synctex_display_form(synctex_node_p node); |
|
static void _synctex_log_form(synctex_node_p node); |
|
|
|
static const synctex_tree_model_s synctex_tree_model_form = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
-1, /* parent */ |
|
synctex_tree_s_child_idx, /* child */ |
|
-1, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
synctex_tree_sc_target_idx, /* target */ |
|
synctex_tree_sct_form_max |
|
}; |
|
static const synctex_data_model_s synctex_data_model_form = { |
|
synctex_data_form_tag_idx, /* tag */ |
|
-1, /* line */ |
|
-1, /* column */ |
|
-1, /* h */ |
|
-1, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
synctex_data_t_form_max |
|
}; |
|
static synctex_class_s synctex_class_form = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_form, /* Node type */ |
|
&_synctex_new_form, /* creator */ |
|
&_synctex_free_node, /* destructor */ |
|
&_synctex_log_form, /* log */ |
|
&_synctex_display_form, /* display */ |
|
&_synctex_abstract_form, /* abstract */ |
|
&synctex_tree_model_form, /* tree model */ |
|
&synctex_data_model_form, /* data model */ |
|
&synctex_tlcpector_none, /* tlcpector */ |
|
&synctex_inspector_none, /* inspector */ |
|
&synctex_vispector_none, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark vbox. |
|
# endif |
|
|
|
/* A box node contains navigation and synctex information |
|
* There are different kinds of boxes. |
|
* Only horizontal boxes are treated differently because of their visible size. |
|
*/ |
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spcfl_vbox_max+synctex_data_box_max]; |
|
} synctex_node_vbox_s; |
|
|
|
/* vertical box node creator */ |
|
DEFINE_synctex_new_scanned_NODE(vbox) |
|
|
|
static char * _synctex_abstract_vbox(synctex_node_p node); |
|
static void _synctex_display_vbox(synctex_node_p node); |
|
static void _synctex_log_vbox(synctex_node_p node); |
|
|
|
static const synctex_tree_model_s synctex_tree_model_vbox = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
synctex_tree_sp_child_idx, /* child */ |
|
synctex_tree_spc_friend_idx, /* friend */ |
|
synctex_tree_spcf_last_idx, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
-1, /* target */ |
|
synctex_tree_spcfl_vbox_max |
|
}; |
|
|
|
DEFINE_SYNCTEX_DATA_INT_GETSET(column) |
|
static synctex_status_t _synctex_data_decode_column(synctex_node_p node) { |
|
if (_synctex_data_has_column(node)) { |
|
synctex_is_s is = _synctex_decode_int_opt(node->class->scanner,-1); |
|
if (is.status == SYNCTEX_STATUS_OK) { |
|
_synctex_data_set_column(node,is.integer); |
|
} |
|
return is.status; |
|
} |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(h) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE_v(v) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(width) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(height) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET_DECODE(depth) |
|
|
|
static const synctex_data_model_s synctex_data_model_box = { |
|
synctex_data_tag_idx, /* tag */ |
|
synctex_data_line_idx, /* line */ |
|
synctex_data_column_idx,/* column */ |
|
synctex_data_h_idx, /* h */ |
|
synctex_data_v_idx, /* v */ |
|
synctex_data_width_idx, /* width */ |
|
synctex_data_height_idx,/* height */ |
|
synctex_data_depth_idx, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
synctex_data_box_max |
|
}; |
|
static const synctex_tlcpector_s synctex_tlcpector_default = { |
|
&_synctex_data_tag, /* tag */ |
|
&_synctex_data_line, /* line */ |
|
&_synctex_data_column, /* column */ |
|
}; |
|
static const synctex_inspector_s synctex_inspector_box = { |
|
&_synctex_data_h, |
|
&_synctex_data_v, |
|
&_synctex_data_width, |
|
&_synctex_data_height, |
|
&_synctex_data_depth, |
|
}; |
|
static float __synctex_node_visible_h(synctex_node_p node); |
|
static float __synctex_node_visible_v(synctex_node_p node); |
|
static float __synctex_node_visible_width(synctex_node_p node); |
|
static float __synctex_node_visible_height(synctex_node_p node); |
|
static float __synctex_node_visible_depth(synctex_node_p node); |
|
static synctex_vispector_s synctex_vispector_box = { |
|
&__synctex_node_visible_h, |
|
&__synctex_node_visible_v, |
|
&__synctex_node_visible_width, |
|
&__synctex_node_visible_height, |
|
&__synctex_node_visible_depth, |
|
}; |
|
/* These are static class objects, each scanner will make a copy of them and setup the scanner field. |
|
*/ |
|
static synctex_class_s 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_abstract_vbox, /* abstract */ |
|
&synctex_tree_model_vbox, /* tree model */ |
|
&synctex_data_model_box, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_box, /* inspector */ |
|
&synctex_vispector_box, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark hbox. |
|
# endif |
|
|
|
/* 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. */ |
|
|
|
static const synctex_tree_model_s synctex_tree_model_hbox = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
synctex_tree_sp_child_idx, /* child */ |
|
synctex_tree_spc_friend_idx, /* friend */ |
|
synctex_tree_spcf_last_idx, /* last */ |
|
synctex_tree_spcfl_next_hbox_idx, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
-1, /* target */ |
|
synctex_tree_spcfln_hbox_max |
|
}; |
|
|
|
DEFINE_SYNCTEX_DATA_INT_GETSET(mean_line) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(weight) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(h_V) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(v_V) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(width_V) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(height_V) |
|
DEFINE_SYNCTEX_DATA_INT_GETSET(depth_V) |
|
|
|
/** |
|
* The hbox model. |
|
* It contains V variants of geometrical information. |
|
* It happens that hboxes contain material that is not used to compute |
|
* the bounding box. Some letters may appear out of the box given by TeX. |
|
* In such a situation, the visible bounding box is bigger than the V variant. |
|
* Only hboxes have such variant. It does not make sense for void boxes |
|
* and it is not used here for vboxes. |
|
* - author: JL |
|
*/ |
|
|
|
static const synctex_data_model_s synctex_data_model_hbox = { |
|
synctex_data_tag_idx, /* tag */ |
|
synctex_data_line_idx, /* line */ |
|
synctex_data_column_idx,/* column */ |
|
synctex_data_h_idx, /* h */ |
|
synctex_data_v_idx, /* v */ |
|
synctex_data_width_idx, /* width */ |
|
synctex_data_height_idx,/* height */ |
|
synctex_data_depth_idx, /* depth */ |
|
synctex_data_mean_line_idx, /* mean_line */ |
|
synctex_data_weight_idx, /* weight */ |
|
synctex_data_h_V_idx, /* h_V */ |
|
synctex_data_v_V_idx, /* v_V */ |
|
synctex_data_width_V_idx, /* width_V */ |
|
synctex_data_height_V_idx, /* height_V */ |
|
synctex_data_depth_V_idx, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
synctex_data_hbox_max |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spcfln_hbox_max+synctex_data_hbox_max]; |
|
} synctex_node_hbox_s; |
|
|
|
/* horizontal box node creator */ |
|
DEFINE_synctex_new_scanned_NODE(hbox) |
|
|
|
static void _synctex_log_hbox(synctex_node_p node); |
|
static char * _synctex_abstract_hbox(synctex_node_p node); |
|
static void _synctex_display_hbox(synctex_node_p node); |
|
|
|
static synctex_class_s 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_abstract_hbox, /* abstract */ |
|
&synctex_tree_model_hbox, /* tree model */ |
|
&synctex_data_model_hbox, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_box, /* inspector */ |
|
&synctex_vispector_box, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark void vbox. |
|
# endif |
|
|
|
/* This void box node implementation is either horizontal or vertical |
|
* It does not contain a child field. |
|
*/ |
|
static const synctex_tree_model_s synctex_tree_model_spf = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
-1, /* child */ |
|
synctex_tree_sp_friend_idx, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
-1, /* target */ |
|
synctex_tree_spf_max |
|
}; |
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spf_max+synctex_data_box_max]; |
|
} synctex_node_void_vbox_s; |
|
|
|
/* vertical void box node creator */ |
|
DEFINE_synctex_new_scanned_NODE(void_vbox) |
|
|
|
static void _synctex_log_void_box(synctex_node_p node); |
|
static char * _synctex_abstract_void_vbox(synctex_node_p node); |
|
static void _synctex_display_void_vbox(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_void_vbox = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_void_vbox,/* Node type */ |
|
&_synctex_new_void_vbox, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_void_box, /* log */ |
|
&_synctex_display_void_vbox,/* display */ |
|
&_synctex_abstract_void_vbox,/* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_box, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_box, /* inspector */ |
|
&synctex_vispector_box, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark void hbox. |
|
# endif |
|
|
|
typedef synctex_node_void_vbox_s synctex_node_void_hbox_s; |
|
|
|
/* horizontal void box node creator */ |
|
DEFINE_synctex_new_scanned_NODE(void_hbox) |
|
|
|
static char * _synctex_abstract_void_hbox(synctex_node_p node); |
|
static void _synctex_display_void_hbox(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_void_hbox = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_void_hbox,/* Node type */ |
|
&_synctex_new_void_hbox, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_void_box, /* log */ |
|
&_synctex_display_void_hbox,/* display */ |
|
&_synctex_abstract_void_hbox,/* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_box, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_box, /* inspector */ |
|
&synctex_vispector_box, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark form ref. |
|
# endif |
|
|
|
/* The form ref node. */ |
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spfa_max+synctex_data_ref_thv_max]; |
|
} synctex_node_ref_s; |
|
|
|
/* form ref node creator */ |
|
DEFINE_synctex_new_scanned_NODE(ref) |
|
|
|
static void _synctex_log_ref(synctex_node_p node); |
|
static char * _synctex_abstract_ref(synctex_node_p node); |
|
static void _synctex_display_ref(synctex_node_p node); |
|
|
|
static const synctex_tree_model_s synctex_tree_model_spfa = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
-1, /* child */ |
|
synctex_tree_sp_friend_idx, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
synctex_tree_spf_arg_sibling_idx, /* arg_sibling */ |
|
-1, /* target */ |
|
synctex_tree_spfa_max |
|
}; |
|
static const synctex_data_model_s synctex_data_model_ref = { |
|
synctex_data_tag_idx, /* tag */ |
|
-1, /* line */ |
|
-1, /* column */ |
|
synctex_data_ref_h_idx, /* h */ |
|
synctex_data_ref_v_idx, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
synctex_data_ref_thv_max |
|
}; |
|
static synctex_class_s synctex_class_ref = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_ref, /* Node type */ |
|
&_synctex_new_ref, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_ref, /* log */ |
|
&_synctex_display_ref, /* display */ |
|
&_synctex_abstract_ref, /* abstract */ |
|
&synctex_tree_model_spfa, /* navigator */ |
|
&synctex_data_model_ref, /* data model */ |
|
&synctex_tlcpector_none, /* tlcpector */ |
|
&synctex_inspector_none, /* inspector */ |
|
&synctex_vispector_none, /* vispector */ |
|
}; |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark small node. |
|
# endif |
|
|
|
/* The small nodes correspond to glue, penalty, math and boundary nodes. */ |
|
static const synctex_data_model_s synctex_data_model_tlchv = { |
|
synctex_data_tag_idx, /* tag */ |
|
synctex_data_line_idx, /* line */ |
|
synctex_data_column_idx, /* column */ |
|
synctex_data_h_idx, /* h */ |
|
synctex_data_v_idx, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
synctex_data_tlchv_max |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spf_max+synctex_data_tlchv_max]; |
|
} synctex_node_tlchv_s; |
|
|
|
static void _synctex_log_tlchv_node(synctex_node_p node); |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark math. |
|
# endif |
|
|
|
typedef synctex_node_tlchv_s synctex_node_math_s; |
|
|
|
/* math node creator */ |
|
DEFINE_synctex_new_scanned_NODE(math) |
|
|
|
static char * _synctex_abstract_math(synctex_node_p node); |
|
static void _synctex_display_math(synctex_node_p node); |
|
static synctex_inspector_s synctex_inspector_hv = { |
|
&_synctex_data_h, |
|
&_synctex_data_v, |
|
&_synctex_int_none, |
|
&_synctex_int_none, |
|
&_synctex_int_none, |
|
}; |
|
static synctex_vispector_s synctex_vispector_hv = { |
|
&__synctex_node_visible_h, |
|
&__synctex_node_visible_v, |
|
&_synctex_float_none, |
|
&_synctex_float_none, |
|
&_synctex_float_none, |
|
}; |
|
|
|
static synctex_class_s synctex_class_math = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_math, /* Node type */ |
|
&_synctex_new_math, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_tlchv_node, /* log */ |
|
&_synctex_display_math, /* display */ |
|
&_synctex_abstract_math, /* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_tlchv, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_hv, /* inspector */ |
|
&synctex_vispector_hv, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark kern node. |
|
# endif |
|
|
|
static const synctex_data_model_s synctex_data_model_tlchvw = { |
|
synctex_data_tag_idx, /* tag */ |
|
synctex_data_line_idx, /* line */ |
|
synctex_data_column_idx,/* column */ |
|
synctex_data_h_idx, /* h */ |
|
synctex_data_v_idx, /* v */ |
|
synctex_data_width_idx, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
synctex_data_tlchvw_max |
|
}; |
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spf_max+synctex_data_tlchvw_max]; |
|
} synctex_node_kern_s; |
|
|
|
/* kern node creator */ |
|
DEFINE_synctex_new_scanned_NODE(kern) |
|
|
|
static void _synctex_log_kern_node(synctex_node_p node); |
|
static char * _synctex_abstract_kern(synctex_node_p node); |
|
static void _synctex_display_kern(synctex_node_p node); |
|
|
|
static synctex_inspector_s synctex_inspector_kern = { |
|
&_synctex_data_h, |
|
&_synctex_data_v, |
|
&_synctex_data_width, |
|
&_synctex_int_none, |
|
&_synctex_int_none, |
|
}; |
|
static float __synctex_kern_visible_h(synctex_node_p node); |
|
static float __synctex_kern_visible_width(synctex_node_p node); |
|
static synctex_vispector_s synctex_vispector_kern = { |
|
&__synctex_kern_visible_h, |
|
&__synctex_node_visible_v, |
|
&__synctex_kern_visible_width, |
|
&_synctex_float_none, |
|
&_synctex_float_none, |
|
}; |
|
|
|
static synctex_class_s synctex_class_kern = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_kern, /* Node type */ |
|
&_synctex_new_kern, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_kern_node, /* log */ |
|
&_synctex_display_kern, /* display */ |
|
&_synctex_abstract_kern, /* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_tlchvw, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_kern, /* inspector */ |
|
&synctex_vispector_kern, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark glue. |
|
# endif |
|
|
|
/* glue node creator */ |
|
typedef synctex_node_tlchv_s synctex_node_glue_s; |
|
DEFINE_synctex_new_scanned_NODE(glue) |
|
|
|
static char * _synctex_abstract_glue(synctex_node_p node); |
|
static void _synctex_display_glue(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_glue = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_glue, /* Node type */ |
|
&_synctex_new_glue, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_tlchv_node, /* log */ |
|
&_synctex_display_glue, /* display */ |
|
&_synctex_abstract_glue, /* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_tlchv, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_hv, /* inspector */ |
|
&synctex_vispector_hv, /* vispector */ |
|
}; |
|
|
|
/* The small nodes correspond to glue and boundary nodes. */ |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark rule. |
|
# endif |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spf_max+synctex_data_box_max]; |
|
} synctex_node_rule_s; |
|
|
|
DEFINE_synctex_new_scanned_NODE(rule) |
|
|
|
static void _synctex_log_rule(synctex_node_p node); |
|
static char * _synctex_abstract_rule(synctex_node_p node); |
|
static void _synctex_display_rule(synctex_node_p node); |
|
|
|
static float __synctex_rule_visible_h(synctex_node_p node); |
|
static float __synctex_rule_visible_v(synctex_node_p node); |
|
static float __synctex_rule_visible_width(synctex_node_p node); |
|
static float __synctex_rule_visible_height(synctex_node_p node); |
|
static float __synctex_rule_visible_depth(synctex_node_p node); |
|
static synctex_vispector_s synctex_vispector_rule = { |
|
&__synctex_rule_visible_h, |
|
&__synctex_rule_visible_v, |
|
&__synctex_rule_visible_width, |
|
&__synctex_rule_visible_height, |
|
&__synctex_rule_visible_depth, |
|
}; |
|
|
|
static synctex_class_s synctex_class_rule = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_rule, /* Node type */ |
|
&_synctex_new_rule, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_rule, /* log */ |
|
&_synctex_display_rule, /* display */ |
|
&_synctex_abstract_rule, /* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_box, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_box, /* inspector */ |
|
&synctex_vispector_rule, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark boundary. |
|
# endif |
|
|
|
/* boundary node creator */ |
|
typedef synctex_node_tlchv_s synctex_node_boundary_s; |
|
DEFINE_synctex_new_scanned_NODE(boundary) |
|
|
|
static char * _synctex_abstract_boundary(synctex_node_p node); |
|
static void _synctex_display_boundary(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_boundary = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_boundary, /* Node type */ |
|
&_synctex_new_boundary, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_tlchv_node, /* log */ |
|
&_synctex_display_boundary, /* display */ |
|
&_synctex_abstract_boundary,/* abstract */ |
|
&synctex_tree_model_spf, /* tree model */ |
|
&synctex_data_model_tlchv, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_hv, /* inspector */ |
|
&synctex_vispector_hv, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark box boundary. |
|
# endif |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spfa_max+synctex_data_tlchv_max]; |
|
} synctex_node_box_bdry_s; |
|
|
|
#define DEFINE_synctex_new_unscanned_NODE(NAME)\ |
|
SYNCTEX_INLINE static synctex_node_p _synctex_new_##NAME(synctex_scanner_p scanner) {\ |
|
if (scanner) {\ |
|
synctex_node_p node = _synctex_malloc(sizeof(synctex_node_##NAME##_s));\ |
|
if (node) {\ |
|
node->class = scanner->class+synctex_node_type_##NAME;\ |
|
SYNCTEX_DID_NEW(node); \ |
|
}\ |
|
return node;\ |
|
}\ |
|
return NULL;\ |
|
} |
|
DEFINE_synctex_new_unscanned_NODE(box_bdry) |
|
|
|
static char * _synctex_abstract_box_bdry(synctex_node_p node); |
|
static void _synctex_display_box_bdry(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_box_bdry = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_box_bdry, /* Node type */ |
|
&_synctex_new_box_bdry, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_tlchv_node, /* log */ |
|
&_synctex_display_box_bdry, /* display */ |
|
&_synctex_abstract_box_bdry,/* display */ |
|
&synctex_tree_model_spfa, /* tree model */ |
|
&synctex_data_model_tlchv, /* data model */ |
|
&synctex_tlcpector_default, /* tlcpector */ |
|
&synctex_inspector_hv, /* inspector */ |
|
&synctex_vispector_hv, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark hbox proxy. |
|
# endif |
|
|
|
/** |
|
* Standard nodes refer to TeX nodes: math, kern, boxes... |
|
* Proxy nodes are used to support forms. |
|
* A form is parsed as a tree of standard nodes starting |
|
* at the top left position. |
|
* When a reference is used, the form is duplicated |
|
* to the location specified by the reference. |
|
* As the same form can be duplicated at different locations, |
|
* the geometrical information is relative to its own top left point. |
|
* As we need absolute locations, we use proxy nodes. |
|
* A proxy node records an offset and the target node. |
|
* The target partly acts as a delegate. |
|
* The h and v position of the proxy node is the h and v |
|
* position of the target shifted by the proxy's offset. |
|
* The width, height and depth are not sensitive to offsets. |
|
* When are proxies created ? |
|
* 1) when the synctex file has been parsed, all the form refs |
|
* are replaced by proxies to the content of a form. |
|
* This content is a node with siblings (actually none). |
|
* Those root proxies have the parent of the ref they replace, |
|
* so their parents exist and are no proxy. |
|
* Moreover, if they have no sibling, it means that their target have no |
|
* sibling as well. |
|
* Such nodes are called root proxies. |
|
* 2) On the fly, when a proxy is asked for its child |
|
* (or sibling) and has none, a proxy to its target's child |
|
* (or sibling) is created if any. There are only 2 possible situations: |
|
* either the newly created proxy is the child of a proxy, |
|
* or it is the sibling of a proxy created on the fly. |
|
* In both cases, the parent is a proxy with children. |
|
* Such nodes are called child proxies. |
|
* How to compute the offset of a proxy ? |
|
* The offset of root proxy objects is exactly |
|
* the offset of the ref they replace. |
|
* The offset of other proxies is their owner's, |
|
* except when pointing to a root proxy. |
|
* What happens for cascading forms ? |
|
* Here is an example diagram |
|
* |
|
* At parse time, the arrow means "owns": |
|
* sheet0 -> ref_to1 |
|
* |
|
* target1 -> ref_to2 |
|
* |
|
* target2 -> child22 |
|
* |
|
* After replacing the refs: |
|
* sheet0 -> proxy00 -> proxy01 -> proxy02 |
|
* | | | |
|
* target1 -> proxy11 -> proxy12 |
|
* | | |
|
* target2 -> proxy22 |
|
* |
|
* proxy00, proxy11 and proxy22 are root proxies. |
|
* Their offset is the one of the ref they replace |
|
* proxy01, proxy02 and proxy12 are child proxies. |
|
* Their proxy is the one of their parent. |
|
* Optimization. |
|
* After all the refs are replaced, there are only root nodes |
|
* targeting standard node. We make sure that each child proxy |
|
* also targets a standard node. |
|
* It is possible for a proxy to have a standard sibling |
|
* whereas its target has no sibling at all. Root proxies |
|
* are such nodes, and are the only ones. |
|
* The consequence is that proxies created on the fly |
|
* must take into account this situation. |
|
*/ |
|
|
|
/* A proxy to a hbox. |
|
* A proxy do have a target, which can be a proxy |
|
*/ |
|
|
|
static const synctex_tree_model_s synctex_tree_model_proxy_hbox = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
synctex_tree_sp_child_idx, /* child */ |
|
synctex_tree_spc_friend_idx, /* friend */ |
|
synctex_tree_spcf_last_idx, /* last */ |
|
synctex_tree_spcfl_next_hbox_idx, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
synctex_tree_spcfln_target_idx, /* target */ |
|
synctex_tree_spcflnt_proxy_hbox_max |
|
}; |
|
static const synctex_data_model_s synctex_data_model_proxy = { |
|
-1, /* tag */ |
|
-1, /* line */ |
|
-1, /* column */ |
|
synctex_data_proxy_h_idx, /* h */ |
|
synctex_data_proxy_v_idx, /* v */ |
|
-1, /* width */ |
|
-1, /* height */ |
|
-1, /* depth */ |
|
-1, /* mean_line */ |
|
-1, /* weight */ |
|
-1, /* h_V */ |
|
-1, /* v_V */ |
|
-1, /* width_V */ |
|
-1, /* height_V */ |
|
-1, /* depth_V */ |
|
-1, /* name */ |
|
-1, /* page */ |
|
synctex_data_proxy_hv_max |
|
}; |
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spcflnt_proxy_hbox_max+synctex_data_proxy_hv_max]; |
|
} synctex_node_proxy_hbox_s; |
|
|
|
/* box proxy node creator */ |
|
DEFINE_synctex_new_unscanned_NODE(proxy_hbox) |
|
|
|
static void _synctex_log_proxy(synctex_node_p node); |
|
static char * _synctex_abstract_proxy_hbox(synctex_node_p node); |
|
static void _synctex_display_proxy_hbox(synctex_node_p node); |
|
|
|
static int _synctex_proxy_tag(synctex_node_p); |
|
static int _synctex_proxy_line(synctex_node_p); |
|
static int _synctex_proxy_column(synctex_node_p); |
|
|
|
static synctex_tlcpector_s synctex_tlcpector_proxy = { |
|
&_synctex_proxy_tag, |
|
&_synctex_proxy_line, |
|
&_synctex_proxy_column, |
|
}; |
|
static int _synctex_proxy_h(synctex_node_p); |
|
static int _synctex_proxy_v(synctex_node_p); |
|
static int _synctex_proxy_width(synctex_node_p); |
|
static int _synctex_proxy_height(synctex_node_p); |
|
static int _synctex_proxy_depth(synctex_node_p); |
|
static synctex_inspector_s synctex_inspector_proxy_box = { |
|
&_synctex_proxy_h, |
|
&_synctex_proxy_v, |
|
&_synctex_proxy_width, |
|
&_synctex_proxy_height, |
|
&_synctex_proxy_depth, |
|
}; |
|
|
|
static float __synctex_proxy_visible_h(synctex_node_p); |
|
static float __synctex_proxy_visible_v(synctex_node_p); |
|
static float __synctex_proxy_visible_width(synctex_node_p); |
|
static float __synctex_proxy_visible_height(synctex_node_p); |
|
static float __synctex_proxy_visible_depth(synctex_node_p); |
|
|
|
static synctex_vispector_s synctex_vispector_proxy_box = { |
|
&__synctex_proxy_visible_h, |
|
&__synctex_proxy_visible_v, |
|
&__synctex_proxy_visible_width, |
|
&__synctex_proxy_visible_height, |
|
&__synctex_proxy_visible_depth, |
|
}; |
|
|
|
static synctex_class_s synctex_class_proxy_hbox = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_proxy_hbox, /* Node type */ |
|
&_synctex_new_proxy_hbox, /* creator */ |
|
&_synctex_free_node, /* destructor */ |
|
&_synctex_log_proxy, /* log */ |
|
&_synctex_display_proxy_hbox, /* display */ |
|
&_synctex_abstract_proxy_hbox, /* abstract */ |
|
&synctex_tree_model_proxy_hbox, /* tree model */ |
|
&synctex_data_model_proxy, /* data model */ |
|
&synctex_tlcpector_proxy, /* tlcpector */ |
|
&synctex_inspector_proxy_box, /* inspector */ |
|
&synctex_vispector_proxy_box, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark vbox proxy. |
|
# endif |
|
|
|
/* A proxy to a vbox. */ |
|
|
|
static const synctex_tree_model_s synctex_tree_model_proxy_vbox = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
synctex_tree_sp_child_idx, /* child */ |
|
synctex_tree_spc_friend_idx, /* friend */ |
|
synctex_tree_spcf_last_idx, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
synctex_tree_spcfl_target_idx, /* target */ |
|
synctex_tree_spcflt_proxy_vbox_max |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spcflt_proxy_vbox_max+synctex_data_proxy_hv_max]; |
|
} synctex_node_proxy_vbox_s; |
|
|
|
/* box proxy node creator */ |
|
DEFINE_synctex_new_unscanned_NODE(proxy_vbox) |
|
|
|
static void _synctex_log_proxy(synctex_node_p node); |
|
static char * _synctex_abstract_proxy_vbox(synctex_node_p node); |
|
static void _synctex_display_proxy_vbox(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_proxy_vbox = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_proxy_vbox, /* Node type */ |
|
&_synctex_new_proxy_vbox, /* creator */ |
|
&_synctex_free_node, /* destructor */ |
|
&_synctex_log_proxy, /* log */ |
|
&_synctex_display_proxy_vbox, /* display */ |
|
&_synctex_abstract_proxy_vbox, /* abstract */ |
|
&synctex_tree_model_proxy_vbox, /* tree model */ |
|
&synctex_data_model_proxy, /* data model */ |
|
&synctex_tlcpector_proxy, /* tlcpector */ |
|
&synctex_inspector_proxy_box, /* inspector */ |
|
&synctex_vispector_proxy_box, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark proxy. |
|
# endif |
|
|
|
/** |
|
* A proxy to a node but a box. |
|
*/ |
|
|
|
static const synctex_tree_model_s synctex_tree_model_proxy = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
-1, /* child */ |
|
synctex_tree_sp_friend_idx, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
synctex_tree_spf_target_idx,/* target */ |
|
synctex_tree_spft_proxy_max |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spft_proxy_max+synctex_data_proxy_hv_max]; |
|
} synctex_node_proxy_s; |
|
|
|
/* proxy node creator */ |
|
DEFINE_synctex_new_unscanned_NODE(proxy) |
|
|
|
static void _synctex_log_proxy(synctex_node_p node); |
|
static char * _synctex_abstract_proxy(synctex_node_p node); |
|
static void _synctex_display_proxy(synctex_node_p node); |
|
|
|
static synctex_vispector_s synctex_vispector_proxy = { |
|
&__synctex_proxy_visible_h, |
|
&__synctex_proxy_visible_v, |
|
&__synctex_proxy_visible_width, |
|
&_synctex_float_none, |
|
&_synctex_float_none, |
|
}; |
|
|
|
static synctex_class_s synctex_class_proxy = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_proxy, /* Node type */ |
|
&_synctex_new_proxy, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_proxy, /* log */ |
|
&_synctex_display_proxy, /* display */ |
|
&_synctex_abstract_proxy, /* abstract */ |
|
&synctex_tree_model_proxy, /* tree model */ |
|
&synctex_data_model_proxy, /* data model */ |
|
&synctex_tlcpector_proxy, /* tlcpector */ |
|
&synctex_inspector_proxy_box, /* inspector */ |
|
&synctex_vispector_proxy, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark last proxy. |
|
# endif |
|
|
|
/** |
|
* A proxy to the last proxy/box boundary. |
|
*/ |
|
|
|
static const synctex_tree_model_s synctex_tree_model_proxy_last = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
-1, /* child */ |
|
synctex_tree_sp_friend_idx, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
synctex_tree_spf_arg_sibling_idx, /* arg_sibling */ |
|
synctex_tree_spfa_target_idx, /* target */ |
|
synctex_tree_spfat_proxy_last_max |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spfat_proxy_last_max+synctex_data_proxy_hv_max]; |
|
} synctex_node_proxy_last_s; |
|
|
|
/* proxy node creator */ |
|
DEFINE_synctex_new_unscanned_NODE(proxy_last) |
|
|
|
static void _synctex_log_proxy(synctex_node_p node); |
|
static char * _synctex_abstract_proxy(synctex_node_p node); |
|
static void _synctex_display_proxy(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_proxy_last = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_proxy_last, /* Node type */ |
|
&_synctex_new_proxy, /* creator */ |
|
&_synctex_free_leaf, /* destructor */ |
|
&_synctex_log_proxy, /* log */ |
|
&_synctex_display_proxy, /* display */ |
|
&_synctex_abstract_proxy, /* abstract */ |
|
&synctex_tree_model_proxy_last, /* tree model */ |
|
&synctex_data_model_proxy, /* data model */ |
|
&synctex_tlcpector_proxy, /* tlcpector */ |
|
&synctex_inspector_proxy_box, /* inspector */ |
|
&synctex_vispector_proxy, /* vispector */ |
|
}; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark handle. |
|
# endif |
|
|
|
/** |
|
* A result node. |
|
*/ |
|
|
|
static const synctex_tree_model_s synctex_tree_model_handle = { |
|
synctex_tree_sibling_idx, /* sibling */ |
|
synctex_tree_s_parent_idx, /* parent */ |
|
synctex_tree_sp_child_idx, /* child */ |
|
-1, /* friend */ |
|
-1, /* last */ |
|
-1, /* next_hbox */ |
|
-1, /* arg_sibling */ |
|
synctex_tree_spc_target_idx,/* target */ |
|
synctex_tree_spct_handle_max |
|
}; |
|
|
|
typedef struct { |
|
SYNCTEX_DECLARE_CHARINDEX |
|
synctex_class_p class; |
|
synctex_data_u data[synctex_tree_spct_handle_max+0]; |
|
} synctex_node_handle_s; |
|
|
|
/* result node creator */ |
|
DEFINE_synctex_new_unscanned_NODE(handle) |
|
|
|
static void _synctex_log_handle(synctex_node_p node); |
|
static char * _synctex_abstract_handle(synctex_node_p node); |
|
static void _synctex_display_handle(synctex_node_p node); |
|
|
|
static synctex_class_s synctex_class_handle = { |
|
NULL, /* No scanner yet */ |
|
synctex_node_type_handle, /* Node type */ |
|
&_synctex_new_handle, /* creator */ |
|
&_synctex_free_node, /* destructor */ |
|
&_synctex_log_handle, /* log */ |
|
&_synctex_display_handle, /* display */ |
|
&_synctex_abstract_handle, /* abstract */ |
|
&synctex_tree_model_handle, /* tree model */ |
|
&synctex_data_model_none, /* data model */ |
|
&synctex_tlcpector_proxy, /* tlcpector */ |
|
&synctex_inspector_proxy_box, /* inspector */ |
|
&synctex_vispector_proxy_box, /* vispector */ |
|
}; |
|
|
|
SYNCTEX_INLINE static synctex_node_p _synctex_new_handle_with_target(synctex_node_p target) { |
|
if (target) { |
|
synctex_node_p result = _synctex_new_handle(target->class->scanner); |
|
if (result) { |
|
_synctex_tree_set_target(result,target); |
|
return result; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Navigation |
|
# endif |
|
synctex_node_p synctex_node_parent(synctex_node_p node) |
|
{ |
|
return _synctex_tree_parent(node); |
|
} |
|
synctex_node_p synctex_node_parent_sheet(synctex_node_p node) |
|
{ |
|
while(node && synctex_node_type(node) != synctex_node_type_sheet) { |
|
node = _synctex_tree_parent(node); |
|
} |
|
/* exit the while loop either when node is NULL or node is a sheet */ |
|
return node; |
|
} |
|
synctex_node_p synctex_node_parent_form(synctex_node_p node) |
|
{ |
|
while(node && synctex_node_type(node) != synctex_node_type_form) { |
|
node = _synctex_tree_parent(node); |
|
} |
|
/* exit the while loop either when node is NULL or node is a form */ |
|
return node; |
|
} |
|
|
|
/** |
|
* The returned proxy will be the child or a sibling of source. |
|
* The returned proxy has no parent, child nor sibling. |
|
* Used only by __synctex_replace_ref. |
|
* argument to_node: a box, not a proxy nor anything else. |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_new_proxy_from_ref_to(synctex_node_p ref, synctex_node_p to_node) { |
|
synctex_node_p proxy = NULL; |
|
if (!ref || !to_node) { |
|
return NULL; |
|
} |
|
switch(synctex_node_type(to_node)) { |
|
case synctex_node_type_vbox: |
|
proxy = _synctex_new_proxy_vbox(ref->class->scanner); |
|
break; |
|
case synctex_node_type_hbox: |
|
proxy = _synctex_new_proxy_hbox(ref->class->scanner); |
|
break; |
|
default: |
|
_synctex_error("! __synctex_new_proxy_from_ref_to. Unexpected form child (%s). Please report.", synctex_node_isa(to_node)); |
|
return NULL; |
|
} |
|
if (!proxy) { |
|
_synctex_error("! __synctex_new_proxy_from_ref_to. Internal error. Please report."); |
|
return NULL; |
|
} |
|
_synctex_data_set_h(proxy, _synctex_data_h(ref)); |
|
_synctex_data_set_v(proxy, _synctex_data_v(ref)); |
|
_synctex_tree_set_target(proxy,to_node); |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
proxy->line_index=to_node?to_node->line_index:0; |
|
proxy->char_index=to_node?to_node->char_index:0; |
|
# endif |
|
return proxy; |
|
} |
|
/** |
|
* The returned proxy will be the child or a sibling of owning_proxy. |
|
* The returned proxy has no parent, nor child. |
|
* Used only by synctex_node_child and synctex_node_sibling |
|
* to create proxies on the fly. |
|
* If the to_node has an already computed sibling, |
|
* then the returned proxy has itself a sibling |
|
* pointing to that already computed sibling. |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_new_child_proxy_to(synctex_node_p owner, synctex_node_p to_node) { |
|
synctex_node_p proxy = NULL; |
|
synctex_node_p target = to_node; |
|
if (!owner) { |
|
return NULL; |
|
} |
|
switch(synctex_node_type(target)) { |
|
case synctex_node_type_vbox: |
|
if ((proxy = _synctex_new_proxy_vbox(owner->class->scanner))) { |
|
exit_standard: |
|
_synctex_data_set_h(proxy, _synctex_data_h(owner)); |
|
_synctex_data_set_v(proxy, _synctex_data_v(owner)); |
|
exit0: |
|
_synctex_tree_set_target(proxy,target); |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
proxy->line_index=to_node?to_node->line_index:0; |
|
proxy->char_index=to_node?to_node->char_index:0; |
|
# endif |
|
return proxy; |
|
}; |
|
break; |
|
case synctex_node_type_proxy_vbox: |
|
if ((proxy = _synctex_new_proxy_vbox(owner->class->scanner))) { |
|
exit_proxy: |
|
target = _synctex_tree_target(to_node); |
|
_synctex_data_set_h(proxy, _synctex_data_h(owner)+_synctex_data_h(to_node)); |
|
_synctex_data_set_v(proxy, _synctex_data_v(owner)+_synctex_data_v(to_node)); |
|
goto exit0; |
|
}; |
|
break; |
|
case synctex_node_type_hbox: |
|
if ((proxy = _synctex_new_proxy_hbox(owner->class->scanner))) { |
|
goto exit_standard; |
|
}; |
|
break; |
|
case synctex_node_type_proxy_hbox: |
|
if ((proxy = _synctex_new_proxy_hbox(owner->class->scanner))) { |
|
goto exit_proxy; |
|
}; |
|
break; |
|
case synctex_node_type_proxy: |
|
case synctex_node_type_proxy_last: |
|
if ((proxy = _synctex_new_proxy(owner->class->scanner))) { |
|
goto exit_proxy; |
|
}; |
|
break; |
|
default: |
|
if ((proxy = _synctex_new_proxy(owner->class->scanner))) { |
|
goto exit_standard; |
|
}; |
|
break; |
|
} |
|
_synctex_error("! __synctex_new_child_proxy_to. " |
|
"Internal error. " |
|
"Please report."); |
|
return NULL; |
|
} |
|
SYNCTEX_INLINE static synctex_node_p _synctex_tree_set_sibling(synctex_node_p node, synctex_node_p new_sibling); |
|
typedef struct synctex_nns_t { |
|
synctex_node_p first; |
|
synctex_node_p last; |
|
synctex_status_t status; |
|
} synctex_nns_s; |
|
/** |
|
* Given a target node, create a list of proxies. |
|
* The first proxy points to the target node, |
|
* its sibling points to the target's sibling and so on. |
|
* Returns the first created proxy, the last one and |
|
* an error status. |
|
*/ |
|
SYNCTEX_INLINE static synctex_nns_s _synctex_new_child_proxies_to(synctex_node_p owner, synctex_node_p to_node) { |
|
synctex_nns_s nns = {NULL,NULL,SYNCTEX_STATUS_OK}; |
|
if ((nns.first = nns.last = __synctex_new_child_proxy_to(owner,to_node))) { |
|
synctex_node_p to_next_sibling = __synctex_tree_sibling(to_node); |
|
synctex_node_p to_sibling; |
|
while ((to_sibling = to_next_sibling)) { |
|
synctex_node_p sibling; |
|
if ((to_next_sibling = __synctex_tree_sibling(to_sibling))) { |
|
/* This is not the last sibling */ |
|
if((sibling = __synctex_new_child_proxy_to(owner,to_sibling))) { |
|
_synctex_tree_set_sibling(nns.last,sibling); |
|
nns.last = sibling; |
|
continue; |
|
} else { |
|
_synctex_error("! _synctex_new_child_proxy_to. " |
|
"Internal error (1). " |
|
"Please report."); |
|
nns.status = SYNCTEX_STATUS_ERROR; |
|
} |
|
} else if((sibling = _synctex_new_proxy_last(owner->class->scanner))) { |
|
_synctex_tree_set_sibling(nns.last,sibling); |
|
nns.last = sibling; |
|
_synctex_data_set_h(nns.last, _synctex_data_h(nns.first)); |
|
_synctex_data_set_v(nns.last, _synctex_data_v(nns.first)); |
|
_synctex_tree_set_target(nns.last,to_sibling); |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
nns.last->line_index=to_sibling->line_index; |
|
nns.last->char_index=to_sibling->char_index; |
|
# endif |
|
} else { |
|
_synctex_error("! _synctex_new_child_proxy_to. " |
|
"Internal error (2). " |
|
"Please report."); |
|
nns.status = SYNCTEX_STATUS_ERROR; |
|
} |
|
break; |
|
} |
|
} |
|
return nns; |
|
} |
|
static char * _synctex_node_abstract(synctex_node_p node); |
|
SYNCTEX_INLINE static synctex_node_p synctex_tree_set_friend(synctex_node_p node,synctex_node_p new_friend) { |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG |
|
synctex_node_p F = new_friend; |
|
while (F) { |
|
if (node == F) { |
|
printf("THIS IS AN ERROR\n"); |
|
F = new_friend; |
|
while (F) { |
|
printf("%s\n",_synctex_node_abstract(F)); |
|
if (node == F) { |
|
return NULL; |
|
} |
|
F = _synctex_tree_friend(F); |
|
} |
|
return NULL; |
|
} |
|
F = _synctex_tree_friend(F); |
|
} |
|
#endif |
|
return new_friend?_synctex_tree_set_friend(node,new_friend):_synctex_tree_reset_friend(node); |
|
} |
|
/** |
|
* |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_node_make_friend(synctex_node_p node, int i) { |
|
synctex_node_p old = NULL; |
|
if (i>=0) { |
|
i = i%(node->class->scanner->number_of_lists); |
|
old = synctex_tree_set_friend(node,(node->class->scanner->lists_of_friends)[i]); |
|
(node->class->scanner->lists_of_friends)[i] = node; |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("tl(%i)=>",i); |
|
synctex_node_log(node); |
|
if (synctex_node_parent_form(node)) { |
|
printf("! ERROR. No registration expected!\n"); |
|
} |
|
#endif |
|
} |
|
return old; |
|
} |
|
/** |
|
* All proxies have tlc attributes, on behalf of their target. |
|
* The purpose is to register all af them. |
|
* - argument node: is the proxy, must not be NULL |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_proxy_make_friend_and_next_hbox(synctex_node_p node) { |
|
synctex_node_p old = NULL; |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
int i = _synctex_data_tag(target)+_synctex_data_line(target); |
|
old = __synctex_node_make_friend(node,i); |
|
} else { |
|
old = __synctex_tree_reset_friend(node); |
|
} |
|
if (synctex_node_type(node) == synctex_node_type_proxy_hbox) { |
|
synctex_node_p sheet = synctex_node_parent_sheet(node); |
|
if (sheet) { |
|
_synctex_tree_set_next_hbox(node,_synctex_tree_next_hbox(sheet)); |
|
_synctex_tree_set_next_hbox(sheet,node); |
|
} |
|
} |
|
return old; |
|
} |
|
/** |
|
* Register a node which have tag, line and column. |
|
* - argument node: the node |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p __synctex_node_make_friend_tlc(synctex_node_p node) { |
|
int i = synctex_node_tag(node)+synctex_node_line(node); |
|
return __synctex_node_make_friend(node,i); |
|
} |
|
/** |
|
* Register a node which have tag, line and column. |
|
* Does nothing if the argument is NULL. |
|
* Calls __synctex_node_make_friend_tlc. |
|
* - argument node: the node |
|
*/ |
|
SYNCTEX_INLINE static void _synctex_node_make_friend_tlc(synctex_node_p node) { |
|
if (node) { |
|
__synctex_node_make_friend_tlc(node); |
|
} |
|
} |
|
static synctex_node_p _synctex_node_set_child(synctex_node_p node, synctex_node_p new_child); |
|
/** |
|
* The (first) child of the node, if any, NULL otherwise. |
|
* At parse time, non void box nodes have children. |
|
* All other nodes have no children. |
|
* In order to support pdf forms, proxies are created |
|
* to place form nodes at real locations. |
|
* Ref nodes are replaced by root proxies targeting |
|
* form contents. If root proxies have no children, |
|
* they are created on the fly as proxies to the |
|
* children of the targeted box. |
|
* As such, proxies created here are targeting a |
|
* node that belongs to a form. |
|
* This is the only place where child proxies are created. |
|
*/ |
|
synctex_node_p synctex_node_child(synctex_node_p node) { |
|
synctex_node_p child = NULL; |
|
synctex_node_p target = NULL; |
|
if ((child = _synctex_tree_child(node))) { |
|
return child; |
|
} else if ((target = _synctex_tree_target(node))) { |
|
if ((child = synctex_node_child(target))) { |
|
/* This is a proxy with no child |
|
* which target does have a child. */ |
|
synctex_nns_s nns = _synctex_new_child_proxies_to(node, child); |
|
if (nns.first) { |
|
_synctex_node_set_child(node,nns.first); |
|
return nns.first; |
|
} else { |
|
_synctex_error("! synctex_node_child. Internal inconsistency. Please report."); |
|
} |
|
} |
|
} |
|
return NULL; |
|
} |
|
/* |
|
* Set the parent/child bound. |
|
* Things get complicated when new_child has siblings. |
|
* The caller is responsible for releasing the returned value. |
|
*/ |
|
static synctex_node_p _synctex_node_set_child(synctex_node_p parent, synctex_node_p new_child) { |
|
if (parent) { |
|
synctex_node_p old = _synctex_tree_set_child(parent,new_child); |
|
synctex_node_p last_child = NULL; |
|
synctex_node_p child; |
|
if ((child = old)) { |
|
do { |
|
_synctex_tree_reset_parent(child); |
|
} while ((child = __synctex_tree_sibling(child))); |
|
} |
|
if ((child = new_child)) { |
|
do { |
|
_synctex_tree_set_parent(child,parent); |
|
last_child = child; |
|
} while ((child = __synctex_tree_sibling(child))); |
|
} |
|
_synctex_tree_set_last(parent,last_child); |
|
return old; |
|
} |
|
return NULL; |
|
} |
|
|
|
/* The last child of the given node, or NULL. |
|
*/ |
|
synctex_node_p synctex_node_last_child(synctex_node_p node) { |
|
return _synctex_tree_last(node); |
|
} |
|
/** |
|
* All nodes siblings are properly set up at parse time |
|
* except for non root proxies. |
|
*/ |
|
synctex_node_p synctex_node_sibling(synctex_node_p node) { |
|
return node? __synctex_tree_sibling(node): NULL; |
|
} |
|
/** |
|
* All the _synctex_tree_... methods refer to the tree model. |
|
* __synctex_tree_... methods are low level. |
|
*/ |
|
/** |
|
* Replace the sibling. |
|
* Connect to the arg_sibling of the new_sibling if relevant. |
|
* - returns the old sibling. |
|
* The caller is responsible for releasing the old sibling. |
|
* The bound to the parent is managed below. |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p _synctex_tree_set_sibling(synctex_node_p node, synctex_node_p new_sibling) { |
|
if (node == new_sibling) { |
|
printf("BOF\n"); |
|
} |
|
synctex_node_p old = node? __synctex_tree_set_sibling(node,new_sibling): NULL; |
|
_synctex_tree_set_arg_sibling(new_sibling,node); |
|
return old; |
|
} |
|
/** |
|
* Replace the sibling. |
|
* Set the parent of the new sibling (and further siblings) |
|
* to the parent of the receiver. |
|
* Also set the last sibling of parent. |
|
* - argument new_sibling: must not be NULL. |
|
* - returns the old sibling. |
|
* The caller is responsible for releasing the old sibling. |
|
*/ |
|
static synctex_node_p _synctex_node_set_sibling(synctex_node_p node, synctex_node_p new_sibling) { |
|
if (node && new_sibling) { |
|
synctex_node_p old = _synctex_tree_set_sibling(node,new_sibling); |
|
if (_synctex_tree_has_parent(node)) { |
|
synctex_node_p parent = __synctex_tree_parent(node); |
|
if (parent) { |
|
synctex_node_p N = new_sibling; |
|
while (synctex_YES) { |
|
if (_synctex_tree_has_parent(N)) { |
|
__synctex_tree_set_parent(N,parent); |
|
_synctex_tree_set_last(parent,N); |
|
N = __synctex_tree_sibling(N); |
|
continue; |
|
} else if (N) { |
|
_synctex_error("! synctex_node_sibling. " |
|
"Internal inconsistency. " |
|
"Please report."); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
return old; |
|
} |
|
return NULL; |
|
} |
|
/** |
|
* The last sibling of the given node, or NULL with node. |
|
*/ |
|
synctex_node_p synctex_node_last_sibling(synctex_node_p node) { |
|
synctex_node_p sibling; |
|
do { |
|
sibling = node; |
|
} while((node = synctex_node_sibling(node))); |
|
return sibling; |
|
} |
|
/** |
|
* The next nodes corresponds to a deep first tree traversal. |
|
* Does not create child proxies as side effect contrary to |
|
* the synctex_node_next method above. |
|
* May loop infinitely many times if the tree |
|
* is not properly built (contains loops). |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p _synctex_node_sibling_or_parents(synctex_node_p node) { |
|
while (node) { |
|
synctex_node_p N; |
|
if ((N = __synctex_tree_sibling(node))) { |
|
return N; |
|
} else if ((node = _synctex_tree_parent(node))) { |
|
if (synctex_node_type(node) == synctex_node_type_sheet) {/* EXC_BAD_ACCESS? */ |
|
return NULL; |
|
} else if (synctex_node_type(node) == synctex_node_type_form) { |
|
return NULL; |
|
} |
|
} else { |
|
return NULL; |
|
} |
|
} |
|
return NULL; |
|
} |
|
/** |
|
* The next nodes corresponds to a deep first tree traversal. |
|
* Creates child proxies as side effect. |
|
* May loop infinitely many times if the tree |
|
* is not properly built (contains loops). |
|
*/ |
|
synctex_node_p synctex_node_next(synctex_node_p node) { |
|
synctex_node_p N = synctex_node_child(node); |
|
if (N) { |
|
return N; |
|
} |
|
return _synctex_node_sibling_or_parents(node); |
|
} |
|
/** |
|
* The next nodes corresponds to a deep first tree traversal. |
|
* Does not create child proxies as side effect contrary to |
|
* the synctex_node_next method above. |
|
* May loop infinitely many times if the tree |
|
* is not properly built (contains loops). |
|
*/ |
|
synctex_node_p _synctex_node_next(synctex_node_p node) { |
|
synctex_node_p N = _synctex_tree_child(node); |
|
if (N) { |
|
return N; |
|
} |
|
return _synctex_node_sibling_or_parents(node); |
|
} |
|
/** |
|
* The node which argument is the sibling. |
|
* - return: NULL if the argument has no parent or |
|
* is the first child of its parent. |
|
* - Input nodes have no arg siblings |
|
*/ |
|
synctex_node_p synctex_node_arg_sibling(synctex_node_p node) { |
|
#if 1 |
|
return _synctex_tree_arg_sibling(node); |
|
#else |
|
synctex_node_p N = _synctex_tree_parent(node); |
|
if ((N = _synctex_tree_child(N))) { |
|
do { |
|
synctex_node_p NN = __synctex_tree_sibling(N); |
|
if (NN == node) { |
|
return N; |
|
} |
|
N = NN; |
|
} while (N); |
|
} |
|
return N; |
|
#endif |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark CLASS |
|
# endif |
|
|
|
/* Public node accessor: the type */ |
|
synctex_node_type_t synctex_node_type(synctex_node_p node) { |
|
return node? node->class->type: synctex_node_type_none; |
|
} |
|
|
|
/* Public node accessor: the type */ |
|
synctex_node_type_t synctex_node_target_type(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
return (((target)->class))->type; |
|
} else if (node) { |
|
return (((node)->class))->type; |
|
} |
|
return synctex_node_type_none; |
|
} |
|
|
|
/* Public node accessor: the human readable type */ |
|
const char * synctex_node_isa(synctex_node_p node) { |
|
static const char * isa[synctex_node_number_of_types] = |
|
{"Not a node", |
|
"input", |
|
"sheet", |
|
"form", |
|
"ref", |
|
"vbox", |
|
"void vbox", |
|
"hbox", |
|
"void hbox", |
|
"kern", |
|
"glue", |
|
"rule", |
|
"math", |
|
"boundary", |
|
"box_bdry", |
|
"proxy", |
|
"last proxy", |
|
"vbox proxy", |
|
"hbox proxy", |
|
"handle"}; |
|
return isa[synctex_node_type(node)]; |
|
} |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark LOG |
|
# endif |
|
|
|
/* Public node logger */ |
|
void synctex_node_log(synctex_node_p node) { |
|
SYNCTEX_MSG_SEND(node,log); |
|
} |
|
|
|
static void _synctex_log_input(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i,%s(%i)\n",synctex_node_isa(node), |
|
_synctex_data_tag(node), |
|
_synctex_data_name(node), |
|
_synctex_data_line(node)); |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n", |
|
(void *)__synctex_tree_sibling(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_sheet(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i",synctex_node_isa(node),_synctex_data_page(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
printf(" NEXT_hbox:%p\n",(void *)_synctex_tree_next_hbox(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_form(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i",synctex_node_isa(node),_synctex_data_tag(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_ref(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i:%i,%i", |
|
synctex_node_isa(node), |
|
_synctex_data_tag(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_tlchv_node(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i,%i:%i,%i", |
|
synctex_node_isa(node), |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_kern_node(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i,%i:%i,%i:%i", |
|
synctex_node_isa(node), |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_rule(synctex_node_p node) { |
|
if (node) { |
|
printf("%s:%i,%i:%i,%i", |
|
synctex_node_isa(node), |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node)); |
|
printf(":%i",_synctex_data_width(node)); |
|
printf(",%i",_synctex_data_height(node)); |
|
printf(",%i",_synctex_data_depth(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_void_box(synctex_node_p node) { |
|
if (node) { |
|
printf("%s",synctex_node_isa(node)); |
|
printf(":%i",_synctex_data_tag(node)); |
|
printf(",%i",_synctex_data_line(node)); |
|
printf(",%i",0); |
|
printf(":%i",_synctex_data_h(node)); |
|
printf(",%i",_synctex_data_v(node)); |
|
printf(":%i",_synctex_data_width(node)); |
|
printf(",%i",_synctex_data_height(node)); |
|
printf(",%i",_synctex_data_depth(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_vbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%s",synctex_node_isa(node)); |
|
printf(":%i",_synctex_data_tag(node)); |
|
printf(",%i",_synctex_data_line(node)); |
|
printf(",%i",0); |
|
printf(":%i",_synctex_data_h(node)); |
|
printf(",%i",_synctex_data_v(node)); |
|
printf(":%i",_synctex_data_width(node)); |
|
printf(",%i",_synctex_data_height(node)); |
|
printf(",%i",_synctex_data_depth(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
printf(" NEXT_hbox:%p\n",(void *)_synctex_tree_next_hbox(node)); |
|
} |
|
} |
|
|
|
static void _synctex_log_hbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%s",synctex_node_isa(node)); |
|
printf(":%i",_synctex_data_tag(node)); |
|
printf(",%i~%i*%i",_synctex_data_line(node),_synctex_data_mean_line(node),_synctex_data_weight(node)); |
|
printf(",%i",0); |
|
printf(":%i",_synctex_data_h(node)); |
|
printf(",%i",_synctex_data_v(node)); |
|
printf(":%i",_synctex_data_width(node)); |
|
printf(",%i",_synctex_data_height(node)); |
|
printf(",%i",_synctex_data_depth(node)); |
|
printf("/%i",_synctex_data_h_V(node)); |
|
printf(",%i",_synctex_data_v_V(node)); |
|
printf(":%i",_synctex_data_width_V(node)); |
|
printf(",%i",_synctex_data_height_V(node)); |
|
printf(",%i",_synctex_data_depth_V(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" PARENT:%p\n",(void *)_synctex_tree_parent(node)); |
|
printf(" CHILD:%p\n",(void *)_synctex_tree_child(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
printf(" NEXT_hbox:%p\n",(void *)_synctex_tree_next_hbox(node)); |
|
} |
|
} |
|
static void _synctex_log_proxy(synctex_node_p node) { |
|
if (node) { |
|
synctex_node_p N = _synctex_tree_target(node); |
|
printf("%s",synctex_node_isa(node)); |
|
printf(":%i",_synctex_data_h(node)); |
|
printf(",%i",_synctex_data_v(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" LEFT:%p\n",(void *)_synctex_tree_friend(node)); |
|
printf(" ->%s\n",_synctex_node_abstract(N)); |
|
} |
|
} |
|
static void _synctex_log_handle(synctex_node_p node) { |
|
if (node) { |
|
synctex_node_p N = _synctex_tree_target(node); |
|
printf("%s",synctex_node_isa(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
printf("SELF:%p\n",(void *)node); |
|
printf(" SIBLING:%p\n",(void *)__synctex_tree_sibling(node)); |
|
printf(" ->%s\n",_synctex_node_abstract(N)); |
|
} |
|
} |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark SYNCTEX_DISPLAY |
|
# endif |
|
|
|
int synctex_scanner_display_switcher(synctex_scanner_p scanR) { |
|
return scanR->display_switcher; |
|
} |
|
void synctex_scanner_set_display_switcher(synctex_scanner_p scanR, int switcher) { |
|
scanR->display_switcher = switcher; |
|
} |
|
static const char * const _synctex_display_prompt = "................................"; |
|
|
|
static char * _synctex_scanner_display_prompt_down(synctex_scanner_p scanR) { |
|
if (scanR->display_prompt>_synctex_display_prompt) { |
|
--scanR->display_prompt; |
|
} |
|
return scanR->display_prompt; |
|
} |
|
static char * _synctex_scanner_display_prompt_up(synctex_scanner_p scanR) { |
|
if (scanR->display_prompt+1<_synctex_display_prompt+strlen(_synctex_display_prompt)) { |
|
++scanR->display_prompt; |
|
} |
|
return scanR->display_prompt; |
|
} |
|
|
|
void synctex_node_display(synctex_node_p node) { |
|
if (node) { |
|
synctex_scanner_p scanR = node->class->scanner; |
|
if (scanR) { |
|
if (scanR->display_switcher<0) { |
|
SYNCTEX_MSG_SEND(node, display); |
|
} else if (scanR->display_switcher>0 && --scanR->display_switcher>0) { |
|
SYNCTEX_MSG_SEND(node, display); |
|
} else if (scanR->display_switcher-->=0) { |
|
printf("%s Next display skipped. Reset display switcher.\n",node->class->scanner->display_prompt); |
|
} |
|
} else { |
|
SYNCTEX_MSG_SEND(node, display); |
|
} |
|
} |
|
} |
|
static char * _synctex_node_abstract(synctex_node_p node) { |
|
SYNCTEX_PARAMETER_ASSERT(node || node->class); |
|
return (node && node->class->abstract)? node->class->abstract(node):"none"; |
|
} |
|
|
|
SYNCTEX_INLINE static void _synctex_display_child(synctex_node_p node) { |
|
synctex_node_p N = _synctex_tree_child(node); |
|
if (N) { |
|
_synctex_scanner_display_prompt_down(N->class->scanner); |
|
synctex_node_display(N); |
|
_synctex_scanner_display_prompt_up(N->class->scanner); |
|
} |
|
} |
|
|
|
SYNCTEX_INLINE static void _synctex_display_sibling(synctex_node_p node) { |
|
synctex_node_display(__synctex_tree_sibling(node)); |
|
} |
|
#define SYNCTEX_ABSTRACT_MAX 128 |
|
static char * _synctex_abstract_input(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"Input:%i:%s(%i)" SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_name(node), |
|
_synctex_data_line(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_input(synctex_node_p node) { |
|
if (node) { |
|
printf("Input:%i:%s(%i)" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
_synctex_data_tag(node), |
|
_synctex_data_name(node), |
|
_synctex_data_line(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
synctex_node_display(__synctex_tree_sibling(node)); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_sheet(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"{%i...}" SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_page(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_sheet(synctex_node_p node) { |
|
if (node) { |
|
printf("%s{%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_page(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_child(node); |
|
printf("%s}\n",node->class->scanner->display_prompt); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_form(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"<%i...>" SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
SYNCTEX_PRINT_CHARINDEX; |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_form(synctex_node_p node) { |
|
if (node) { |
|
printf("%s<%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_child(node); |
|
printf("%s>\n",node->class->scanner->display_prompt); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_vbox(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"[%i,%i:%i,%i:%i,%i,%i...]" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_vbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%s[%i,%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_child(node); |
|
printf("%s]\n%slast:%s\n", |
|
node->class->scanner->display_prompt, |
|
node->class->scanner->display_prompt, |
|
_synctex_node_abstract(_synctex_tree_last(node))); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_hbox(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"(%i,%i~%i*%i:%i,%i:%i,%i,%i...)" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_mean_line(node), |
|
_synctex_data_weight(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_hbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%s(%i,%i~%i*%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_mean_line(node), |
|
_synctex_data_weight(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_child(node); |
|
printf("%s)\n%slast:%s\n", |
|
node->class->scanner->display_prompt, |
|
node->class->scanner->display_prompt, |
|
_synctex_node_abstract(_synctex_tree_last(node))); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_void_vbox(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"v%i,%i;%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_void_vbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%sv%i,%i;%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_void_hbox(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"h%i,%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_void_hbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%sh%i,%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_glue(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"glue:%i,%i:%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_glue(synctex_node_p node) { |
|
if (node) { |
|
printf("%sglue:%i,%i:%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_rule(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"rule:%i,%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_rule(synctex_node_p node) { |
|
if (node) { |
|
printf("%srule:%i,%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node), |
|
_synctex_data_height(node), |
|
_synctex_data_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_math(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"math:%i,%i:%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_math(synctex_node_p node) { |
|
if (node) { |
|
printf("%smath:%i,%i:%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_kern(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"kern:%i,%i:%i,%i:%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_kern(synctex_node_p node) { |
|
if (node) { |
|
printf("%skern:%i,%i:%i,%i:%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
_synctex_data_width(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_boundary(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"boundary:%i,%i:%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_boundary(synctex_node_p node) { |
|
if (node) { |
|
printf("%sboundary:%i,%i:%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_box_bdry(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"box bdry:%i,%i:%i,%i" SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_box_bdry(synctex_node_p node) { |
|
if (node) { |
|
printf("%sbox bdry:%i,%i:%i,%i", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_ref(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"form ref:%i:%i,%i" SYNCTEX_PRINT_CHARINDEX_FMT, |
|
_synctex_data_tag(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_ref(synctex_node_p node) { |
|
if (node) { |
|
printf("%sform ref:%i:%i,%i", |
|
node->class->scanner->display_prompt, |
|
_synctex_data_tag(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node)); |
|
SYNCTEX_PRINT_CHARINDEX_NL; |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
static char * _synctex_abstract_proxy(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
synctex_node_p N = _synctex_tree_target(node); |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"%s:%i,%i:%i,%i/%p%s", |
|
synctex_node_isa(node), |
|
synctex_node_tag(node), |
|
synctex_node_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node), |
|
node, |
|
_synctex_node_abstract(N)); |
|
} |
|
return abstract; |
|
} |
|
static void _synctex_display_proxy(synctex_node_p node) { |
|
if (node) { |
|
synctex_node_p N = _synctex_tree_target(node); |
|
printf("%s%s:%i,%i:%i,%i", |
|
node->class->scanner->display_prompt, |
|
synctex_node_isa(node), |
|
synctex_node_tag(node), |
|
synctex_node_line(node), |
|
_synctex_data_h(node), |
|
_synctex_data_v(node)); |
|
if (N) { |
|
printf("=%i,%i:%i,%i,%i->%s", |
|
synctex_node_h(node), |
|
synctex_node_v(node), |
|
synctex_node_width(node), |
|
synctex_node_height(node), |
|
synctex_node_depth(node), |
|
_synctex_node_abstract(N)); |
|
} |
|
printf("\n"); |
|
_synctex_display_child(node); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
static char * _synctex_abstract_proxy_vbox(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX, |
|
"[*%i,%i:%i,%i:%i,%i,%i...*]" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
synctex_node_tag(node), |
|
synctex_node_line(node), |
|
synctex_node_h(node), |
|
synctex_node_v(node), |
|
synctex_node_width(node), |
|
synctex_node_height(node), |
|
synctex_node_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_proxy_vbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%s[*%i,%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
synctex_node_tag(node), |
|
synctex_node_line(node), |
|
synctex_node_h(node), |
|
synctex_node_v(node), |
|
synctex_node_width(node), |
|
synctex_node_height(node), |
|
synctex_node_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_child(node); |
|
printf("%s*]\n%slast:%s\n", |
|
node->class->scanner->display_prompt, |
|
node->class->scanner->display_prompt, |
|
_synctex_node_abstract(_synctex_tree_last(node))); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_proxy_hbox(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"(*%i,%i~%i*%i:%i,%i:%i,%i,%i...*)/%p" |
|
SYNCTEX_PRINT_CHARINDEX_FMT, |
|
synctex_node_tag(node), |
|
synctex_node_line(node), |
|
synctex_node_mean_line(node), |
|
synctex_node_weight(node), |
|
synctex_node_h(node), |
|
synctex_node_v(node), |
|
synctex_node_width(node), |
|
synctex_node_height(node), |
|
synctex_node_depth(node), |
|
node |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
} |
|
return abstract; |
|
} |
|
|
|
static void _synctex_display_proxy_hbox(synctex_node_p node) { |
|
if (node) { |
|
printf("%s(*%i,%i~%i*%i:%i,%i:%i,%i,%i" |
|
SYNCTEX_PRINT_CHARINDEX_FMT |
|
"\n", |
|
node->class->scanner->display_prompt, |
|
synctex_node_tag(node), |
|
synctex_node_line(node), |
|
synctex_node_mean_line(node), |
|
synctex_node_weight(node), |
|
synctex_node_h(node), |
|
synctex_node_v(node), |
|
synctex_node_width(node), |
|
synctex_node_height(node), |
|
synctex_node_depth(node) |
|
SYNCTEX_PRINT_CHARINDEX_WHAT); |
|
_synctex_display_child(node); |
|
printf("%s*)\n%slast:%s\n", |
|
node->class->scanner->display_prompt, |
|
node->class->scanner->display_prompt, |
|
_synctex_node_abstract(_synctex_tree_last(node))); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
|
|
static char * _synctex_abstract_handle(synctex_node_p node) { |
|
static char abstract[SYNCTEX_ABSTRACT_MAX] = "none"; |
|
if (node) { |
|
synctex_node_p N = _synctex_tree_target(node); |
|
if (N && !N->class) { |
|
exit(1); |
|
} |
|
snprintf(abstract,SYNCTEX_ABSTRACT_MAX,"%s:%s", |
|
synctex_node_isa(node), |
|
(N?_synctex_node_abstract(N):"")); |
|
} |
|
return abstract; |
|
} |
|
static void _synctex_display_handle(synctex_node_p node) { |
|
if (node) { |
|
synctex_node_p N = _synctex_tree_target(node); |
|
printf("%s%s:->%s\n", |
|
node->class->scanner->display_prompt, |
|
synctex_node_isa(node), |
|
_synctex_node_abstract(N)); |
|
_synctex_display_child(node); |
|
_synctex_display_sibling(node); |
|
} |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark STATUS |
|
# endif |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Prototypes |
|
# endif |
|
typedef struct { |
|
size_t size; |
|
synctex_status_t status; |
|
} synctex_zs_s; |
|
static synctex_zs_s _synctex_buffer_get_available_size(synctex_scanner_p scanner, size_t size); |
|
static synctex_status_t _synctex_next_line(synctex_scanner_p scanner); |
|
static synctex_status_t _synctex_match_string(synctex_scanner_p scanner, const char * the_string); |
|
|
|
typedef struct synctex_ns_t { |
|
synctex_node_p node; |
|
synctex_status_t status; |
|
} synctex_ns_s; |
|
static synctex_ns_s __synctex_parse_new_input(synctex_scanner_p scanner); |
|
static synctex_status_t _synctex_scan_preamble(synctex_scanner_p scanner); |
|
typedef struct { |
|
float value; |
|
synctex_status_t status; |
|
} synctex_fs_s; |
|
static synctex_fs_s _synctex_scan_float_and_dimension(synctex_scanner_p scanner); |
|
static synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_p scanner); |
|
static synctex_status_t _synctex_scan_postamble(synctex_scanner_p scanner); |
|
static synctex_status_t _synctex_setup_visible_hbox(synctex_node_p box); |
|
static synctex_status_t _synctex_scan_content(synctex_scanner_p scanner); |
|
int synctex_scanner_pre_x_offset(synctex_scanner_p scanner); |
|
int synctex_scanner_pre_y_offset(synctex_scanner_p scanner); |
|
const char * synctex_scanner_get_output_fmt(synctex_scanner_p scanner); |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark SCANNER UTILITIES |
|
# endif |
|
|
|
# define SYNCTEX_FILE (scanner->reader->file) |
|
|
|
/** |
|
* Try to ensure that the buffer contains at least size bytes. |
|
* Passing a huge size argument means the whole buffer length. |
|
* Passing a 0 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. |
|
* The size_t value returned 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. |
|
* - parameter scanner: The owning scanner. When NULL, returns SYNCTEX_STATUS_BAD_ARGUMENT. |
|
* - parameter expected: expected number of bytes. |
|
* - returns: a size and a status. |
|
*/ |
|
static synctex_zs_s _synctex_buffer_get_available_size(synctex_scanner_p scanner, size_t expected) { |
|
size_t size = 0; |
|
if (NULL == scanner) { |
|
return (synctex_zs_s){0,SYNCTEX_STATUS_BAD_ARGUMENT}; |
|
} |
|
if (expected>scanner->reader->size){ |
|
expected = scanner->reader->size; |
|
} |
|
size = SYNCTEX_END - SYNCTEX_CUR; /* available is the number of unparsed chars in the buffer */ |
|
if (expected<=size) { |
|
/* There are already sufficiently many characters in the buffer */ |
|
return (synctex_zs_s){size,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->reader->charindex_offset += SYNCTEX_CUR - SYNCTEX_START; |
|
# endif |
|
if (size) { |
|
memmove(SYNCTEX_START, SYNCTEX_CUR, size); |
|
} |
|
SYNCTEX_CUR = SYNCTEX_START + size; /* 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 - size)); |
|
if (already_read>0) { |
|
/* We assume that 0<already_read<=SYNCTEX_BUFFER_SIZE - size, such that |
|
* SYNCTEX_CUR + already_read = SYNCTEX_START + size + 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'; /* there is enough room */ |
|
SYNCTEX_CUR = SYNCTEX_START; |
|
/* May be available is less than size, the caller will have to test. */ |
|
return (synctex_zs_s){SYNCTEX_END - SYNCTEX_CUR,SYNCTEX_STATUS_OK}; |
|
} 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_zs_s){0,SYNCTEX_STATUS_ERROR}; |
|
} else if (errnum) { |
|
_synctex_error("gzread error (%i:%i,%s)",already_read,errnum,error_string); |
|
return (synctex_zs_s){0,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.*/ |
|
/* there might be a bit of text left */ |
|
return (synctex_zs_s){SYNCTEX_END - SYNCTEX_CUR,SYNCTEX_STATUS_EOF}; |
|
} |
|
/* We cannot enlarge the buffer because the end of the file was reached. */ |
|
return (synctex_zs_s){size,SYNCTEX_STATUS_EOF}; |
|
} |
|
|
|
/* 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 |
|
*/ |
|
static synctex_status_t _synctex_next_line(synctex_scanner_p scanner) { |
|
synctex_status_t status = SYNCTEX_STATUS_OK; |
|
if (NULL == scanner) { |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
infinite_loop: |
|
while(SYNCTEX_CUR<SYNCTEX_END) { |
|
if (*SYNCTEX_CUR == '\n') { |
|
++SYNCTEX_CUR; |
|
++scanner->reader->line_number; |
|
return _synctex_buffer_get_available_size(scanner, 1).status; |
|
} |
|
++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. */ |
|
status = _synctex_buffer_get_available_size(scanner, 1).status; |
|
if (status<=SYNCTEX_STATUS_EOF) { |
|
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. |
|
*/ |
|
static synctex_status_t _synctex_match_string(synctex_scanner_p 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_zs_s zs = {0,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? */ |
|
zs = _synctex_buffer_get_available_size(scanner,remaining_len); |
|
if (zs.status<SYNCTEX_STATUS_EOF) { |
|
return zs.status; |
|
} |
|
/* Maybe we have less characters than expected because the buffer is too small. */ |
|
if (zs.size>=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,zs.size)) { |
|
/* 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 += zs.size; |
|
/* update the remaining length and the parsed length. */ |
|
remaining_len -= zs.size; |
|
tested_len += zs.size; |
|
SYNCTEX_CUR += zs.size; /* We validate the tested characters. */ |
|
if (0 == remaining_len) { |
|
/* Nothing left to test, we have found the given string. */ |
|
return SYNCTEX_STATUS_OK; |
|
} |
|
/* 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. */ |
|
/* SYNCTEX_CUR - SYNCTEX_START is 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 whole buffer contents is replaced in _synctex_buffer_get_available_size. |
|
* They were buffered from offset-len location in the file. */ |
|
offset -= SYNCTEX_CUR - SYNCTEX_START; |
|
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. */ |
|
zs = _synctex_buffer_get_available_size(scanner,remaining_len); |
|
if (zs.status<SYNCTEX_STATUS_EOF) { |
|
return zs.status; /* This is an error, no need to go further. */ |
|
} |
|
if (zs.size==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 (zs.size<remaining_len) { |
|
/* We'll have to loop one more time. */ |
|
if (strncmp((char *)SYNCTEX_CUR,the_string,zs.size)) { |
|
/* 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 -= zs.size; |
|
tested_len += zs.size; |
|
SYNCTEX_CUR += zs.size; /* We validate the tested characters. */ |
|
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. |
|
*/ |
|
static synctex_is_s _synctex_decode_int(synctex_scanner_p scanner) { |
|
char * ptr = NULL; |
|
char * end = NULL; |
|
synctex_zs_s zs = {0,0}; |
|
int result; |
|
if (NULL == scanner) { |
|
return (synctex_is_s){0, SYNCTEX_STATUS_BAD_ARGUMENT}; |
|
} |
|
zs = _synctex_buffer_get_available_size(scanner, SYNCTEX_BUFFER_MIN_SIZE); |
|
if (zs.status<SYNCTEX_STATUS_EOF) { |
|
return (synctex_is_s){0,zs.status}; |
|
} |
|
if (zs.size==0) { |
|
return (synctex_is_s){0,SYNCTEX_STATUS_NOT_OK}; |
|
} |
|
ptr = SYNCTEX_CUR; |
|
/* Optionally parse the separator */ |
|
if (*ptr==':' || *ptr==',') { |
|
++ptr; |
|
--zs.size; |
|
if (zs.size==0) { |
|
return (synctex_is_s){0,SYNCTEX_STATUS_NOT_OK}; |
|
} |
|
} |
|
result = (int)strtol(ptr, &end, 10); |
|
if (end>ptr) { |
|
SYNCTEX_CUR = end; |
|
return (synctex_is_s){result,SYNCTEX_STATUS_OK}; |
|
} |
|
return (synctex_is_s){result,SYNCTEX_STATUS_NOT_OK}; |
|
} |
|
static synctex_is_s _synctex_decode_int_opt(synctex_scanner_p scanner, int default_value) { |
|
char * ptr = NULL; |
|
char * end = NULL; |
|
synctex_zs_s zs = {0, 0}; |
|
if (NULL == scanner) { |
|
return (synctex_is_s){default_value, SYNCTEX_STATUS_BAD_ARGUMENT}; |
|
} |
|
zs = _synctex_buffer_get_available_size(scanner, SYNCTEX_BUFFER_MIN_SIZE); |
|
if (zs.status<SYNCTEX_STATUS_EOF) { |
|
return (synctex_is_s){default_value,zs.status}; |
|
} |
|
if (zs.size==0) { |
|
return (synctex_is_s){default_value,SYNCTEX_STATUS_OK}; |
|
} |
|
ptr = SYNCTEX_CUR; |
|
/* Comma separator required */ |
|
if (*ptr==',') { |
|
int result; |
|
++ptr; |
|
--zs.size; |
|
if (zs.size==0) { |
|
return (synctex_is_s){default_value,SYNCTEX_STATUS_NOT_OK}; |
|
} |
|
result = (int)strtol(ptr, &end, 10); |
|
if (end>ptr) { |
|
SYNCTEX_CUR = end; |
|
return (synctex_is_s){result,SYNCTEX_STATUS_OK}; |
|
} |
|
return (synctex_is_s){default_value,SYNCTEX_STATUS_NOT_OK}; |
|
} |
|
return (synctex_is_s){default_value,SYNCTEX_STATUS_OK}; |
|
} |
|
/* Used when parsing the synctex file. |
|
* Decode an integer for a v field. |
|
* Try the _synctex_decode_int version and set the last v field scanned. |
|
* If it does not succeed, tries to match an '=' sign, |
|
* which is a shortcut for the last v field scanned. |
|
*/ |
|
# define SYNCTEX_INPUT_COMEQUALS ",=" |
|
static synctex_is_s _synctex_decode_int_v(synctex_scanner_p scanner) { |
|
synctex_is_s is = _synctex_decode_int(scanner); |
|
if (SYNCTEX_STATUS_OK == is.status) { |
|
scanner->reader->lastv = is.integer; |
|
return is; |
|
} |
|
is.status = _synctex_match_string(scanner,SYNCTEX_INPUT_COMEQUALS); |
|
if (is.status<SYNCTEX_STATUS_OK) { |
|
return is; |
|
} |
|
is.integer = scanner->reader->lastv; |
|
return is; |
|
} |
|
|
|
/* 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, |
|
* unless it transfers the ownership to another object. |
|
* 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. |
|
*/ |
|
static synctex_ss_s _synctex_decode_string(synctex_scanner_p scanner) { |
|
char * end = NULL; |
|
size_t len = 0;/* The number of bytes to copy */ |
|
size_t already_len = 0; |
|
synctex_zs_s zs = {0,0}; |
|
char * string = NULL; |
|
if (NULL == scanner) { |
|
return (synctex_ss_s){NULL,SYNCTEX_STATUS_BAD_ARGUMENT}; |
|
} |
|
/* The buffer must at least contain one character: the '\n' end of line marker */ |
|
if (SYNCTEX_CUR>=SYNCTEX_END) { |
|
more_characters: |
|
zs = _synctex_buffer_get_available_size(scanner,1); |
|
if (zs.status < SYNCTEX_STATUS_EOF) { |
|
return (synctex_ss_s){NULL,zs.status}; |
|
} else if (0 == zs.size) { |
|
return (synctex_ss_s){NULL,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; |
|
/* We scan all the characters up to the next '\n' */ |
|
while (end<SYNCTEX_END && *end != '\n') { |
|
++end; |
|
} |
|
/* OK, we found where to stop: |
|
* either end == SYNCTEX_END |
|
* or *end == '\n' */ |
|
len = end - SYNCTEX_CUR; |
|
if (len<UINT_MAX-already_len) { |
|
if ((string = realloc(string,len+already_len+1)) != NULL) { |
|
if (memcpy(string+already_len,SYNCTEX_CUR,len)) { |
|
already_len += len; |
|
string[already_len]='\0'; /* Terminate the string */ |
|
SYNCTEX_CUR += len;/* Eventually advance to the terminating '\n' */ |
|
if (SYNCTEX_CUR==SYNCTEX_END) { |
|
/* No \n found*/ |
|
goto more_characters; |
|
} |
|
/* trim the trailing whites */ |
|
len = already_len; |
|
while (len>0) { |
|
already_len = len--; |
|
if (string[len]!=' ') { |
|
break; |
|
} |
|
} |
|
string[already_len] = '\0'; |
|
return (synctex_ss_s){string,SYNCTEX_STATUS_OK}; |
|
} |
|
free(string); |
|
_synctex_error("could not copy memory (1)."); |
|
return (synctex_ss_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
} |
|
_synctex_error("could not (re)allocate memory (1)."); |
|
return (synctex_ss_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
|
|
/* Used when parsing the synctex file. |
|
* Read an Input record. |
|
* - parameter scanner: non NULL scanner |
|
* - returns SYNCTEX_STATUS_OK on successful completions, others values otherwise. |
|
*/ |
|
static synctex_ns_s __synctex_parse_new_input(synctex_scanner_p scanner) { |
|
synctex_node_p input = NULL; |
|
synctex_status_t status = SYNCTEX_STATUS_BAD_ARGUMENT; |
|
synctex_zs_s zs = {0,0}; |
|
if (NULL == scanner) { |
|
return (synctex_ns_s){NULL,status}; |
|
} |
|
if ((status=_synctex_match_string(scanner,SYNCTEX_INPUT_MARK))<SYNCTEX_STATUS_OK) { |
|
return (synctex_ns_s){NULL,status}; |
|
} |
|
/* Create a node */ |
|
if (NULL == (input = _synctex_new_input(scanner))) { |
|
_synctex_error("Could not create an input node."); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
/* Decode the tag */ |
|
if ((status=_synctex_data_decode_tag(input))<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Bad format of input node."); |
|
synctex_node_free(input); |
|
return (synctex_ns_s){NULL,status}; |
|
} |
|
/* The next character is a field separator, we expect one character in the buffer. */ |
|
zs = _synctex_buffer_get_available_size(scanner, 1); |
|
if (zs.status<=SYNCTEX_STATUS_ERROR) { |
|
return (synctex_ns_s){NULL,status}; |
|
} |
|
if (0 == zs.size) { |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_EOF}; |
|
} |
|
/* We can now safely advance to the next character, stepping over the field separator. */ |
|
++SYNCTEX_CUR; |
|
--zs.size; |
|
/* Then we scan the file name */ |
|
if ((status=_synctex_data_decode_name(input))<SYNCTEX_STATUS_OK) { |
|
synctex_node_free(input); |
|
_synctex_next_line(scanner);/* Ignore this whole line */ |
|
return (synctex_ns_s){NULL,status}; |
|
} |
|
/* Prepend this input node to the input linked list of the scanner */ |
|
__synctex_tree_set_sibling(input,scanner->input);/* input has no parent */ |
|
scanner->input = input; |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(input); |
|
# endif |
|
return (synctex_ns_s){input,_synctex_next_line(scanner)};/* read the line termination character, if any */ |
|
} |
|
|
|
typedef synctex_is_s (*synctex_decoder_t)(synctex_scanner_p); |
|
|
|
/* 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. |
|
*/ |
|
static synctex_status_t _synctex_scan_named(synctex_scanner_p scanner,const char * name) { |
|
synctex_status_t status = 0; |
|
if (NULL == scanner || NULL == name) { |
|
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; |
|
} |
|
return SYNCTEX_STATUS_OK; |
|
} |
|
|
|
/* Used when parsing the synctex file. |
|
* Read the preamble. |
|
*/ |
|
static synctex_status_t _synctex_scan_preamble(synctex_scanner_p scanner) { |
|
synctex_status_t status = 0; |
|
synctex_is_s is = {0,0}; |
|
synctex_ss_s ss = {NULL,0}; |
|
if (NULL == scanner) { |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
status = _synctex_scan_named(scanner,"SyncTeX Version:"); |
|
if (status<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
is = _synctex_decode_int(scanner); |
|
if (is.status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
status = _synctex_next_line(scanner); |
|
if (status<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->version = is.integer; |
|
/* Read all the input records */ |
|
do { |
|
status = __synctex_parse_new_input(scanner).status; |
|
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. */ |
|
if ((status=_synctex_scan_named(scanner,"Output:"))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
if ((ss=_synctex_decode_string(scanner)).status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->output_fmt = ss.string; |
|
if ((status=_synctex_scan_named(scanner,"Magnification:"))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->pre_magnification = is.integer; |
|
if ((status=_synctex_scan_named(scanner,"Unit:"))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->pre_unit = is.integer; |
|
if ((status=_synctex_scan_named(scanner,"X Offset:"))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->pre_x_offset = is.integer; |
|
if ((status=_synctex_scan_named(scanner,"Y Offset:"))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->pre_y_offset = is.integer; |
|
return SYNCTEX_STATUS_OK; |
|
} |
|
|
|
/* parse a float with a dimension */ |
|
static synctex_fs_s _synctex_scan_float_and_dimension(synctex_scanner_p scanner) { |
|
synctex_fs_s fs = {0,0}; |
|
synctex_zs_s zs = {0,0}; |
|
char * endptr = NULL; |
|
#ifdef HAVE_SETLOCALE |
|
char * loc = setlocale(LC_NUMERIC, NULL); |
|
#endif |
|
if (NULL == scanner) { |
|
return (synctex_fs_s){0,SYNCTEX_STATUS_BAD_ARGUMENT}; |
|
} |
|
zs = _synctex_buffer_get_available_size(scanner, SYNCTEX_BUFFER_MIN_SIZE); |
|
if (zs.status<SYNCTEX_STATUS_EOF) { |
|
_synctex_error("Problem with float."); |
|
return (synctex_fs_s){0,zs.status}; |
|
} |
|
#ifdef HAVE_SETLOCALE |
|
setlocale(LC_NUMERIC, "C"); |
|
#endif |
|
fs.value = strtod(SYNCTEX_CUR,&endptr); |
|
#ifdef HAVE_SETLOCALE |
|
setlocale(LC_NUMERIC, loc); |
|
#endif |
|
if (endptr == SYNCTEX_CUR) { |
|
_synctex_error("A float was expected."); |
|
return (synctex_fs_s){0,SYNCTEX_STATUS_ERROR}; |
|
} |
|
SYNCTEX_CUR = endptr; |
|
if ((fs.status = _synctex_match_string(scanner,"in")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 72.27f*65536; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
report_unit_error: |
|
_synctex_error("problem with unit."); |
|
return fs; |
|
} else if ((fs.status = _synctex_match_string(scanner,"cm")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 72.27f*65536/2.54f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 72.27f*65536/25.4f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 65536.0f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 72.27f/72*65536.0f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 12.0*65536.0f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 1.0f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 1238.0f/1157*65536.0f; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 14856.0f/1157*65536; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 685.0f/642*65536; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} else if ((fs.status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) { |
|
fs.value *= 1370.0f/107*65536; |
|
} else if (fs.status<SYNCTEX_STATUS_EOF) { |
|
goto report_unit_error; |
|
} |
|
return fs; |
|
} |
|
|
|
/* parse the post scriptum |
|
* SYNCTEX_STATUS_OK is returned on completion |
|
* a negative error is returned otherwise */ |
|
static synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_p scanner) { |
|
synctex_status_t status = 0; |
|
synctex_fs_s fs = {0,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) { |
|
fs = _synctex_scan_float_and_dimension(scanner); |
|
if (fs.status<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Problem with X offset in the Post Scriptum."); |
|
return fs.status; |
|
} |
|
scanner->x_offset = fs.value; |
|
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) { |
|
fs = _synctex_scan_float_and_dimension(scanner); |
|
if (fs.status<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Problem with Y offset in the Post Scriptum."); |
|
return fs.status; |
|
} |
|
scanner->x_offset = fs.value; |
|
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. |
|
*/ |
|
static synctex_status_t _synctex_scan_postamble(synctex_scanner_p scanner) { |
|
synctex_status_t status = 0; |
|
synctex_is_s is = {0,0}; |
|
if (NULL == scanner) { |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
if (!scanner->flags.postamble && (status=_synctex_match_string(scanner,"Postamble:"))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
count_again: |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
if ((status=_synctex_scan_named(scanner,"Count:"))< SYNCTEX_STATUS_EOF) { |
|
return status; /* forward the error */ |
|
} else if (status < SYNCTEX_STATUS_OK) { /* No Count record found */ |
|
goto count_again; |
|
} |
|
if ((is=_synctex_decode_int(scanner)).status<SYNCTEX_STATUS_OK) { |
|
return is.status; |
|
} |
|
if ((status=_synctex_next_line(scanner))<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
scanner->count = is.integer; |
|
/* 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. |
|
*/ |
|
static synctex_status_t _synctex_setup_visible_hbox(synctex_node_p box) { |
|
if (box) { |
|
switch(synctex_node_type(box)) { |
|
case synctex_node_type_hbox: |
|
_synctex_data_set_h_V(box,_synctex_data_h(box)); |
|
_synctex_data_set_v_V(box,_synctex_data_v(box)); |
|
_synctex_data_set_width_V(box,_synctex_data_width(box)); |
|
_synctex_data_set_height_V(box,_synctex_data_height(box)); |
|
_synctex_data_set_depth_V(box,_synctex_data_depth(box)); |
|
return SYNCTEX_STATUS_OK; |
|
default: |
|
break; |
|
} |
|
} |
|
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). |
|
*/ |
|
static synctex_status_t _synctex_make_hbox_contain_point(synctex_node_p node,synctex_point_s point) { |
|
int min, max, n; |
|
if (NULL == node || synctex_node_type(node) != synctex_node_type_hbox) { |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
if ((n = _synctex_data_width_V(node))<0) { |
|
max = _synctex_data_h_V(node); |
|
min = max+n; |
|
if (point.h<min) { |
|
_synctex_data_set_width_V(node,point.h-max); |
|
} else if (point.h>max) { |
|
_synctex_data_set_h_V(node,point.h); |
|
_synctex_data_set_width_V(node,min-point.h); |
|
} |
|
} else { |
|
min = _synctex_data_h_V(node); |
|
max = min+n; |
|
if (point.h<min) { |
|
_synctex_data_set_h_V(node,point.h); |
|
_synctex_data_set_width_V(node,max - point.h); |
|
} else if (point.h>max) { |
|
_synctex_data_set_width_V(node,point.h - min); |
|
} |
|
} |
|
n = _synctex_data_v_V(node); |
|
min = n - _synctex_data_height_V(node); |
|
max = n + _synctex_data_depth_V(node); |
|
if (point.v<min) { |
|
_synctex_data_set_height_V(node,n-point.v); |
|
} else if (point.v>max) { |
|
_synctex_data_set_depth_V(node,point.v-n); |
|
} |
|
return SYNCTEX_STATUS_OK; |
|
} |
|
static synctex_status_t _synctex_make_hbox_contain_box(synctex_node_p node,synctex_box_s box) { |
|
int min, max, n; |
|
if (NULL == node || synctex_node_type(node) != synctex_node_type_hbox) { |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
if ((n = _synctex_data_width_V(node))<0) { |
|
max = _synctex_data_h_V(node); |
|
min = max+n; |
|
if (box.min.h <min) { |
|
_synctex_data_set_width_V(node,box.min.h-max); |
|
} else if (box.max.h>max) { |
|
_synctex_data_set_h_V(node,box.max.h); |
|
_synctex_data_set_width_V(node,min-box.max.h); |
|
} |
|
} else { |
|
min = _synctex_data_h_V(node); |
|
max = min+n; |
|
if (box.min.h<min) { |
|
_synctex_data_set_h_V(node,box.min.h); |
|
_synctex_data_set_width_V(node,max - box.min.h); |
|
} else if (box.max.h>max) { |
|
_synctex_data_set_width_V(node,box.max.h - min); |
|
} |
|
} |
|
n = _synctex_data_v_V(node); |
|
min = n - _synctex_data_height_V(node); |
|
max = n + _synctex_data_depth_V(node); |
|
if (box.min.v<min) { |
|
_synctex_data_set_height_V(node,n-box.min.v); |
|
} else if (box.max.v>max) { |
|
_synctex_data_set_depth_V(node,box.max.v-n); |
|
} |
|
return SYNCTEX_STATUS_OK; |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark SPECIAL CHARACTERS |
|
# endif |
|
|
|
|
|
/* 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_FORM '<' |
|
# define SYNCTEX_CHAR_END_FORM '>' |
|
# 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_RULE 'r' |
|
# define SYNCTEX_CHAR_MATH '$' |
|
# define SYNCTEX_CHAR_FORM_REF 'f' |
|
# define SYNCTEX_CHAR_BOUNDARY 'x' |
|
# define SYNCTEX_CHAR_CHARACTER 'c' |
|
# define SYNCTEX_CHAR_COMMENT '%' |
|
|
|
# define SYNCTEX_RETURN(STATUS) return STATUS; |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark SCANNERS & PARSERS |
|
# endif |
|
|
|
# define SYNCTEX_DECODE_FAILED(NODE,WHAT) \ |
|
(_synctex_data_decode_##WHAT(NODE)<SYNCTEX_STATUS_OK) |
|
# define SYNCTEX_DECODE_FAILED_V(NODE,WHAT) \ |
|
(_synctex_data_decode_##WHAT##_v(NODE)<SYNCTEX_STATUS_OK) |
|
|
|
#define SYNCTEX_NS_NULL (synctex_ns_s){NULL,SYNCTEX_STATUS_NOT_OK} |
|
static synctex_ns_s _synctex_parse_new_sheet(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_sheet(scanner))) { |
|
if ( |
|
SYNCTEX_DECODE_FAILED(node,page)) { |
|
_synctex_error("Bad sheet record."); |
|
} else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of sheet."); |
|
} else { |
|
/* Now set the owner */ |
|
if (scanner->sheet) { |
|
synctex_node_p last_sheet = scanner->sheet; |
|
synctex_node_p next_sheet = NULL; |
|
while ((next_sheet = __synctex_tree_sibling(last_sheet))) { |
|
last_sheet = next_sheet; |
|
} |
|
__synctex_tree_set_sibling(last_sheet,node); /* sheets have no parent */ |
|
} else { |
|
scanner->sheet = node; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_free_node(node); |
|
} |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
/** |
|
* - requirement: scanner != NULL |
|
*/ |
|
static synctex_ns_s _synctex_parse_new_form(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_form(scanner))) { |
|
if ( |
|
SYNCTEX_DECODE_FAILED(node,tag)) { |
|
_synctex_error("Bad sheet record."); |
|
} else if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of form."); |
|
} else { |
|
/* Now set the owner */ |
|
if (scanner->form) { |
|
synctex_node_p last_form = scanner->form; |
|
synctex_node_p next_form = NULL; |
|
while ((next_form = __synctex_tree_sibling(last_form))) { |
|
last_form = next_form; |
|
} |
|
__synctex_tree_set_sibling(last_form,node); |
|
} else { |
|
scanner->form = node; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_free_node(node); |
|
} |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
# define SYNCTEX_SHOULD_DECODE_FAILED(NODE,WHAT) \ |
|
(_synctex_data_has_##WHAT(NODE) &&(_synctex_data_decode_##WHAT(NODE)<SYNCTEX_STATUS_OK)) |
|
# define SYNCTEX_SHOULD_DECODE_FAILED_V(NODE,WHAT) \ |
|
(_synctex_data_has_##WHAT(NODE) &&(_synctex_data_decode_##WHAT##_v(NODE)<SYNCTEX_STATUS_OK)) |
|
|
|
static synctex_status_t _synctex_data_decode_tlchvwhd(synctex_node_p node) { |
|
return SYNCTEX_SHOULD_DECODE_FAILED(node,tag) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED(node,line) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED(node,column) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED(node,h) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED_V(node,v) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED(node,width) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED(node,height) |
|
|| SYNCTEX_SHOULD_DECODE_FAILED(node,depth); |
|
} |
|
static synctex_ns_s _synctex_parse_new_vbox(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_vbox(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad vbox record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of vbox."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
SYNCTEX_INLINE static synctex_node_p __synctex_node_make_friend_tlc(synctex_node_p node); |
|
static synctex_ns_s _synctex_parse_new_hbox(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_hbox(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad hbox record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of hbox."); |
|
goto out; |
|
} |
|
if (_synctex_setup_visible_hbox(node)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Unexpected error (_synctex_parse_new_hbox)."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_void_vbox(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_void_vbox(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad void vbox record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_void_hbox(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_void_hbox(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad void hbox record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_kern(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_kern(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad kern record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_glue(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_glue(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad glue record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_rule(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_rule(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad rule record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_math(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_math(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad math record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
static synctex_ns_s _synctex_parse_new_boundary(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_boundary(scanner))) { |
|
if (_synctex_data_decode_tlchvwhd(node)) { |
|
_synctex_error("Bad boundary record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
SYNCTEX_INLINE static synctex_ns_s _synctex_parse_new_ref(synctex_scanner_p scanner) { |
|
synctex_node_p node; |
|
if ((node = _synctex_new_ref(scanner))) { |
|
if (SYNCTEX_DECODE_FAILED(node,tag) |
|
|| SYNCTEX_DECODE_FAILED(node,h) |
|
|| SYNCTEX_DECODE_FAILED_V(node,v)) { |
|
_synctex_error("Bad form ref record."); |
|
_synctex_next_line(scanner); |
|
out: |
|
_synctex_free_node(node); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
goto out; |
|
} |
|
return (synctex_ns_s){node,SYNCTEX_STATUS_OK}; |
|
} |
|
_synctex_next_line(scanner); |
|
return (synctex_ns_s){NULL,SYNCTEX_STATUS_ERROR}; |
|
} |
|
# undef SYNCTEX_DECODE_FAILED |
|
# undef SYNCTEX_DECODE_FAILED_V |
|
|
|
SYNCTEX_INLINE static synctex_point_s _synctex_data_point(synctex_node_p node); |
|
SYNCTEX_INLINE static synctex_point_s _synctex_data_point_V(synctex_node_p node); |
|
SYNCTEX_INLINE static synctex_point_s _synctex_data_set_point(synctex_node_p node, synctex_point_s point); |
|
SYNCTEX_INLINE static synctex_box_s _synctex_data_box(synctex_node_p node); |
|
SYNCTEX_INLINE static synctex_box_s _synctex_data_xob(synctex_node_p node); |
|
SYNCTEX_INLINE static synctex_box_s _synctex_data_box_V(synctex_node_p node); |
|
|
|
SYNCTEX_INLINE static synctex_node_p _synctex_input_register_line(synctex_node_p input,synctex_node_p node) { |
|
if (node && _synctex_data_tag(input) != _synctex_data_tag(node)) { |
|
input = synctex_scanner_input_with_tag(node->class->scanner,_synctex_data_tag(node)); |
|
} |
|
if (_synctex_data_line(node)>_synctex_data_line(input)) { |
|
_synctex_data_set_line(input,_synctex_data_line(node)); |
|
} |
|
return input; |
|
} |
|
/** |
|
* Scan sheets, forms and input records. |
|
* - parameter scanner: owning scanner |
|
* - returns: status |
|
*/ |
|
static synctex_status_t __synctex_parse_sfi(synctex_scanner_p scanner) { |
|
synctex_status_t status = SYNCTEX_STATUS_OK; |
|
synctex_zs_s zs = {0,0}; |
|
synctex_ns_s input = SYNCTEX_NS_NULL; |
|
synctex_node_p sheet = NULL; |
|
synctex_node_p form = NULL; |
|
synctex_node_p parent = NULL; |
|
synctex_node_p child = NULL; |
|
synctex_ns_s ns = SYNCTEX_NS_NULL; |
|
int form_depth = 0; |
|
int ignored_form_depth = 0; |
|
synctex_bool_t try_input = synctex_YES; |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark MAIN LOOP |
|
# endif |
|
main_loop: |
|
status = SYNCTEX_STATUS_OK; |
|
sheet = form = parent = child = NULL; |
|
# define SYNCTEX_START_SCAN(WHAT)\ |
|
(*SYNCTEX_CUR == SYNCTEX_CHAR_##WHAT) |
|
if (SYNCTEX_CUR<SYNCTEX_END) { |
|
if (SYNCTEX_START_SCAN(BEGIN_FORM)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN FORM |
|
# endif |
|
scan_form: |
|
ns = _synctex_parse_new_form(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
++form_depth; |
|
if (_synctex_tree_parent(form)) { |
|
/* This form is already being parsed */ |
|
++ignored_form_depth; |
|
goto ignore_loop; |
|
} |
|
_synctex_tree_set_parent(ns.node,form); |
|
form = ns.node; |
|
parent = form; |
|
child = NULL; |
|
goto content_loop; |
|
} |
|
if (form || sheet) { |
|
goto content_loop; |
|
} |
|
try_input = synctex_YES; |
|
goto main_loop; |
|
} else if (SYNCTEX_START_SCAN(BEGIN_SHEET)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN SHEET |
|
# endif |
|
try_input = synctex_YES; |
|
ns = _synctex_parse_new_sheet(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
sheet = ns.node; |
|
parent = sheet; |
|
goto content_loop; |
|
} |
|
goto main_loop; |
|
} else if (SYNCTEX_START_SCAN(ANCHOR)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN ANCHOR |
|
# endif |
|
scan_anchor: |
|
++SYNCTEX_CUR; |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing anchor."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
if (form || sheet) { |
|
goto content_loop; |
|
} |
|
try_input = synctex_YES; |
|
goto main_loop; |
|
} else if (SYNCTEX_START_SCAN(ANCHOR)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN COMMENT |
|
# endif |
|
++SYNCTEX_CUR; |
|
_synctex_next_line(scanner); |
|
try_input = synctex_YES; |
|
goto main_loop; |
|
} else if (try_input) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN INPUT |
|
# endif |
|
try_input = synctex_NO; |
|
do { |
|
input = __synctex_parse_new_input(scanner); |
|
} while (input.status == SYNCTEX_STATUS_OK); |
|
goto main_loop; |
|
} |
|
status = _synctex_match_string(scanner,"Postamble:"); |
|
if (status==SYNCTEX_STATUS_OK) { |
|
scanner->flags.postamble = 1; |
|
return status; |
|
} |
|
status = _synctex_next_line(scanner); |
|
if (status<SYNCTEX_STATUS_OK) { |
|
return status; |
|
} |
|
} |
|
/* At least 1 more character */ |
|
zs = _synctex_buffer_get_available_size(scanner,1); |
|
if (zs.size == 0){ |
|
_synctex_error("Uncomplete synctex file, postamble missing."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
goto main_loop; |
|
/* Unreachable. */ |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark IGNORE LOOP |
|
# endif |
|
ignore_loop: |
|
ns = SYNCTEX_NS_NULL; |
|
if (SYNCTEX_CUR<SYNCTEX_END) { |
|
if (SYNCTEX_START_SCAN(BEGIN_FORM)) { |
|
++ignored_form_depth; |
|
} else if (SYNCTEX_START_SCAN(END_FORM)) { |
|
--ignored_form_depth; |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Uncomplete container."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
} else { |
|
zs = _synctex_buffer_get_available_size(scanner,1); |
|
if (zs.size == 0){ |
|
_synctex_error("Uncomplete synctex file, postamble missing."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
} |
|
if (ignored_form_depth) { |
|
goto ignore_loop; |
|
} else { |
|
goto content_loop; |
|
} |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark CONTENT LOOP |
|
# endif |
|
content_loop: |
|
/* Either in a form, a sheet or a box. |
|
* - in a sheet, "{" is not possible, only boxes and "}" at top level. |
|
* - in a form, "{" is not possible, only boxes, "<" and ">" at top level. |
|
* - in a box, the unique possibility is '<', '[', '(' or ">". |
|
* We still keep the '(' for a sheet, because that dos not cost too much. |
|
* We must also consider void boxes as children. |
|
*/ |
|
/* forms are everywhere */ |
|
ns = SYNCTEX_NS_NULL; |
|
#ifdef SYNCTEX_VERBOSE |
|
synctex_scanner_set_display_switcher(scanner,-1); |
|
printf("NEW CONTENT LOOP\n"); |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
synctex_node_display(sheet); |
|
#endif |
|
#endif |
|
if (SYNCTEX_CUR<SYNCTEX_END) { |
|
if (SYNCTEX_START_SCAN(BEGIN_FORM)) { |
|
goto scan_form; |
|
} else if (SYNCTEX_START_SCAN(BEGIN_VBOX)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN VBOX |
|
# endif |
|
ns = _synctex_parse_new_vbox(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
parent = ns.node; |
|
child = _synctex_tree_last(parent); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(parent); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,parent); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(END_VBOX)) { |
|
if (synctex_node_type(parent) == synctex_node_type_vbox) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN XOBV |
|
# endif |
|
++SYNCTEX_CUR; |
|
if (NULL == _synctex_tree_child(parent) && !form) { |
|
/* only void v boxes are friends */ |
|
_synctex_node_make_friend_tlc(parent); |
|
} |
|
child = parent; |
|
parent = _synctex_tree_parent(child); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Uncomplete container."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(BEGIN_HBOX)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN HBOX |
|
# endif |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
synctex_charindex_t char_index = (synctex_charindex_t)(scanner->reader->charindex_offset+SYNCTEX_CUR-SYNCTEX_START); |
|
synctex_lineindex_t line_index = scanner->reader->line_number; |
|
# endif |
|
ns = _synctex_parse_new_hbox(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
parent = ns.node; |
|
/* add a box boundary node at the start */ |
|
if ((child = _synctex_new_box_bdry(scanner))) { |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
child->line_index=line_index; |
|
child->char_index=char_index; |
|
# endif |
|
_synctex_node_set_child(parent,child); |
|
_synctex_data_set_tag(child,_synctex_data_tag(parent)); |
|
_synctex_data_set_line(child,_synctex_data_line(parent)); |
|
_synctex_data_set_h(child,_synctex_data_h(parent)); |
|
_synctex_data_set_v(child,_synctex_data_v(parent)); |
|
if (!form) { |
|
__synctex_node_make_friend_tlc(child); |
|
} |
|
} else { |
|
_synctex_error("Can't create box bdry record."); |
|
} |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(parent); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,parent); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(END_HBOX)) { |
|
if (synctex_node_type(parent) == synctex_node_type_hbox) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN XOBH |
|
# endif |
|
++SYNCTEX_CUR; |
|
{ |
|
/* setting the next horizontal box at the end ensures |
|
* that a child is recorded before any of its ancestors. |
|
*/ |
|
if (form == NULL /* && sheet != NULL*/ ) { |
|
_synctex_tree_set_next_hbox(parent,_synctex_tree_next_hbox(sheet)); |
|
_synctex_tree_set_next_hbox(sheet,parent); |
|
} |
|
/* Update the mean line number */ |
|
synctex_node_p node = _synctex_tree_child(parent); |
|
synctex_node_p sibling = NULL; |
|
/* Ignore the first node (a box_bdry) */ |
|
if (node && (node = __synctex_tree_sibling(node))) { |
|
unsigned int node_weight = 0; |
|
unsigned int cumulated_line_numbers = 0; |
|
do { |
|
if (synctex_node_type(node)==synctex_node_type_hbox) { |
|
if (_synctex_data_weight(node)) { |
|
node_weight += _synctex_data_weight(node); |
|
cumulated_line_numbers += _synctex_data_mean_line(node)*_synctex_data_weight(node); |
|
} else { |
|
++node_weight; |
|
cumulated_line_numbers += _synctex_data_mean_line(node); |
|
} |
|
} else { |
|
++node_weight; |
|
cumulated_line_numbers += synctex_node_line(node); |
|
} |
|
} while ((node = __synctex_tree_sibling(node))); |
|
_synctex_data_set_mean_line(parent,(cumulated_line_numbers + node_weight/2)/node_weight); |
|
_synctex_data_set_weight(parent,node_weight); |
|
} else { |
|
_synctex_data_set_mean_line(parent,_synctex_data_line(parent)); |
|
_synctex_data_set_weight(parent,1); |
|
} |
|
if ((sibling = _synctex_new_box_bdry(scanner))) { |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
sibling->line_index=child->line_index; |
|
sibling->char_index=child->char_index; |
|
# endif |
|
_synctex_node_set_sibling(child,sibling); |
|
{ |
|
synctex_node_p N = child; |
|
while (synctex_node_type(N) == synctex_node_type_ref) { |
|
N = _synctex_tree_arg_sibling(N); |
|
} |
|
_synctex_data_set_tag(sibling,_synctex_data_tag(N)); |
|
_synctex_data_set_line(sibling,_synctex_data_line(N)); |
|
} |
|
_synctex_data_set_h(sibling,_synctex_data_h_V(parent)+_synctex_data_width_V(parent)); |
|
_synctex_data_set_v(sibling,_synctex_data_v_V(parent)); |
|
child = sibling; |
|
} else { |
|
_synctex_error("Can't create box bdry record."); |
|
} |
|
sibling = _synctex_tree_child(parent); |
|
_synctex_data_set_point(sibling,_synctex_data_point_V(parent)); |
|
child = parent; |
|
parent = _synctex_tree_parent(child); |
|
_synctex_make_hbox_contain_box(parent,_synctex_data_box_V(child)); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
} |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Uncomplete container."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(VOID_VBOX)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN VOID VBOX |
|
# endif |
|
ns = _synctex_parse_new_void_vbox(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(VOID_HBOX)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN VOID HBOX |
|
# endif |
|
ns = _synctex_parse_new_void_hbox(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (_synctex_data_width(ns.node)<0) { |
|
printf("Negative width\n"); |
|
} |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
_synctex_make_hbox_contain_box(parent,_synctex_data_box(child)); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(KERN)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN KERN |
|
# endif |
|
ns = _synctex_parse_new_kern(scanner); |
|
/* continue_scan: */ |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
if (!form) { |
|
__synctex_node_make_friend_tlc(child); |
|
} |
|
_synctex_make_hbox_contain_box(parent,_synctex_data_xob(child)); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(GLUE)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN GLUE |
|
# endif |
|
ns = _synctex_parse_new_glue(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
if (!form) { |
|
__synctex_node_make_friend_tlc(child); |
|
} |
|
_synctex_make_hbox_contain_point(parent,_synctex_data_point(child)); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(RULE)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN RULE |
|
# endif |
|
ns = _synctex_parse_new_rule(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
if (!form) { |
|
__synctex_node_make_friend_tlc(child); |
|
} |
|
/* Rules are sometimes far too big |
|
_synctex_make_hbox_contain_box(parent,_synctex_data_box(child)); |
|
*/ |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(MATH)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN MATH |
|
# endif |
|
ns = _synctex_parse_new_math(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
if (!form) { |
|
__synctex_node_make_friend_tlc(child); |
|
} |
|
_synctex_make_hbox_contain_point(parent,_synctex_data_point(child)); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(FORM_REF)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN FORM REF |
|
# endif |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
synctex_node_display(parent); |
|
synctex_node_display(child); |
|
#endif |
|
ns = _synctex_parse_new_ref(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
if (form) { |
|
if (scanner->ref_in_form) { |
|
synctex_tree_set_friend(child,scanner->ref_in_form); |
|
} |
|
scanner->ref_in_form = child; |
|
} else { |
|
if (scanner->ref_in_sheet) { |
|
synctex_tree_set_friend(child,scanner->ref_in_sheet); |
|
} |
|
scanner->ref_in_sheet = child; |
|
} |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(BOUNDARY)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN BOUNDARY |
|
# endif |
|
ns = _synctex_parse_new_boundary(scanner); |
|
if (ns.status == SYNCTEX_STATUS_OK) { |
|
if (child) { |
|
_synctex_node_set_sibling(child,ns.node); |
|
} else { |
|
_synctex_node_set_child(parent,ns.node); |
|
} |
|
child = ns.node; |
|
if (!form) { |
|
__synctex_node_make_friend_tlc(child); |
|
} |
|
_synctex_make_hbox_contain_point(parent,_synctex_data_point(child)); |
|
# ifdef SYNCTEX_VERBOSE |
|
synctex_node_log(child); |
|
# endif |
|
input.node = _synctex_input_register_line(input.node,child); |
|
goto content_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(CHARACTER)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN CHARACTER |
|
# endif |
|
++SYNCTEX_CUR; |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of container."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
goto content_loop; |
|
} else if (SYNCTEX_START_SCAN(ANCHOR)) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN ANCHOR |
|
# endif |
|
goto scan_anchor; |
|
} else if (SYNCTEX_START_SCAN(END_SHEET)) { |
|
if (sheet && parent == sheet) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN TEEHS |
|
# endif |
|
++SYNCTEX_CUR; |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing anchor."); |
|
} |
|
parent = sheet = NULL; |
|
goto main_loop; |
|
} |
|
} else if (SYNCTEX_START_SCAN(END_FORM)) { |
|
if (parent == form && form_depth > 0) { |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark + SCAN MROF |
|
# endif |
|
++SYNCTEX_CUR; |
|
--form_depth; |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK |
|
&& (form_depth || sheet)) { |
|
_synctex_error("Missing end of container."); |
|
return SYNCTEX_STATUS_ERROR; |
|
} |
|
if ((parent = _synctex_tree_parent(form))) { |
|
_synctex_tree_reset_parent(form); |
|
child = form; |
|
form = parent; |
|
goto content_loop; |
|
} else if (sheet) { |
|
form = NULL; |
|
parent = sheet; |
|
child = synctex_node_last_sibling(child); |
|
goto content_loop; |
|
} |
|
goto main_loop; |
|
} |
|
} |
|
_synctex_error("Ignored record <%.20s...>(line %i)\n",SYNCTEX_CUR, scanner->reader->line_number+1); |
|
if (_synctex_next_line(scanner)<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Missing end of sheet/form."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
goto content_loop; |
|
} |
|
zs = _synctex_buffer_get_available_size(scanner,1); |
|
if (zs.size == 0){ |
|
_synctex_error("Uncomplete synctex file, postamble missing."); |
|
SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); |
|
} |
|
goto content_loop; |
|
} |
|
/** |
|
* Replace ref in its tree hierarchy by a single box |
|
* proxy to the contents of the associated form. |
|
* - argument ref: a ref node with no friend |
|
* - return the proxy created. |
|
* - note: Does nothing if ref is not owned. |
|
* - note: On return, ref will have no parent nor sibling. |
|
* The caller is responsible for releasing ref. |
|
* - note: this is where root proxies are created. |
|
* - note: the target of the root proxy is the content |
|
* of a form. |
|
*/ |
|
SYNCTEX_INLINE static synctex_ns_s __synctex_replace_ref(synctex_node_p ref) { |
|
synctex_ns_s ns = {NULL,SYNCTEX_STATUS_OK}; |
|
synctex_node_p parent; |
|
if ((parent = _synctex_tree_parent(ref))) { |
|
synctex_node_p sibling = __synctex_tree_reset_sibling(ref); |
|
synctex_node_p arg_sibling = synctex_node_arg_sibling(ref); |
|
/* arg_sibling != NULL because the child of a box |
|
* is always a box boundary, not a ref. */ |
|
synctex_node_p target = synctex_form_content(ref->class->scanner, _synctex_data_tag(ref)); |
|
/* The target is a single node (box) |
|
* with children and no siblings. */ |
|
if ((ns.node = __synctex_new_proxy_from_ref_to(ref, target))) { |
|
/* Insert this proxy instead of ref. */ |
|
_synctex_node_set_sibling(arg_sibling,ns.node); |
|
/* Then append the original sibling of ref. */ |
|
_synctex_node_set_sibling(ns.node,sibling); |
|
# if defined(SYNCTEX_USE_CHARINDEX) |
|
if (synctex_node_type(sibling) == synctex_node_type_box_bdry) { |
|
/* The sibling is the last box boundary |
|
* which may have a less accurate information */ |
|
sibling->char_index = arg_sibling->char_index; |
|
sibling->line_index = arg_sibling->line_index; |
|
} |
|
#endif |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("! Ref replacement:\n"); |
|
synctex_node_log(ref); |
|
synctex_node_display(synctex_node_sibling(ref)); |
|
#endif |
|
} else /* simply remove ref */ { |
|
_synctex_tree_set_sibling(arg_sibling,sibling); |
|
} |
|
__synctex_tree_reset_parent(ref); |
|
} else { |
|
_synctex_error("! Missing parent in __synctex_replace_ref. " |
|
"Please report."); |
|
ns.status = SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
return ns; |
|
} |
|
/** |
|
* - argument ref: is the starting point of a linked list |
|
* of refs. The link is made through the friend field. |
|
* - returns: the status and the list of all the proxies |
|
* created. The link is made through the friend field. |
|
* - note: All refs are freed |
|
*/ |
|
SYNCTEX_INLINE static synctex_ns_s _synctex_post_process_ref(synctex_node_p ref) { |
|
synctex_ns_s ns = {NULL, SYNCTEX_STATUS_OK}; |
|
while (ref) { |
|
synctex_node_p next_ref = _synctex_tree_reset_friend(ref); |
|
synctex_ns_s sub_ns = __synctex_replace_ref(ref); |
|
if (sub_ns.status < ns.status) { |
|
ns.status = sub_ns.status; |
|
} else { |
|
/* Insert all the created proxies in the list |
|
* sub_ns.node is the last friend, |
|
*/ |
|
synctex_tree_set_friend(sub_ns.node,ns.node); |
|
ns.node = sub_ns.node; |
|
} |
|
synctex_node_free(ref); |
|
ref = next_ref; |
|
} |
|
return ns; |
|
} |
|
typedef synctex_node_p (* synctex_processor_f)(synctex_node_p node); |
|
/** |
|
* Apply the processor f to the tree hierarchy rooted at proxy. |
|
* proxy has replaced a form ref, no children yet. |
|
* As a side effect all the hierarchy of nodes will be created. |
|
*/ |
|
SYNCTEX_INLINE static synctex_status_t _synctex_post_process_proxy(synctex_node_p proxy, synctex_processor_f f) { |
|
while(proxy) { |
|
synctex_node_p next_proxy = _synctex_tree_friend(proxy); |
|
synctex_node_p halt = __synctex_tree_sibling(proxy); |
|
/* if proxy is the last sibling, halt is NULL. |
|
* Find what should be a next node, |
|
* without creating new nodes. */ |
|
if (!halt) { |
|
synctex_node_p parent = _synctex_tree_parent(proxy); |
|
halt = __synctex_tree_sibling(parent); |
|
while (!halt && parent) { |
|
parent = _synctex_tree_parent(parent); |
|
halt = __synctex_tree_sibling(parent); |
|
} |
|
} |
|
do { |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("POST PROCESSING %s\n",_synctex_node_abstract(proxy)); |
|
{ |
|
int i,j = 0; |
|
for (i=0;i<proxy->class->scanner->number_of_lists;++i) { |
|
synctex_node_p N = proxy->class->scanner->lists_of_friends[i]; |
|
do { |
|
if (N==proxy) { |
|
++j; |
|
printf("%s",_synctex_node_abstract(N)); |
|
} |
|
} while ((N = _synctex_tree_friend(N))); |
|
} |
|
if (j) { |
|
printf("\nBeforehand %i match\n",j); |
|
} |
|
} |
|
#endif |
|
f(proxy); |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
{ |
|
int i,j = 0; |
|
for (i=0;i<proxy->class->scanner->number_of_lists;++i) { |
|
synctex_node_p N = proxy->class->scanner->lists_of_friends[i]; |
|
do { |
|
if (N==proxy) { |
|
++j; |
|
printf("%s",_synctex_node_abstract(N)); |
|
} |
|
} while ((N = _synctex_tree_friend(N))); |
|
} |
|
if (j) { |
|
printf("\n%i match\n",j); |
|
} |
|
} |
|
#endif |
|
/* Side effect: create the hierarchy on the fly */ |
|
proxy = synctex_node_next(proxy); /* Change is here */ |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
if (proxy) { |
|
int i,j = 0; |
|
for (i=0;i<proxy->class->scanner->number_of_lists;++i) { |
|
synctex_node_p N = proxy->class->scanner->lists_of_friends[i]; |
|
do { |
|
if (N==proxy) { |
|
++j; |
|
printf("%s",_synctex_node_abstract(N)); |
|
} |
|
} while ((N = _synctex_tree_friend(N))); |
|
} |
|
if (j) { |
|
printf("\nnext %i match\n",j); |
|
} |
|
} |
|
#endif |
|
} while (proxy && proxy != halt); |
|
proxy = next_proxy; |
|
} |
|
return SYNCTEX_STATUS_OK; |
|
} |
|
/** |
|
* Replace all the form refs by root box proxies. |
|
* Create the node hierarchy and update the friends. |
|
* On entry, the refs are collected as a friend list |
|
* in either a form or a sheet |
|
* - parameter: the owning scanner |
|
*/ |
|
SYNCTEX_INLINE static synctex_status_t _synctex_post_process(synctex_scanner_p scanner) { |
|
synctex_status_t status = SYNCTEX_STATUS_OK; |
|
synctex_ns_s ns = {NULL,SYNCTEX_STATUS_NOT_OK}; |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("! entering _synctex_post_process.\n"); |
|
synctex_node_display(scanner->sheet); |
|
synctex_node_display(scanner->form); |
|
#endif |
|
/* replace form refs inside forms by box proxies */ |
|
ns = _synctex_post_process_ref(scanner->ref_in_form); |
|
scanner->ref_in_form = NULL;/* it was just released */ |
|
if (ns.status<status) { |
|
status = ns.status; |
|
} |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("! ref replaced in form _synctex_post_process.\n"); |
|
synctex_node_display(scanner->form); |
|
#endif |
|
/* Create all the form proxy nodes on the fly. |
|
* ns.node is the root of the list of |
|
* newly created proxies. |
|
* There might be a problem with cascading proxies. |
|
* In order to be properly managed, the data must |
|
* be organized in the right way. |
|
* The inserted form must be defined before |
|
* the inserting one. *TeX will take care of that. */ |
|
ns.status = _synctex_post_process_proxy(ns.node,&_synctex_tree_reset_friend); |
|
if (ns.status<status) { |
|
status = ns.status; |
|
} |
|
/* replace form refs inside sheets by box proxies */ |
|
ns = _synctex_post_process_ref(scanner->ref_in_sheet); |
|
if (ns.status<status) { |
|
status = ns.status; |
|
} |
|
scanner->ref_in_sheet = NULL; |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("! ref replaced in sheet _synctex_post_process.\n"); |
|
synctex_node_display(scanner->sheet); |
|
#endif |
|
#if 0 |
|
{ |
|
int i; |
|
for (i=0;i<scanner->number_of_lists;++i) { |
|
synctex_node_p P = ns.node; |
|
do { |
|
synctex_node_p N = scanner->lists_of_friends[i]; |
|
do { |
|
if (P == N) { |
|
printf("Already registered.\n"); |
|
synctex_node_display(N); |
|
break; |
|
} |
|
} while ((N = _synctex_tree_friend(N))); |
|
} while((P = _synctex_tree_friend(P))); |
|
} |
|
} |
|
#endif |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>10000 |
|
{ |
|
int i; |
|
for (i=0;i<scanner->number_of_lists;++i) { |
|
synctex_node_p P = scanner->lists_of_friends[i]; |
|
int j = 0; |
|
while (P) { |
|
++j; |
|
synctex_node_log(P); |
|
P = _synctex_tree_friend(P); |
|
} |
|
if (j) { |
|
printf("friends %i -> # %i\n",i,j); |
|
} |
|
} |
|
} |
|
#endif |
|
ns.status = _synctex_post_process_proxy(ns.node,&__synctex_proxy_make_friend_and_next_hbox); |
|
if (ns.status<status) { |
|
status = ns.status; |
|
} |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
printf("! exiting _synctex_post_process.\n"); |
|
synctex_node_display(scanner->sheet); |
|
synctex_node_display(scanner->form); |
|
printf("! display all.\n"); |
|
synctex_node_display(scanner->sheet); |
|
synctex_node_display(scanner->form); |
|
#endif |
|
return status; |
|
} |
|
/* Used when parsing the synctex file |
|
*/ |
|
static synctex_status_t _synctex_scan_content(synctex_scanner_p scanner) { |
|
scanner->reader->lastv = -1; |
|
synctex_status_t status = 0; |
|
if (NULL == scanner) { |
|
return SYNCTEX_STATUS_BAD_ARGUMENT; |
|
} |
|
/* 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; |
|
} |
|
status = __synctex_parse_sfi(scanner); |
|
if (status == SYNCTEX_STATUS_OK) { |
|
status = _synctex_post_process(scanner); |
|
} |
|
return status; |
|
} |
|
synctex_scanner_p synctex_scanner_new() { |
|
synctex_scanner_p scanner =(synctex_scanner_p)_synctex_malloc(sizeof(synctex_scanner_s)); |
|
if (scanner) { |
|
if (!(scanner->reader = _synctex_malloc(sizeof(synctex_reader_s)))) { |
|
_synctex_free(scanner); |
|
return NULL; |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# endif |
|
# 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(input); |
|
DEFINE_synctex_scanner_class(sheet); |
|
DEFINE_synctex_scanner_class(form); |
|
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(rule); |
|
DEFINE_synctex_scanner_class(math); |
|
DEFINE_synctex_scanner_class(boundary); |
|
DEFINE_synctex_scanner_class(box_bdry); |
|
DEFINE_synctex_scanner_class(ref); |
|
DEFINE_synctex_scanner_class(proxy_hbox); |
|
DEFINE_synctex_scanner_class(proxy_vbox); |
|
DEFINE_synctex_scanner_class(proxy); |
|
DEFINE_synctex_scanner_class(proxy_last); |
|
DEFINE_synctex_scanner_class(handle); |
|
/* set up the lists of friends */ |
|
scanner->number_of_lists = 1024; |
|
scanner->lists_of_friends = (synctex_node_r)_synctex_malloc(scanner->number_of_lists*sizeof(synctex_node_p)); |
|
if (NULL == scanner->lists_of_friends) { |
|
synctex_scanner_free(scanner); |
|
_synctex_error("malloc:2"); |
|
return NULL; |
|
} |
|
scanner->display_switcher = 100; |
|
scanner->display_prompt = (char *)_synctex_display_prompt+strlen(_synctex_display_prompt)-1; |
|
} |
|
return scanner; |
|
} |
|
/* Where the synctex scanner is created. */ |
|
synctex_scanner_p synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse) { |
|
synctex_scanner_p scanner = synctex_scanner_new(); |
|
if (NULL == scanner) { |
|
_synctex_error("malloc problem"); |
|
return NULL; |
|
} |
|
if ((scanner->reader = synctex_reader_init_with_output_file(scanner->reader, output, build_directory))) { |
|
return parse? synctex_scanner_parse(scanner):scanner; |
|
} |
|
#if defined(SYNCTEX_DEBUG) |
|
_synctex_error("No file?"); |
|
#endif |
|
synctex_scanner_free(scanner); |
|
return NULL; |
|
} |
|
|
|
/* The scanner destructor |
|
*/ |
|
int synctex_scanner_free(synctex_scanner_p scanner) { |
|
int node_count = 0; |
|
if (scanner) { |
|
if (scanner->reader && SYNCTEX_FILE) { |
|
gzclose(SYNCTEX_FILE); |
|
SYNCTEX_FILE = NULL; |
|
} |
|
synctex_node_free(scanner->sheet); |
|
synctex_node_free(scanner->form); |
|
synctex_node_free(scanner->input); |
|
synctex_reader_free(scanner->reader); |
|
SYNCTEX_SCANNER_FREE_HANDLE(scanner); |
|
synctex_iterator_free(scanner->iterator); |
|
free(scanner->output_fmt); |
|
free(scanner->lists_of_friends); |
|
#if defined SYNCTEX_USE_NODE_COUNT && SYNCTEX_USE_NODE_COUNT>0 |
|
node_count = scanner->node_count; |
|
#endif |
|
free(scanner); |
|
} |
|
return node_count; |
|
} |
|
|
|
/* Where the synctex scanner parses the contents of the file. */ |
|
synctex_scanner_p synctex_scanner_parse(synctex_scanner_p 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 overridden by the real life value */ |
|
scanner->x_offset = scanner->y_offset = 6.027e23f; |
|
scanner->reader->line_number = 1; |
|
|
|
/* TODO: cleanup |
|
* In some (all?) cases SYNCTEX_START is already initialized |
|
* in synctex_reader_init_with_output_file(). Much of the |
|
* following code seems like a duplicate and is perhaps a |
|
* candidate for deletion. To be on the safe side though, we |
|
* keep it for now and just free() any prior malloc() if |
|
* existing. */ |
|
_synctex_free(SYNCTEX_START); |
|
|
|
SYNCTEX_START = (char *)malloc(SYNCTEX_BUFFER_SIZE+1); /* one more character for null termination */ |
|
if (NULL == SYNCTEX_START) { |
|
_synctex_error("! malloc error in synctex_scanner_parse."); |
|
bailey: |
|
#ifdef SYNCTEX_DEBUG |
|
return scanner; |
|
#else |
|
synctex_scanner_free(scanner); |
|
return NULL; |
|
#endif |
|
} |
|
synctex_scanner_set_display_switcher(scanner, 1000); |
|
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->reader->charindex_offset = -SYNCTEX_BUFFER_SIZE; |
|
# endif |
|
status = _synctex_scan_preamble(scanner); |
|
if (status<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Bad preamble\n"); |
|
goto bailey; |
|
} |
|
status = _synctex_scan_content(scanner); |
|
if (status<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Bad content\n"); |
|
goto bailey; |
|
} |
|
status = _synctex_scan_postamble(scanner); |
|
if (status<SYNCTEX_STATUS_OK) { |
|
_synctex_error("Bad postamble. Ignored\n"); |
|
} |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
synctex_scanner_set_display_switcher(scanner, 100); |
|
synctex_node_display(scanner->sheet); |
|
synctex_node_display(scanner->form); |
|
#endif |
|
synctex_scanner_set_display_switcher(scanner, 1000); |
|
/* 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_p scanner){ |
|
return scanner?scanner->pre_x_offset:0; |
|
} |
|
int synctex_scanner_pre_y_offset(synctex_scanner_p scanner){ |
|
return scanner?scanner->pre_y_offset:0; |
|
} |
|
int synctex_scanner_x_offset(synctex_scanner_p scanner){ |
|
return scanner?scanner->x_offset:0; |
|
} |
|
int synctex_scanner_y_offset(synctex_scanner_p scanner){ |
|
return scanner?scanner->y_offset:0; |
|
} |
|
float synctex_scanner_magnification(synctex_scanner_p scanner){ |
|
return scanner?scanner->unit:1; |
|
} |
|
void synctex_scanner_display(synctex_scanner_p scanner) { |
|
if (NULL == scanner) { |
|
return; |
|
} |
|
printf("The scanner:\noutput:%s\noutput_fmt:%s\nversion:%i\n",scanner->reader->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_node_display(scanner->input); |
|
if (scanner->count<1000) { |
|
printf("The sheets:\n"); |
|
synctex_node_display(scanner->sheet); |
|
printf("The friends:\n"); |
|
if (scanner->lists_of_friends) { |
|
int i = scanner->number_of_lists; |
|
synctex_node_p 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_data_tag(node), |
|
_synctex_data_line(node) |
|
); |
|
node = _synctex_tree_friend(node); |
|
} |
|
} |
|
} |
|
} else { |
|
printf("SyncTeX Warning: Too many objects\n"); |
|
} |
|
} |
|
/* Public */ |
|
const char * synctex_scanner_get_name(synctex_scanner_p scanner,int tag) { |
|
synctex_node_p input = NULL; |
|
if (NULL == scanner) { |
|
return NULL; |
|
} |
|
if ((input = scanner->input)) {; |
|
do { |
|
if (tag == _synctex_data_tag(input)) { |
|
return (_synctex_data_name(input)); |
|
} |
|
} while((input = __synctex_tree_sibling(input))); |
|
} |
|
return NULL; |
|
} |
|
const char * synctex_node_get_name(synctex_node_p node) { |
|
if (node) { |
|
return synctex_scanner_get_name(node->class->scanner,_synctex_data_tag(node)); |
|
} |
|
return NULL; |
|
} |
|
|
|
static int _synctex_scanner_get_tag(synctex_scanner_p scanner,const char * name); |
|
static int _synctex_scanner_get_tag(synctex_scanner_p scanner,const char * name) { |
|
synctex_node_p input = NULL; |
|
if (NULL == scanner) { |
|
return 0; |
|
} |
|
if ((input = scanner->input)) { |
|
do { |
|
if (_synctex_is_equivalent_file_name(name,(_synctex_data_name(input)))) { |
|
return _synctex_data_tag(input); |
|
} |
|
} while((input = __synctex_tree_sibling(input))); |
|
} |
|
/* 2011 version */ |
|
name = _synctex_base_name(name); |
|
if ((input = scanner->input)) { |
|
do { |
|
if (_synctex_is_equivalent_file_name(name,_synctex_base_name(_synctex_data_name(input)))) { |
|
synctex_node_p other_input = input; |
|
while((other_input = __synctex_tree_sibling(other_input))) { |
|
if (_synctex_is_equivalent_file_name(name,_synctex_base_name(_synctex_data_name(other_input))) |
|
&& (strlen(_synctex_data_name(input))!=strlen(_synctex_data_name(other_input)) |
|
|| strncmp(_synctex_data_name(other_input),_synctex_data_name(input),strlen(_synctex_data_name(input))))) { |
|
/* There is a second possible candidate */ |
|
return 0; |
|
} |
|
} |
|
return _synctex_data_tag(input); |
|
} |
|
} while((input = __synctex_tree_sibling(input))); |
|
} |
|
return 0; |
|
} |
|
|
|
int synctex_scanner_get_tag(synctex_scanner_p 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->reader->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_p synctex_scanner_input(synctex_scanner_p scanner) { |
|
return scanner?scanner->input:NULL; |
|
} |
|
synctex_node_p synctex_scanner_input_with_tag(synctex_scanner_p scanner, int tag) { |
|
synctex_node_p input = scanner?scanner->input:NULL; |
|
while (_synctex_data_tag(input)!=tag) { |
|
if ((input = __synctex_tree_sibling(input))) { |
|
continue; |
|
} |
|
break; |
|
} |
|
return input; |
|
} |
|
const char * synctex_scanner_get_output_fmt(synctex_scanner_p scanner) { |
|
return NULL != scanner && scanner->output_fmt?scanner->output_fmt:""; |
|
} |
|
const char * synctex_scanner_get_output(synctex_scanner_p scanner) { |
|
return NULL != scanner && scanner->reader->output?scanner->reader->output:""; |
|
} |
|
const char * synctex_scanner_get_synctex(synctex_scanner_p scanner) { |
|
return NULL != scanner && scanner->reader->synctex?scanner->reader->synctex:""; |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Public node attributes |
|
# endif |
|
|
|
# define SYNCTEX_DEFINE_NODE_HVWHD(WHAT) \ |
|
int synctex_node_##WHAT(synctex_node_p node) { \ |
|
return (node && node->class->inspector->WHAT)? \ |
|
node->class->inspector->WHAT(node): 0; \ |
|
} |
|
# define SYNCTEX_DEFINE_PROXY_HV(WHAT) \ |
|
static int _synctex_proxy_##WHAT(synctex_proxy_p proxy) { \ |
|
synctex_node_p target = _synctex_tree_target(proxy); \ |
|
if (target) { \ |
|
return _synctex_data_##WHAT(proxy)+synctex_node_##WHAT(target); \ |
|
} else { \ |
|
return proxy? _synctex_data_##WHAT(proxy): 0; \ |
|
} \ |
|
} |
|
#define SYNCTEX_DEFINE_PROXY_TLCWVD(WHAT) \ |
|
static int _synctex_proxy_##WHAT(synctex_proxy_p proxy) { \ |
|
synctex_node_p target = _synctex_tree_target(proxy); \ |
|
return target? synctex_node_##WHAT(target): 0; \ |
|
} |
|
|
|
/** |
|
* The horizontal location of the node. |
|
* Idem for v, width, height and depth. |
|
* - parameter node: a node with geometrical information. |
|
* - returns: an integer. |
|
* - requires: every proxy node has a target. |
|
* - note: recursive call if the parameter has a proxy. |
|
* - author: JL |
|
*/ |
|
SYNCTEX_DEFINE_NODE_HVWHD(h) |
|
SYNCTEX_DEFINE_NODE_HVWHD(v) |
|
SYNCTEX_DEFINE_NODE_HVWHD(width) |
|
SYNCTEX_DEFINE_NODE_HVWHD(height) |
|
SYNCTEX_DEFINE_NODE_HVWHD(depth) |
|
SYNCTEX_DEFINE_PROXY_TLCWVD(tag) |
|
SYNCTEX_DEFINE_PROXY_TLCWVD(line) |
|
SYNCTEX_DEFINE_PROXY_TLCWVD(column) |
|
SYNCTEX_DEFINE_PROXY_HV(h) |
|
SYNCTEX_DEFINE_PROXY_HV(v) |
|
SYNCTEX_DEFINE_PROXY_TLCWVD(width) |
|
SYNCTEX_DEFINE_PROXY_TLCWVD(height) |
|
SYNCTEX_DEFINE_PROXY_TLCWVD(depth) |
|
|
|
/** |
|
* Whether the argument is a box, |
|
* either vertical or horizontal, |
|
* either void or not, |
|
* or a proxy to such a box. |
|
* - parameter NODE: of type synctex_node_p |
|
* - returns: yorn |
|
*/ |
|
|
|
SYNCTEX_INLINE static synctex_bool_t _synctex_node_is_box(synctex_node_p node) { |
|
return node && |
|
(node->class->type == synctex_node_type_hbox |
|
|| node->class->type == synctex_node_type_void_hbox |
|
|| node->class->type == synctex_node_type_vbox |
|
|| node->class->type == synctex_node_type_void_vbox |
|
|| _synctex_node_is_box(_synctex_tree_target(node))); |
|
} |
|
|
|
/** |
|
* Whether the argument is a handle. |
|
* Handles are similar to proxies because they have a target. |
|
* They are used for query results. |
|
* - parameter NODE: of type synctex_node_p |
|
* - returns: yorn |
|
*/ |
|
|
|
SYNCTEX_INLINE static synctex_bool_t _synctex_node_is_handle(synctex_node_p node) { |
|
return node && |
|
(node->class->type == synctex_node_type_handle); |
|
} |
|
|
|
/** |
|
* Resolves handle indirection. |
|
* - parameter node: of type synctex_node_p |
|
* - returns: node if it is not a handle, |
|
* its target otherwise. |
|
*/ |
|
|
|
SYNCTEX_INLINE static synctex_node_p _synctex_node_or_handle_target(synctex_node_p node) { |
|
return _synctex_node_is_handle(node)? |
|
_synctex_tree_target(node):node; |
|
} |
|
|
|
/** |
|
* Whether the argument is an hbox. |
|
* - parameter NODE: of type synctex_node_p |
|
* - returns: yorn |
|
*/ |
|
|
|
SYNCTEX_INLINE static synctex_bool_t _synctex_node_is_hbox(synctex_node_p node) { |
|
return node && |
|
(node->class->type == synctex_node_type_hbox |
|
|| node->class->type == synctex_node_type_void_hbox |
|
|| _synctex_node_is_hbox(_synctex_tree_target(node))); |
|
} |
|
|
|
/** |
|
* The horizontal location of the first box enclosing node. |
|
* - parameter node: a node with geometrical information. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
int synctex_node_box_h(synctex_node_p node) { |
|
if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { |
|
return synctex_node_h(node); |
|
} |
|
return 0; |
|
} |
|
/** |
|
* The vertical location of the first box enclosing node. |
|
* - parameter node: a node with geometrical information. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
int synctex_node_box_v(synctex_node_p node) { |
|
if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { |
|
return synctex_node_v(node); |
|
} |
|
return 0; |
|
} |
|
/** |
|
* The width of the first box enclosing node. |
|
* - parameter node: a node with geometrical information. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
int synctex_node_box_width(synctex_node_p node) { |
|
if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { |
|
return synctex_node_width(node); |
|
} |
|
return 0; |
|
} |
|
/** |
|
* The height of the first box enclosing node. |
|
* - parameter node: a node with geometrical information. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
int synctex_node_box_height(synctex_node_p node) { |
|
if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { |
|
return synctex_node_height(node); |
|
} |
|
return 0; |
|
} |
|
/** |
|
* The depth of the first box enclosing node. |
|
* - parameter node: a node with geometrical information. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
int synctex_node_box_depth(synctex_node_p node) { |
|
if (_synctex_node_is_box(node) || (node = _synctex_tree_parent(node))) { |
|
return synctex_node_depth(node); |
|
} |
|
return 0; |
|
} |
|
/** |
|
* The horizontal location of an hbox, corrected with contents. |
|
* - parameter node: an hbox node. |
|
* - returns: an integer, 0 if node is not an hbox or an hbox proxy. |
|
* - note: recursive call when node is an hbox proxy. |
|
* - author: JL |
|
*/ |
|
int synctex_node_hbox_h(synctex_node_p node) { |
|
switch(synctex_node_type(node)) { |
|
case synctex_node_type_hbox: |
|
return _synctex_data_h_V(node); |
|
case synctex_node_type_proxy_hbox: |
|
return _synctex_data_h(node)+synctex_node_hbox_h(_synctex_tree_target(node)); |
|
default: |
|
return 0; |
|
} |
|
} |
|
/** |
|
* The vertical location of an hbox, corrected with contents. |
|
* - parameter node: an hbox node. |
|
* - returns: an integer, 0 if node is not an hbox or an hbox proxy. |
|
* - note: recursive call when node is an hbox proxy. |
|
* - author: JL |
|
*/ |
|
int synctex_node_hbox_v(synctex_node_p node) { |
|
switch(synctex_node_type(node)) { |
|
case synctex_node_type_hbox: |
|
return _synctex_data_v_V(node); |
|
case synctex_node_type_proxy_hbox: |
|
return _synctex_data_v(node)+synctex_node_hbox_v(_synctex_tree_target(node)); |
|
default: |
|
return 0; |
|
} |
|
} |
|
/** |
|
* The width of an hbox, corrected with contents. |
|
* - parameter node: an hbox node, 0 if node is not an hbox or an hbox proxy. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
int synctex_node_hbox_width(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
node = target; |
|
} |
|
return synctex_node_type(node) == synctex_node_type_hbox? |
|
_synctex_data_width_V(node): 0; |
|
} |
|
/** |
|
* The height of an hbox, corrected with contents. |
|
* - parameter node: an hbox node. |
|
* - returns: an integer, 0 if node is not an hbox or an hbox proxy. |
|
* - author: JL |
|
*/ |
|
int synctex_node_hbox_height(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
node = target; |
|
} |
|
return synctex_node_type(node) == synctex_node_type_hbox? |
|
_synctex_data_height_V(node): 0; |
|
} |
|
/** |
|
* The depth of an hbox, corrected with contents. |
|
* - parameter node: an hbox node. |
|
* - returns: an integer, 0 if node is not an hbox or an hbox proxy. |
|
* - note: recursive call when node is an hbox proxy. |
|
* - author: JL |
|
*/ |
|
int synctex_node_hbox_depth(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
node = target; |
|
} |
|
return synctex_node_type(node) == synctex_node_type_hbox? |
|
_synctex_data_depth_V(node): 0; |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Public node visible attributes |
|
# endif |
|
|
|
#define SYNCTEX_VISIBLE_SIZE(node,s) \ |
|
(s)*node->class->scanner->unit |
|
#define SYNCTEX_VISIBLE_DISTANCE_h(node,d) \ |
|
((d)*node->class->scanner->unit+node->class->scanner->x_offset) |
|
#define SYNCTEX_VISIBLE_DISTANCE_v(node,d) \ |
|
((d)*node->class->scanner->unit+node->class->scanner->y_offset) |
|
static float __synctex_node_visible_h(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_DISTANCE_h(node,synctex_node_h(node)); |
|
} |
|
static float __synctex_node_visible_v(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_DISTANCE_v(node,synctex_node_v(node)); |
|
} |
|
static float __synctex_node_visible_width(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_SIZE(node,synctex_node_width(node)); |
|
} |
|
static float __synctex_node_visible_height(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_SIZE(node,synctex_node_height(node)); |
|
} |
|
static float __synctex_node_visible_depth(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_SIZE(node,synctex_node_depth(node)); |
|
} |
|
static float __synctex_proxy_visible_h(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_DISTANCE_h(node,synctex_node_h(node)); |
|
} |
|
static float __synctex_proxy_visible_v(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_DISTANCE_v(node,synctex_node_v(node)); |
|
} |
|
static float __synctex_proxy_visible_width(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
return __synctex_node_visible_width(target); |
|
} |
|
static float __synctex_proxy_visible_height(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
return __synctex_node_visible_height(target); |
|
} |
|
static float __synctex_proxy_visible_depth(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
return __synctex_node_visible_depth(target); |
|
} |
|
static float __synctex_kern_visible_h(synctex_noxy_p noxy) { |
|
int h = _synctex_data_h(noxy); |
|
int width = _synctex_data_width(noxy); |
|
return SYNCTEX_VISIBLE_DISTANCE_h(noxy, width>0?h-width:h); |
|
} |
|
static float __synctex_kern_visible_width(synctex_noxy_p noxy) { |
|
int width = _synctex_data_width(noxy); |
|
return SYNCTEX_VISIBLE_SIZE(noxy, width>0?width:-width); |
|
} |
|
static float __synctex_rule_visible_h(synctex_noxy_p noxy) { |
|
int h = _synctex_data_h(noxy); |
|
int width = _synctex_data_width(noxy); |
|
return SYNCTEX_VISIBLE_DISTANCE_h(noxy, width>0?h:h-width); |
|
} |
|
static float __synctex_rule_visible_width(synctex_noxy_p noxy) { |
|
int width = _synctex_data_width(noxy); |
|
return SYNCTEX_VISIBLE_SIZE(noxy, width>0?width:-width); |
|
} |
|
static float __synctex_rule_visible_v(synctex_noxy_p noxy) { |
|
return __synctex_node_visible_v(noxy); |
|
} |
|
static float __synctex_rule_visible_height(synctex_noxy_p noxy) { |
|
return __synctex_node_visible_height(noxy); |
|
} |
|
static float __synctex_rule_visible_depth(synctex_noxy_p noxy) { |
|
return __synctex_node_visible_depth(noxy); |
|
} |
|
|
|
/** |
|
* The horizontal location of node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_visible_h(synctex_node_p node){ |
|
return node? node->class->vispector->h(node): 0; |
|
} |
|
/** |
|
* The vertical location of node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_visible_v(synctex_node_p node){ |
|
return node? node->class->vispector->v(node): 0; |
|
} |
|
/** |
|
* The width of node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_visible_width(synctex_node_p node){ |
|
return node? node->class->vispector->width(node): 0; |
|
} |
|
/** |
|
* The height of node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_visible_height(synctex_node_p node){ |
|
return node? node->class->vispector->height(node): 0; |
|
} |
|
/** |
|
* The depth of node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_visible_depth(synctex_node_p node){ |
|
return node? node->class->vispector->depth(node): 0; |
|
} |
|
|
|
/** |
|
* The V variant of geometrical information. |
|
* - parameter node: a node. |
|
* - returns: an integer. |
|
* - author: JL |
|
*/ |
|
#define SYNCTEX_DEFINE_V(WHAT)\ |
|
SYNCTEX_INLINE static int _synctex_node_##WHAT##_V(synctex_node_p node) { \ |
|
synctex_node_p target = _synctex_tree_target(node); \ |
|
if (target) { \ |
|
return _synctex_data_##WHAT(node)+_synctex_node_##WHAT##_V(target); \ |
|
} else if (_synctex_data_has_##WHAT##_V(node)) { \ |
|
return _synctex_data_##WHAT##_V(node); \ |
|
} else { \ |
|
return _synctex_data_##WHAT(node); \ |
|
} \ |
|
} |
|
SYNCTEX_DEFINE_V(h) |
|
SYNCTEX_DEFINE_V(v) |
|
SYNCTEX_DEFINE_V(width) |
|
SYNCTEX_DEFINE_V(height) |
|
SYNCTEX_DEFINE_V(depth) |
|
|
|
SYNCTEX_INLINE static synctex_point_s _synctex_data_point(synctex_node_p node) { |
|
return (synctex_point_s){synctex_node_h(node),synctex_node_v(node)}; |
|
} |
|
SYNCTEX_INLINE static synctex_point_s _synctex_data_point_V(synctex_node_p node) { |
|
return (synctex_point_s){_synctex_node_h_V(node),_synctex_node_v_V(node)}; |
|
} |
|
SYNCTEX_INLINE static synctex_point_s _synctex_data_set_point(synctex_node_p node, synctex_point_s point) { |
|
synctex_point_s old = _synctex_data_point(node); |
|
_synctex_data_set_h(node,point.h); |
|
_synctex_data_set_v(node,point.v); |
|
return old; |
|
} |
|
SYNCTEX_INLINE static synctex_box_s _synctex_data_box(synctex_node_p node) { |
|
synctex_box_s box = {{0,0},{0,0}}; |
|
int n; |
|
n = synctex_node_width(node); |
|
if (n<0) { |
|
box.max.h = synctex_node_h(node); |
|
box.min.h = box.max.h + n; |
|
} else { |
|
box.min.h = synctex_node_h(node); |
|
box.max.h = box.min.h + n; |
|
} |
|
n = synctex_node_v(node); |
|
box.min.v = n - synctex_node_height(node); |
|
box.max.v = n + synctex_node_depth(node); |
|
return box; |
|
} |
|
SYNCTEX_INLINE static synctex_box_s _synctex_data_xob(synctex_node_p node) { |
|
synctex_box_s box = {{0,0},{0,0}}; |
|
int n; |
|
n = synctex_node_width(node); |
|
if (n>0) { |
|
box.max.h = synctex_node_h(node); |
|
box.min.h = box.max.h - n; |
|
} else { |
|
box.min.h = synctex_node_h(node); |
|
box.max.h = box.min.h - n; |
|
} |
|
n = synctex_node_v(node); |
|
box.min.v = n - synctex_node_height(node); |
|
box.max.v = n + synctex_node_depth(node); |
|
return box; |
|
} |
|
SYNCTEX_INLINE static synctex_box_s _synctex_data_box_V(synctex_node_p node) { |
|
synctex_box_s box = {{0,0},{0,0}}; |
|
int n; |
|
n = _synctex_node_width_V(node); |
|
if (n<0) { |
|
box.max.h = _synctex_node_h_V(node); |
|
box.min.h = box.max.h + n; |
|
} else { |
|
box.min.h = _synctex_node_h_V(node); |
|
box.max.h = box.min.h + n; |
|
} |
|
n = _synctex_node_v_V(node); |
|
box.min.v = n - _synctex_node_height_V(node); |
|
box.max.v = n + _synctex_node_depth_V(node); |
|
return box; |
|
} |
|
|
|
/** |
|
* The higher box node in the parent hierarchy which |
|
* mean line number is the one of node ±1. |
|
* This enclosing box is computed as follows |
|
* 1) get the first hbox in the parent linked list |
|
* starting at node. |
|
* If there is none, simply return the parent of node. |
|
* 2) compute the mean line number |
|
* 3) scans up the tree for the higher hbox with |
|
* the same mean line number, ±1 eventually |
|
* - parameter node: a node. |
|
* - returns: a (proxy to a) box node. |
|
* - author: JL |
|
*/ |
|
static synctex_node_p _synctex_node_box_visible(synctex_node_p node) { |
|
if ((node = _synctex_node_or_handle_target(node))) { |
|
int mean = 0; |
|
int bound = 1500000/(node->class->scanner->pre_magnification/1000); |
|
synctex_node_p parent = NULL; |
|
/* get the first enclosing parent |
|
* then get the highest enclosing parent with the same mean line ±1 */ |
|
node = _synctex_node_or_handle_target(node); |
|
if (!_synctex_node_is_box(node)) { |
|
if ((parent = _synctex_tree_parent(node))) { |
|
node = parent; |
|
} else if ((node = _synctex_tree_target(node))) { |
|
if (!_synctex_node_is_box(node)) { |
|
if ((parent = _synctex_tree_parent(node))) { |
|
node = parent; |
|
} else { |
|
return NULL; |
|
} |
|
} |
|
} |
|
} |
|
parent = node; |
|
mean = synctex_node_mean_line(node); |
|
while ((parent = _synctex_tree_parent(parent))) { |
|
if (_synctex_node_is_hbox(parent)) { |
|
if (_synctex_abs(mean-synctex_node_mean_line(parent))>1) { |
|
return node; |
|
} else if (synctex_node_width(parent)>bound) { |
|
return parent; |
|
} else if (synctex_node_height(parent)+synctex_node_depth(parent)>bound) { |
|
return parent; |
|
} |
|
node = parent; |
|
} |
|
} |
|
} |
|
return node; |
|
} |
|
/** |
|
* The horizontal location of the first box enclosing node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_box_visible_h(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_DISTANCE_h(node,_synctex_node_h_V(_synctex_node_box_visible(node))); |
|
} |
|
/** |
|
* The vertical location of the first box enclosing node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_box_visible_v(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_DISTANCE_v(node,_synctex_node_v_V(_synctex_node_box_visible(node))); |
|
} |
|
/** |
|
* The width of the first box enclosing node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_box_visible_width(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_SIZE(node,_synctex_node_width_V(_synctex_node_box_visible(node))); |
|
} |
|
/** |
|
* The height of the first box enclosing node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_box_visible_height(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_SIZE(node,_synctex_node_height_V(_synctex_node_box_visible(node))); |
|
} |
|
/** |
|
* The depth of the first box enclosing node, in page coordinates. |
|
* - parameter node: a node. |
|
* - returns: a float. |
|
* - author: JL |
|
*/ |
|
float synctex_node_box_visible_depth(synctex_node_p node) { |
|
return SYNCTEX_VISIBLE_SIZE(node,_synctex_node_depth_V(_synctex_node_box_visible(node))); |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Other public node attributes |
|
# endif |
|
|
|
/** |
|
* The page number of the sheet enclosing node. |
|
* - parameter node: a node. |
|
* - returns: the page number or -1 if node does not belong to a sheet tree. |
|
* - note: a proxy target does not belong to a sheet |
|
* but a form, its page number is always -1. |
|
* - note: a handles does not belong to a sheet not a form. |
|
* its page number is -1. |
|
* - author: JL |
|
*/ |
|
int synctex_node_page(synctex_node_p node){ |
|
synctex_node_p parent = NULL; |
|
while((parent = _synctex_tree_parent(node))) { |
|
node = parent; |
|
} |
|
if (synctex_node_type(node) == synctex_node_type_sheet) { |
|
return _synctex_data_page(node); |
|
} |
|
return -1; |
|
} |
|
/** |
|
* The page number of the target. |
|
* - author: JL |
|
*/ |
|
SYNCTEX_INLINE static int _synctex_node_target_page(synctex_node_p node){ |
|
return synctex_node_page(_synctex_tree_target(node)); |
|
} |
|
|
|
#if defined (SYNCTEX_USE_CHARINDEX) |
|
synctex_charindex_t synctex_node_charindex(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
return target? SYNCTEX_CHARINDEX(target):(node?SYNCTEX_CHARINDEX(node):0); |
|
} |
|
#endif |
|
|
|
/** |
|
* The tag of the node. |
|
* - parameter node: a node. |
|
* - returns: the tag or -1 if node is NULL. |
|
* - author: JL |
|
*/ |
|
int synctex_node_tag(synctex_node_p node) { |
|
return node? node->class->tlcpector->tag(node): -1; |
|
} |
|
/** |
|
* The line of the node. |
|
* - parameter node: a node. |
|
* - returns: the line or -1 if node is NULL. |
|
* - author: JL |
|
*/ |
|
int synctex_node_line(synctex_node_p node) { |
|
return node? node->class->tlcpector->line(node): -1; |
|
} |
|
/** |
|
* The column of the node. |
|
* - parameter node: a node. |
|
* - returns: the column or -1 if node is NULL. |
|
* - author: JL |
|
*/ |
|
int synctex_node_column(synctex_node_p node) { |
|
return node? node->class->tlcpector->column(node): -1; |
|
} |
|
/** |
|
* The mean line number of the node. |
|
* - parameter node: a node. |
|
* - returns: the mean line or -1 if node is NULL. |
|
* - author: JL |
|
*/ |
|
int synctex_node_mean_line(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
node = target; |
|
} |
|
return _synctex_data_has_mean_line(node)? |
|
_synctex_data_mean_line(node):_synctex_data_line(node); |
|
} |
|
/** |
|
* The weight of the node. |
|
* - parameter node: a node. |
|
* - returns: the weight or -1 if node is NULL. |
|
* - author: JL |
|
*/ |
|
int synctex_node_weight(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
node = target; |
|
} |
|
return node?(synctex_node_type(node)==synctex_node_type_hbox?_synctex_data_weight(node):0):-1; |
|
} |
|
/** |
|
* The number of children of the node. |
|
* - parameter node: a node. |
|
* - returns: the count or -1 if node is NULL. |
|
* - author: JL |
|
*/ |
|
int synctex_node_child_count(synctex_node_p node) { |
|
synctex_node_p target = _synctex_tree_target(node); |
|
if (target) { |
|
node = target; |
|
} |
|
return node?(synctex_node_type(node)==synctex_node_type_hbox?_synctex_data_weight(node):0):-1; |
|
} |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Sheet & Form |
|
# endif |
|
|
|
/** |
|
* The sheet of the scanner with a given page number. |
|
* - parameter scanner: a scanner. |
|
* - parameter page: a 1 based page number. |
|
* If page == 0, returns the first sheet. |
|
* - returns: a sheet or NULL. |
|
* - author: JL |
|
*/ |
|
synctex_node_p synctex_sheet(synctex_scanner_p scanner,int page) { |
|
if (scanner) { |
|
synctex_node_p sheet = scanner->sheet; |
|
while(sheet) { |
|
if (page == _synctex_data_page(sheet)) { |
|
return sheet; |
|
} |
|
sheet = __synctex_tree_sibling(sheet); |
|
} |
|
if (page == 0) { |
|
return scanner->sheet; |
|
} |
|
} |
|
return NULL; |
|
} |
|
/** |
|
* The form of the scanner with a given tag. |
|
* - parameter scanner: a scanner. |
|
* - parameter tag: an integer identifier. |
|
* If tag == 0, returns the first form. |
|
* - returns: a form. |
|
* - author: JL |
|
*/ |
|
synctex_node_p synctex_form(synctex_scanner_p scanner,int tag) { |
|
if (scanner) { |
|
synctex_node_p form = scanner->form; |
|
while(form) { |
|
if (tag == _synctex_data_tag(form)) { |
|
return form; |
|
} |
|
form = __synctex_tree_sibling(form); |
|
} |
|
if (tag == 0) { |
|
return scanner->form; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* The content of the sheet with given page number. |
|
* - parameter scanner: a scanner. |
|
* - parameter page: a 1 based page number. |
|
* - returns: a (vertical) box node. |
|
* - author: JL |
|
*/ |
|
synctex_node_p synctex_sheet_content(synctex_scanner_p scanner,int page) { |
|
if (scanner) { |
|
return _synctex_tree_child(synctex_sheet(scanner,page)); |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* The content of the sheet with given page number. |
|
* - parameter scanner: a scanner. |
|
* - parameter tag: an integer identifier. |
|
* - returns: a box node. |
|
* - author: JL |
|
*/ |
|
synctex_node_p synctex_form_content(synctex_scanner_p scanner,int tag) { |
|
if (scanner) { |
|
return _synctex_tree_child(synctex_form(scanner,tag)); |
|
} |
|
return NULL; |
|
} |
|
|
|
SYNCTEX_INLINE static synctex_node_p _synctex_scanner_friend(synctex_scanner_p scanner,int i) { |
|
if (i>=0) { |
|
i = _synctex_abs(i)%(scanner->number_of_lists); |
|
return (scanner->lists_of_friends)[i]; |
|
} |
|
return NULL; |
|
} |
|
SYNCTEX_INLINE static synctex_bool_t _synctex_nodes_are_friend(synctex_node_p left, synctex_node_p right) { |
|
return synctex_node_tag(left) == synctex_node_tag(right) && synctex_node_line(left) == synctex_node_line(right); |
|
} |
|
SYNCTEX_INLINE static synctex_node_p _synctex_vertically_sorted_v2(synctex_node_p sibling) { |
|
synctex_node_p child = NULL; |
|
synctex_node_p best_child = sibling; |
|
synctex_node_p next_child = _synctex_tree_reset_child(best_child); |
|
synctex_node_p target = _synctex_tree_target(best_child); |
|
synctex_node_p parent = _synctex_tree_parent(target); |
|
unsigned int best_count = 0; |
|
unsigned int count = 0; |
|
synctex_node_p N = _synctex_tree_child(parent); |
|
do { |
|
if (_synctex_nodes_are_friend(N,best_child)) { |
|
++best_count; |
|
} |
|
} while ((N = __synctex_tree_sibling(N))); |
|
/* Navigate through the other children */ |
|
while ((child = next_child)) { |
|
next_child = _synctex_tree_reset_child(child); |
|
target = _synctex_tree_target(child); |
|
parent = _synctex_tree_parent(target); |
|
count = 0; |
|
N = _synctex_tree_child(parent); |
|
do { |
|
if (_synctex_nodes_are_friend(N,best_child)) { |
|
++count; |
|
} |
|
} while ((N = __synctex_tree_sibling(N))); |
|
if (count>best_count) { |
|
best_count = count; |
|
synctex_node_free(best_child); |
|
best_child = child; |
|
} else { |
|
synctex_node_free(child); |
|
} |
|
} |
|
return best_child; |
|
} |
|
|
|
SYNCTEX_INLINE static synctex_bool_t _synctex_point_in_box_v2(synctex_point_p hitP, synctex_node_p node); |
|
|
|
/* This struct records distances, the left one is non negative and the right one is non positive. |
|
* 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 { |
|
synctex_node_p node; |
|
int distance; |
|
} synctex_nd_s; |
|
|
|
#define SYNCTEX_ND_0 (synctex_nd_s){NULL,INT_MAX} |
|
|
|
typedef synctex_nd_s * synctex_nd_p; |
|
|
|
typedef struct { |
|
synctex_nd_s l; |
|
synctex_nd_s r; |
|
} synctex_nd_lr_s; |
|
|
|
/* The best container is the deeper box that contains the hit point (H,V). |
|
* _synctex_eq_deepest_container_v2 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_p _synctex_eq_deepest_container_v2(synctex_point_p hitP, synctex_node_p node); |
|
|
|
SYNCTEX_INLINE static synctex_nd_lr_s _synctex_eq_get_closest_children_in_box_v2(synctex_point_p hitP, synctex_node_p node); |
|
|
|
/* Closest child, recursive. */ |
|
static synctex_nd_s __synctex_closest_deep_child_v2(synctex_point_p hitP, synctex_node_p node); |
|
|
|
/* 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_p _synctex_smallest_container_v2(synctex_node_p node, synctex_node_p other_node); |
|
|
|
/* Returns the distance between the hit point hit point=(H,V) and the given node. */ |
|
|
|
static int _synctex_point_node_distance_v2(synctex_point_p hitP, synctex_node_p node); |
|
|
|
/* 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. */ |
|
static synctex_nd_s _synctex_eq_closest_child_v2(synctex_point_p hitP, synctex_node_p node); |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Queries |
|
# endif |
|
|
|
struct synctex_iterator_t { |
|
synctex_node_p seed; |
|
synctex_node_p next; |
|
int count0; |
|
int count; |
|
}; |
|
|
|
SYNCTEX_INLINE static synctex_iterator_p _synctex_iterator_new(synctex_node_p result, int count) { |
|
synctex_iterator_p iterator; |
|
if ((iterator = _synctex_malloc(sizeof(synctex_iterator_s)))) { |
|
iterator->seed = iterator->next = result; |
|
iterator->count0 = iterator->count = count; |
|
} |
|
return iterator; |
|
}; |
|
|
|
void synctex_iterator_free(synctex_iterator_p iterator) { |
|
if (iterator) { |
|
synctex_node_free(iterator->seed); |
|
_synctex_free(iterator); |
|
} |
|
} |
|
synctex_bool_t synctex_iterator_has_next(synctex_iterator_p iterator) { |
|
return iterator?iterator->count>0:0; |
|
} |
|
int synctex_iterator_count(synctex_iterator_p iterator) { |
|
return iterator? iterator->count: 0; |
|
} |
|
|
|
/** |
|
* The next result of the iterator. |
|
* Internally, the iterator stores handles to nodes. |
|
* Externally, it returns the targets, |
|
* such that the caller only sees nodes. |
|
*/ |
|
synctex_node_p synctex_iterator_next_result(synctex_iterator_p iterator) { |
|
if (iterator && iterator->count>0) { |
|
synctex_node_p N = iterator->next; |
|
iterator->next = __synctex_tree_sibling(N); |
|
--iterator->count; |
|
return _synctex_tree_target(N); |
|
} |
|
return NULL; |
|
} |
|
int synctex_iterator_reset(synctex_iterator_p iterator) { |
|
if (iterator) { |
|
iterator->next = iterator->seed; |
|
return iterator->count = iterator->count0; |
|
} |
|
return 0; |
|
} |
|
|
|
synctex_iterator_p synctex_iterator_new_edit(synctex_scanner_p scanner,int page,float h,float v){ |
|
if (scanner) { |
|
synctex_node_p sheet = NULL; |
|
synctex_point_s hit; |
|
synctex_node_p node = NULL; |
|
synctex_nd_lr_s nds = {{NULL,0},{NULL,0}}; |
|
if (NULL == (scanner = synctex_scanner_parse(scanner)) || 0 >= scanner->unit) {/* scanner->unit must be >0 */ |
|
return NULL; |
|
} |
|
/* Find the proper sheet */ |
|
sheet = synctex_sheet(scanner,page); |
|
if (NULL == sheet) { |
|
return NULL; |
|
} |
|
/* Now sheet points to the sheet node with proper page number. */ |
|
/* Now that scanner has been initialized, we can convert |
|
* the given point to scanner integer coordinates */ |
|
hit = (synctex_point_s) |
|
{(h-scanner->x_offset)/scanner->unit, |
|
(v-scanner->y_offset)/scanner->unit}; |
|
/* At first, we browse all the horizontal boxes of the sheet |
|
* until we find one containing the hit point. */ |
|
if ((node = _synctex_tree_next_hbox(sheet))) { |
|
do { |
|
if (_synctex_point_in_box_v2(&hit,node)) { |
|
/* Maybe the hit point belongs to a contained vertical box. |
|
* This is the most likely situation. |
|
*/ |
|
synctex_node_p next = node; |
|
#if defined(SYNCTEX_DEBUG) |
|
printf("--- We are lucky\n"); |
|
#endif |
|
/* This trick is for catching overlapping boxes */ |
|
while ((next = _synctex_tree_next_hbox(next))) { |
|
if (_synctex_point_in_box_v2(&hit,next)) { |
|
node = _synctex_smallest_container_v2(next,node); |
|
} |
|
} |
|
/* node is the smallest horizontal box that contains hit, |
|
* unless there is no hbox at all. |
|
*/ |
|
node = _synctex_eq_deepest_container_v2(&hit, node); |
|
nds = _synctex_eq_get_closest_children_in_box_v2(&hit, node); |
|
end: |
|
if (nds.r.node && nds.l.node) { |
|
if ((_synctex_data_tag(nds.r.node)!=_synctex_data_tag(nds.l.node)) |
|
|| (_synctex_data_line(nds.r.node)!=_synctex_data_line(nds.l.node)) |
|
|| (_synctex_data_column(nds.r.node)!=_synctex_data_column(nds.l.node))) { |
|
if (nds.l.distance>nds.r.distance) { |
|
node = nds.r.node; |
|
nds.r.node = nds.l.node; |
|
nds.l.node = node; |
|
} |
|
if((node = _synctex_new_handle_with_target(nds.l.node))) { |
|
synctex_node_p other_handle; |
|
if((other_handle = _synctex_new_handle_with_target(nds.r.node))) { |
|
_synctex_tree_set_sibling(node,other_handle); |
|
return _synctex_iterator_new(node,2); |
|
} |
|
return _synctex_iterator_new(node,1); |
|
} |
|
return NULL; |
|
} |
|
/* both nodes have the same input coordinates |
|
* We choose the one closest to the hit point */ |
|
if (nds.l.distance>nds.r.distance) { |
|
nds.l.node = nds.r.node; |
|
} |
|
nds.r.node = NULL; |
|
} else if (nds.r.node) { |
|
nds.l = nds.r; |
|
} else if (!nds.l.node) { |
|
nds.l.node = node; |
|
} |
|
if((node = _synctex_new_handle_with_target(nds.l.node))) { |
|
return _synctex_iterator_new(node,1); |
|
} |
|
return 0; |
|
} |
|
} while ((node = _synctex_tree_next_hbox(node))); |
|
/* All the horizontal boxes have been tested, |
|
* None of them contains the hit point. |
|
*/ |
|
} |
|
/* We are not lucky, |
|
* we test absolutely all the node |
|
* to find the closest... */ |
|
if ((node = _synctex_tree_child(sheet))) { |
|
#if defined(SYNCTEX_DEBUG) |
|
printf("--- We are not lucky\n"); |
|
#endif |
|
nds.l = __synctex_closest_deep_child_v2(&hit, node); |
|
#if defined(SYNCTEX_DEBUG) |
|
printf("Edit query best: %i\n", nds.l.distance); |
|
#endif |
|
goto end; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* Loop the candidate friendly list to find the ones with the proper |
|
* tag and line. |
|
* Returns a tree of results targeting the found candidates. |
|
* At the top level each sibling has its own page number. |
|
* All the results with the same page number are linked by child/parent entry. |
|
* - parameter candidate: a friendly list of candidates |
|
*/ |
|
static synctex_node_p _synctex_display_query_v2(synctex_node_p target, int tag, int line, synctex_bool_t exclude_box) { |
|
synctex_node_p first_handle = NULL; |
|
/* Search the first match */ |
|
if (target == NULL) { |
|
return first_handle; |
|
} |
|
do { |
|
int page; |
|
if ((exclude_box |
|
&& _synctex_node_is_box(target)) |
|
|| (tag != synctex_node_tag(target)) |
|
|| (line != synctex_node_line(target))) { |
|
continue; |
|
} |
|
/* We found a first match, create |
|
* a result handle targeting that candidate. */ |
|
first_handle = _synctex_new_handle_with_target(target); |
|
if (first_handle == NULL) { |
|
return first_handle; |
|
} |
|
/* target is either a node, |
|
* or a proxy to some node, in which case, |
|
* the target's target belongs to a form, |
|
* not a sheet. */ |
|
page = synctex_node_page(target); |
|
/* Now create all the other results */ |
|
while ((target = _synctex_tree_friend(target))) { |
|
synctex_node_p result = NULL; |
|
if ((exclude_box |
|
&& _synctex_node_is_box(target)) |
|
|| (tag != synctex_node_tag(target)) |
|
|| (line != synctex_node_line(target))) { |
|
continue; |
|
} |
|
/* Another match, same page number ? */ |
|
result = _synctex_new_handle_with_target(target); |
|
if (NULL == result ) { |
|
return first_handle; |
|
} |
|
/* is it the same page number ? */ |
|
if (synctex_node_page(target) == page) { |
|
__synctex_tree_set_child(result, first_handle); |
|
first_handle = result; |
|
} else { |
|
/* We have 2 page numbers involved */ |
|
__synctex_tree_set_sibling(first_handle, result); |
|
while ((target = _synctex_tree_friend(target))) { |
|
synctex_node_p same_page_node; |
|
if ((exclude_box |
|
&& _synctex_node_is_box(target)) |
|
|| (tag != synctex_node_tag(target)) |
|
|| (line != synctex_node_line(target))) { |
|
continue; |
|
} |
|
/* New match found, which page? */ |
|
result = _synctex_new_handle_with_target(target); |
|
if (NULL == result) { |
|
return first_handle; |
|
} |
|
same_page_node = first_handle; |
|
page = synctex_node_page(target); |
|
/* Find a result with the same page number */; |
|
do { |
|
if (_synctex_node_target_page(same_page_node) == page) { |
|
_synctex_tree_set_child(result,_synctex_tree_set_child(same_page_node,result)); |
|
} else if ((same_page_node = __synctex_tree_sibling(same_page_node))) { |
|
continue; |
|
} else { |
|
/* This is a new page number */ |
|
__synctex_tree_set_sibling(result,first_handle); |
|
first_handle = result; |
|
} |
|
break; |
|
} while (synctex_YES); |
|
} |
|
return first_handle; |
|
} |
|
} |
|
} while ((target = _synctex_tree_friend(target))); |
|
return first_handle; |
|
} |
|
synctex_iterator_p synctex_iterator_new_display(synctex_scanner_p scanner,const char * name,int line,int column, int page_hint) { |
|
(void)column; /* unused */ |
|
if (scanner) { |
|
int tag = synctex_scanner_get_tag(scanner,name);/* parse if necessary */ |
|
int max_line = 0; |
|
int line_offset = 1; |
|
int try_count = 100; |
|
synctex_node_p node = NULL; |
|
synctex_node_p result = NULL; |
|
if (tag == 0) { |
|
printf("SyncTeX Warning: No tag for %s\n",name); |
|
return NULL; |
|
} |
|
node = synctex_scanner_input_with_tag(scanner, tag); |
|
max_line = _synctex_data_line(node); |
|
/* node = NULL; */ |
|
if (line>max_line) { |
|
line = max_line; |
|
} |
|
while(try_count--) { |
|
if (line<=max_line) { |
|
/* This loop will only be performed once for advanced viewers */ |
|
synctex_node_p friend = _synctex_scanner_friend(scanner,tag+line); |
|
if ((node = friend)) { |
|
result = _synctex_display_query_v2(node,tag,line,synctex_YES); |
|
if (!result) { |
|
/* We did not find any matching boundary, retry including boxes */ |
|
node = friend;/* no need to test it again, already done */ |
|
result = _synctex_display_query_v2(node,tag,line,synctex_NO); |
|
} |
|
/* Now reverse the order to have nodes in display order, and then keep just a few nodes. |
|
* Order first the best node. */ |
|
/* The result is a tree. At the root level, all nodes |
|
* correspond to different page numbers. |
|
* Each node has a child which corresponds to the same |
|
* page number if relevant. |
|
* Then reorder the nodes to put first the one which fits best. |
|
* The idea is to count the number of nodes |
|
* with the same tag and line number in the parents |
|
* and choose the one with the biggest count. |
|
*/ |
|
if (result) { |
|
/* navigate through siblings, |
|
then children */ |
|
int count = 1; |
|
synctex_node_p next_sibling = __synctex_tree_reset_sibling(result); |
|
int best_match = abs(page_hint-_synctex_node_target_page(result)); |
|
synctex_node_p sibling; |
|
int match; |
|
result = _synctex_vertically_sorted_v2(result); |
|
while((sibling = next_sibling)) { |
|
/* What is next? Do not miss that step! */ |
|
next_sibling = __synctex_tree_reset_sibling(sibling); |
|
sibling = _synctex_vertically_sorted_v2(sibling); |
|
match = abs(page_hint-_synctex_node_target_page(sibling)); |
|
if (match<best_match) { |
|
/* Order this node first */ |
|
__synctex_tree_set_sibling(sibling,result); |
|
result = sibling; |
|
best_match = match; |
|
} else /*if (match>best_match)*/ { |
|
__synctex_tree_set_sibling(sibling,__synctex_tree_sibling(result)); |
|
__synctex_tree_set_sibling(result,sibling); |
|
} |
|
++count; |
|
} |
|
/* Now order first the result closest to the page hint */ |
|
return _synctex_iterator_new(result,count); |
|
} |
|
} |
|
# if defined(__SYNCTEX_STRONG_DISPLAY_QUERY__) |
|
break; |
|
# else |
|
line += line_offset; |
|
line_offset=line_offset<0?-(line_offset-1):-(line_offset+1); |
|
if (line <= 0) { |
|
line += line_offset; |
|
line_offset=line_offset<0?-(line_offset-1):-(line_offset+1); |
|
} |
|
# endif |
|
} |
|
} |
|
} |
|
return NULL; |
|
} |
|
synctex_status_t synctex_display_query(synctex_scanner_p scanner,const char * name,int line,int column, int page_hint) { |
|
if (scanner) { |
|
synctex_iterator_free(scanner->iterator); |
|
scanner->iterator = synctex_iterator_new_display(scanner, name,line,column, page_hint); |
|
return synctex_iterator_count(scanner->iterator); |
|
} |
|
return SYNCTEX_STATUS_ERROR; |
|
} |
|
synctex_status_t synctex_edit_query(synctex_scanner_p scanner,int page,float h,float v) { |
|
if (scanner) { |
|
synctex_iterator_free(scanner->iterator); |
|
scanner->iterator = synctex_iterator_new_edit(scanner, page, h, v); |
|
return synctex_iterator_count(scanner->iterator); |
|
} |
|
return SYNCTEX_STATUS_ERROR; |
|
} |
|
/** |
|
* The next result of a query. |
|
*/ |
|
synctex_node_p synctex_scanner_next_result(synctex_scanner_p scanner) { |
|
return scanner? synctex_iterator_next_result(scanner->iterator): NULL; |
|
} |
|
synctex_status_t synctex_scanner_reset_result(synctex_scanner_p scanner) { |
|
return scanner? synctex_iterator_reset(scanner->iterator): SYNCTEX_STATUS_ERROR; |
|
} |
|
|
|
synctex_node_p synctex_node_target(synctex_node_p node) { |
|
return _synctex_tree_target(node); |
|
} |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Geometric utilities |
|
# endif |
|
|
|
/** Rougly speaking, this is: |
|
* node's h coordinate - hit point'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. |
|
* If the argument is a pdf form reference, then the child is used and returned instead. |
|
* Last Revision: Mon Apr 24 07:05:27 UTC 2017 |
|
*/ |
|
static synctex_nd_s _synctex_point_h_ordered_distance_v2 |
|
(synctex_point_p hit, synctex_node_p node) { |
|
synctex_nd_s nd = {node,INT_MAX}; |
|
if (node) { |
|
int min,med,max,width; |
|
switch(synctex_node_type(node)) { |
|
/* 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_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. */ |
|
width = _synctex_data_width(node); |
|
min = _synctex_data_h(node); |
|
max = min + (width>0?width:-width); |
|
/* We always have min <= max */ |
|
if (hit->h<min) { |
|
nd.distance = min - hit->h; /* regions 1+4+7, result is > 0 */ |
|
} else if (hit->h>max) { |
|
nd.distance = max - hit->h; /* regions 3+6+9, result is < 0 */ |
|
} else { |
|
nd.distance = 0; /* regions 2+5+8, inside the box, except for vertical coordinates */ |
|
} |
|
break; |
|
case synctex_node_type_proxy_vbox: |
|
/* getting the box bounds, taking into account negative width, height and depth. */ |
|
width = synctex_node_width(node); |
|
min = synctex_node_h(node); |
|
max = min + (width>0?width:-width); |
|
/* We always have min <= max */ |
|
if (hit->h<min) { |
|
nd.distance = min - hit->h; /* regions 1+4+7, result is > 0 */ |
|
} else if (hit->h>max) { |
|
nd.distance = max - hit->h; /* regions 3+6+9, result is < 0 */ |
|
} else { |
|
nd.distance = 0; /* regions 2+5+8, inside the box, except for vertical coordinates */ |
|
} |
|
break; |
|
case synctex_node_type_hbox: |
|
case synctex_node_type_proxy_hbox: |
|
/* getting the box bounds, taking into account negative width, height and depth. */ |
|
width = synctex_node_hbox_width(node); |
|
min = synctex_node_hbox_h(node); |
|
max = min + (width>0?width:-width); |
|
/* We always have min <= max */ |
|
if (hit->h<min) { |
|
nd.distance = min - hit->h; /* regions 1+4+7, result is > 0 */ |
|
} else if (hit->h>max) { |
|
nd.distance = max - hit->h; /* regions 3+6+9, result is < 0 */ |
|
} else { |
|
nd.distance = 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_data_width(node); |
|
if (max<0) { |
|
min = _synctex_data_h(node); |
|
max = min - max; |
|
} else { |
|
min = -max; |
|
max = _synctex_data_h(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 effectively overlapping text. |
|
* But this should be extremely rare. I guess that in that case, many different choices |
|
* could be made, one being in contradiction with the other. |
|
* It means that the best choice should be made according to the situation that occurs |
|
* most frequently. |
|
*/ |
|
if (hit->h<min) { |
|
nd.distance = min - hit->h + 1; /* penalty to ensure other nodes are chosen first in case of overlapping ones */ |
|
} else if (hit->h>max) { |
|
nd.distance = max - hit->h - 1; /* same kind of penalty */ |
|
} else if (hit->h>med) { |
|
/* do things like if the node had 0 width and was placed at the max edge + 1*/ |
|
nd.distance = max - hit->h + 1; /* positive, the kern is to the right of the hit point */ |
|
} else { |
|
nd.distance = min - hit->h - 1; /* negative, the kern is to the left of the hit point */ |
|
} |
|
break; |
|
case synctex_node_type_rule:/* to do: special management */ |
|
case synctex_node_type_glue: |
|
case synctex_node_type_math: |
|
case synctex_node_type_boundary: |
|
case synctex_node_type_box_bdry: |
|
nd.distance = _synctex_data_h(node) - hit->h; |
|
break; |
|
case synctex_node_type_ref: |
|
nd.node = synctex_node_child(node); |
|
nd = _synctex_point_h_ordered_distance_v2(hit,nd.node); |
|
break; |
|
case synctex_node_type_proxy: |
|
case synctex_node_type_proxy_last: |
|
{ |
|
/* shift the hit point to be relative to the proxy origin, |
|
* then compute the distance to the target |
|
*/ |
|
synctex_point_s otherHit = *hit; |
|
otherHit.h -= _synctex_data_h(node); |
|
otherHit.v -= _synctex_data_v(node); |
|
nd.node = _synctex_tree_target(node); |
|
nd = _synctex_point_h_ordered_distance_v2(&otherHit,nd.node); |
|
nd.node = node; |
|
} |
|
default: |
|
break; |
|
} |
|
} |
|
return nd; |
|
} |
|
/** Rougly speaking, this is: |
|
* node's v coordinate - hit point'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. |
|
*/ |
|
static synctex_nd_s _synctex_point_v_ordered_distance_v2 |
|
(synctex_point_p hit, synctex_node_p node) { |
|
synctex_nd_s nd = {node, INT_MAX}; |
|
int min,max,depth,height; |
|
switch(synctex_node_type(node)) { |
|
/* 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_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_node_v(node); |
|
max = min + _synctex_abs(_synctex_data_depth(node)); |
|
min -= _synctex_abs(_synctex_data_height(node)); |
|
/* We always have min <= max */ |
|
if (hit->v<min) { |
|
nd.distance = min - hit->v; /* regions 1+2+3, result is > 0 */ |
|
} else if (hit->v>max) { |
|
nd.distance = max - hit->v; /* regions 7+8+9, result is < 0 */ |
|
} else { |
|
nd.distance = 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */ |
|
} |
|
break; |
|
case synctex_node_type_proxy_vbox: |
|
/* getting the box bounds, taking into account negative width, height and depth. */ |
|
min = synctex_node_v(node); |
|
max = min + _synctex_abs(synctex_node_depth(node)); |
|
min -= _synctex_abs(synctex_node_height(node)); |
|
/* We always have min <= max */ |
|
if (hit->v<min) { |
|
nd.distance = min - hit->v; /* regions 1+2+3, result is > 0 */ |
|
} else if (hit->v>max) { |
|
nd.distance = max - hit->v; /* regions 7+8+9, result is < 0 */ |
|
} else { |
|
nd.distance = 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */ |
|
} |
|
break; |
|
case synctex_node_type_hbox: |
|
case synctex_node_type_proxy_hbox: |
|
/* getting the box bounds, taking into account negative height and depth. */ |
|
min = synctex_node_hbox_v(node); |
|
depth = synctex_node_hbox_depth(node); |
|
max = min + (depth>0?depth:-depth); |
|
height = synctex_node_hbox_height(node); |
|
min -= (height>0?height:-height); |
|
/* We always have min <= max */ |
|
if (hit->v<min) { |
|
nd.distance = min - hit->v; /* regions 1+2+3, result is > 0 */ |
|
} else if (hit->v>max) { |
|
nd.distance = max - hit->v; /* regions 7+8+9, result is < 0 */ |
|
} else { |
|
nd.distance = 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */ |
|
} |
|
break; |
|
case synctex_node_type_rule:/* to do: special management */ |
|
case synctex_node_type_kern: |
|
case synctex_node_type_glue: |
|
case synctex_node_type_math: |
|
min = _synctex_data_v(node); |
|
max = min + _synctex_abs(_synctex_data_depth(_synctex_tree_parent(node))); |
|
min -= _synctex_abs(_synctex_data_height(_synctex_tree_parent(node))); |
|
/* We always have min <= max */ |
|
if (hit->v<min) { |
|
nd.distance = min - hit->v; /* regions 1+2+3, result is > 0 */ |
|
} else if (hit->v>max) { |
|
nd.distance = max - hit->v; /* regions 7+8+9, result is < 0 */ |
|
} else { |
|
nd.distance = 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */ |
|
} |
|
break; |
|
case synctex_node_type_ref: |
|
nd.node = synctex_node_child(node); |
|
nd = _synctex_point_v_ordered_distance_v2(hit,nd.node); |
|
break; |
|
case synctex_node_type_proxy: |
|
case synctex_node_type_proxy_last: |
|
{ |
|
synctex_point_s otherHit = *hit; |
|
otherHit.h -= _synctex_data_h(node); |
|
otherHit.v -= _synctex_data_v(node); |
|
nd.node = _synctex_tree_target(node); |
|
nd = _synctex_point_v_ordered_distance_v2(&otherHit,nd.node); |
|
nd.node = node; |
|
} |
|
default: break; |
|
} |
|
return nd; |
|
} |
|
/** |
|
* The best is the one with the smallest area. |
|
* The area is width*height where width and height may be big. |
|
* So there is a real risk of overflow if we stick with ints. |
|
*/ |
|
SYNCTEX_INLINE static synctex_node_p _synctex_smallest_container_v2(synctex_node_p node, synctex_node_p other_node) { |
|
long total_height, other_total_height; |
|
unsigned long area, other_area; |
|
long width = synctex_node_hbox_width(node); |
|
long other_width = synctex_node_hbox_width(other_node); |
|
if (width<0) { |
|
width = -width; |
|
} |
|
if (other_width<0) { |
|
other_width = -other_width; |
|
} |
|
total_height = _synctex_abs(synctex_node_hbox_depth(node)) + _synctex_abs(synctex_node_hbox_height(node)); |
|
other_total_height = _synctex_abs(synctex_node_hbox_depth(other_node)) + _synctex_abs(synctex_node_hbox_height(other_node)); |
|
area = total_height*width; |
|
other_area = other_total_height*other_width; |
|
if (area<other_area) { |
|
return node; |
|
} |
|
if (area>other_area) { |
|
return other_node; |
|
} |
|
if (_synctex_abs(_synctex_data_width(node))>_synctex_abs(_synctex_data_width(other_node))) { |
|
return node; |
|
} |
|
if (_synctex_abs(_synctex_data_width(node))<_synctex_abs(_synctex_data_width(other_node))) { |
|
return other_node; |
|
} |
|
if (total_height<other_total_height) { |
|
return node; |
|
} |
|
if (total_height>other_total_height) { |
|
return other_node; |
|
} |
|
return node; |
|
} |
|
|
|
SYNCTEX_INLINE static synctex_bool_t _synctex_point_in_box_v2(synctex_point_p hit, synctex_node_p node) { |
|
if (node) { |
|
if (0 == _synctex_point_h_ordered_distance_v2(hit,node).distance |
|
&& 0 == _synctex_point_v_ordered_distance_v2(hit,node).distance) { |
|
return synctex_YES; |
|
} |
|
} |
|
return synctex_NO; |
|
} |
|
|
|
static int _synctex_distance_to_box_v2(synctex_point_p hit,synctex_box_p box) { |
|
/* 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. */ |
|
if (hit->v<box->min.v) { |
|
/* Regions 1, 2 or 3 */ |
|
if (hit->h<box->min.h) { |
|
/* This is region 1. The distance to the box is the L1 distance PA. */ |
|
return box->min.v - hit->v + box->min.h - hit->h;/* Integer overflow? probability epsilon */ |
|
} else if (hit->h<=box->max.h) { |
|
/* This is region 2. The distance to the box is the geometrical distance to the top edge. */ |
|
return box->min.v - hit->v; |
|
} else { |
|
/* This is region 3. The distance to the box is the L1 distance PB. */ |
|
return box->min.v - hit->v + hit->h - box->max.h; |
|
} |
|
} else if (hit->v<=box->max.v) { |
|
/* Regions 4, 5 or 6 */ |
|
if (hit->h<box->min.h) { |
|
/* This is region 4. The distance to the box is the geometrical distance to the left edge. */ |
|
return box->min.h - hit->h; |
|
} else if (hit->h<=box->max.h) { |
|
/* This is region 5. We are inside the box. */ |
|
return 0; |
|
} else { |
|
/* This is region 6. The distance to the box is the geometrical distance to the right edge. */ |
|
return hit->h - box->max.h; |
|
} |
|
} else { |
|
/* Regions 7, 8 or 9 */ |
|
if (hit->h<box->min.h) { |
|
/* This is region 7. The distance to the box is the L1 distance PC. */ |
|
return hit->v - box->max.v + box->min.h - hit->h; |
|
} else if (hit->h<=box->max.h) { |
|
/* This is region 8. The distance to the box is the geometrical distance to the top edge. */ |
|
return hit->v - box->max.v; |
|
} else { |
|
/* This is region 9. The distance to the box is the L1 distance PD. */ |
|
return hit->v - box->max.v + hit->h - box->max.h; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* The distance from the hit point to the node. |
|
*/ |
|
static int _synctex_point_node_distance_v2(synctex_point_p hit, synctex_node_p node) { |
|
int d = INT_MAX; |
|
if (node) { |
|
synctex_box_s box = {{0,0},{0,0}}; |
|
int dd = INT_MAX; |
|
switch(synctex_node_type(node)) { |
|
case synctex_node_type_vbox: |
|
box.min.h = _synctex_data_h(node); |
|
box.max.h = box.min.h + _synctex_abs(_synctex_data_width(node)); |
|
box.min.v = synctex_node_v(node); |
|
box.max.v = box.min.v + _synctex_abs(_synctex_data_depth(node)); |
|
box.min.v -= _synctex_abs(_synctex_data_height(node)); |
|
return _synctex_distance_to_box_v2(hit,&box); |
|
case synctex_node_type_proxy_vbox: |
|
box.min.h = synctex_node_h(node); |
|
box.max.h = box.min.h + _synctex_abs(synctex_node_width(node)); |
|
box.min.v = synctex_node_v(node); |
|
box.max.v = box.min.v + _synctex_abs(synctex_node_depth(node)); |
|
box.min.v -= _synctex_abs(synctex_node_height(node)); |
|
return _synctex_distance_to_box_v2(hit,&box); |
|
case synctex_node_type_hbox: |
|
case synctex_node_type_proxy_hbox: |
|
box.min.h = synctex_node_hbox_h(node); |
|
box.max.h = box.min.h + _synctex_abs(synctex_node_hbox_width(node)); |
|
box.min.v = synctex_node_hbox_v(node); |
|
box.max.v = box.min.v + _synctex_abs(synctex_node_hbox_depth(node)); |
|
box.min.v -= _synctex_abs(synctex_node_hbox_height(node)); |
|
return _synctex_distance_to_box_v2(hit,&box); |
|
case synctex_node_type_void_vbox: |
|
case synctex_node_type_void_hbox: |
|
/* best of distances from the left edge and right edge*/ |
|
box.min.h = _synctex_data_h(node); |
|
box.max.h = box.min.h; |
|
box.min.v = _synctex_data_v(node); |
|
box.max.v = box.min.v + _synctex_abs(_synctex_data_depth(node)); |
|
box.min.v -= _synctex_abs(_synctex_data_height(node)); |
|
d = _synctex_distance_to_box_v2(hit,&box); |
|
box.min.h = box.min.h + _synctex_abs(_synctex_data_width(node)); |
|
box.max.h = box.min.h; |
|
dd = _synctex_distance_to_box_v2(hit,&box); |
|
return d<dd ? d:dd; |
|
case synctex_node_type_kern: |
|
box.min.h = _synctex_data_h(node); |
|
box.max.h = box.min.h; |
|
box.max.v = _synctex_data_v(node); |
|
box.min.v = box.max.v - _synctex_abs(_synctex_data_height(_synctex_tree_parent(node))); |
|
d = _synctex_distance_to_box_v2(hit,&box); |
|
box.min.h -= _synctex_data_width(node); |
|
box.max.h = box.min.h; |
|
dd = _synctex_distance_to_box_v2(hit,&box); |
|
return d<dd ? d:dd; |
|
case synctex_node_type_glue: |
|
case synctex_node_type_math: |
|
case synctex_node_type_boundary: |
|
case synctex_node_type_box_bdry: |
|
box.min.h = _synctex_data_h(node); |
|
box.max.h = box.min.h; |
|
box.max.v = _synctex_data_v(node); |
|
box.min.v = box.max.v - _synctex_abs(_synctex_data_height(_synctex_tree_parent(node))); |
|
return _synctex_distance_to_box_v2(hit,&box); |
|
case synctex_node_type_proxy: |
|
case synctex_node_type_proxy_last: |
|
{ |
|
synctex_point_s otherHit = *hit; |
|
otherHit.h -= _synctex_data_h(node); |
|
otherHit.v -= _synctex_data_v(node); |
|
return _synctex_point_node_distance_v2(&otherHit, _synctex_tree_target(node)); |
|
} |
|
default: break; |
|
} |
|
} |
|
return d; |
|
} |
|
static synctex_node_p _synctex_eq_deepest_container_v2(synctex_point_p hit, synctex_node_p node) { |
|
if (node) { |
|
/**/ |
|
synctex_node_p child; |
|
if ((child = synctex_node_child(node))) { |
|
/* Non void hbox or vbox, form ref or proxy */ |
|
/* We go deep first because some boxes have 0 dimensions |
|
* despite they do contain some black material. |
|
*/ |
|
do { |
|
if ((_synctex_point_in_box_v2(hit,child))) { |
|
synctex_node_p deep = _synctex_eq_deepest_container_v2(hit,child); |
|
if (deep) { |
|
/* One of the children contains the hit. */ |
|
return deep; |
|
} |
|
} |
|
} while((child = synctex_node_sibling(child))); |
|
/* is the hit point inside the box? */ |
|
if (synctex_node_type(node) == synctex_node_type_vbox |
|
|| synctex_node_type(node) == synctex_node_type_proxy_vbox) { |
|
/* 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 inside. */ |
|
if ((child = _synctex_tree_child(node))) { |
|
synctex_nd_s best = SYNCTEX_ND_0; |
|
do { |
|
if (_synctex_tree_child(child)) { |
|
int d = _synctex_point_node_distance_v2(hit,child); |
|
if (d <= best.distance) { |
|
best = (synctex_nd_s){child, d}; |
|
} |
|
} |
|
} while((child = __synctex_tree_sibling(child))); |
|
if (best.node) { |
|
return best.node; |
|
} |
|
} |
|
} |
|
if (_synctex_point_in_box_v2(hit,node)) { |
|
return node; |
|
} |
|
} |
|
} |
|
return NULL; |
|
} |
|
static synctex_nd_s _synctex_eq_deepest_container_v3(synctex_point_p hit, synctex_node_p node) { |
|
if (node) { |
|
synctex_node_p child = NULL; |
|
if ((child = synctex_node_child(node))) { |
|
/* Non void hbox, vbox, box proxy or form ref */ |
|
/* We go deep first because some boxes have 0 dimensions |
|
* despite they do contain some black material. |
|
*/ |
|
do { |
|
synctex_nd_s deep = _synctex_eq_deepest_container_v3(hit, child); |
|
if (deep.node) { |
|
/* One of the children contains the hit-> */ |
|
return deep; |
|
} |
|
} while((child = synctex_node_sibling(child))); |
|
/* 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 inside. */ |
|
if (synctex_node_type(node) == synctex_node_type_vbox |
|
|| synctex_node_type(node) == synctex_node_type_proxy_vbox) { |
|
if ((child = synctex_node_child(node))) { |
|
synctex_nd_s best = SYNCTEX_ND_0; |
|
do { |
|
if (synctex_node_child(child)) { |
|
int d = _synctex_point_node_distance_v2(hit,child); |
|
if (d < best.distance) { |
|
best = (synctex_nd_s){child,d}; |
|
} |
|
} |
|
} while((child = synctex_node_sibling(child))); |
|
if (best.node) { |
|
return best; |
|
} |
|
} |
|
} |
|
/* is the hit point inside the box? */ |
|
if (_synctex_point_in_box_v2(hit,node)) { |
|
return (synctex_nd_s){node, 0}; |
|
} |
|
} |
|
} |
|
return SYNCTEX_ND_0; |
|
} |
|
|
|
/* Compares the locations of the hit point 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 synctex_nd_lr_s __synctex_eq_get_closest_children_in_hbox_v2(synctex_point_p hitP, synctex_node_p node) { |
|
synctex_nd_s childd = SYNCTEX_ND_0; |
|
synctex_nd_lr_s nds = {SYNCTEX_ND_0,SYNCTEX_ND_0}; |
|
if ((childd.node = synctex_node_child(node))) { |
|
synctex_nd_s nd = SYNCTEX_ND_0; |
|
do { |
|
childd = _synctex_point_h_ordered_distance_v2(hitP,childd.node); |
|
if (childd.distance > 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 (nds.r.distance > childd.distance) { |
|
nds.r = childd; |
|
} else if (nds.r.distance == childd.distance && nds.r.node) { |
|
if (_synctex_data_tag(nds.r.node) == _synctex_data_tag(childd.node) |
|
&& (_synctex_data_line(nds.r.node) > _synctex_data_line(childd.node) |
|
|| (_synctex_data_line(nds.r.node) == _synctex_data_line(childd.node) |
|
&& _synctex_data_column(nds.r.node) > _synctex_data_column(childd.node)))) { |
|
nds.r = childd; |
|
} |
|
} |
|
} else if (childd.distance == 0) { |
|
/* hit point is inside node. */ |
|
return _synctex_eq_get_closest_children_in_box_v2(hitP, childd.node); |
|
} else { /* here childd.distance < 0, the hit point is to the right of node */ |
|
childd.distance = -childd.distance; |
|
if (nds.l.distance > childd.distance) { |
|
nds.l = childd; |
|
} else if (nds.l.distance == childd.distance && nds.l.node) { |
|
if (_synctex_data_tag(nds.l.node) == _synctex_data_tag(childd.node) |
|
&& (_synctex_data_line(nds.l.node) > _synctex_data_line(childd.node) |
|
|| (_synctex_data_line(nds.l.node) == _synctex_data_line(childd.node) |
|
&& _synctex_data_column(nds.l.node) > _synctex_data_column(childd.node)))) { |
|
nds.l = childd; |
|
} |
|
} |
|
} |
|
} while((childd.node = synctex_node_sibling(childd.node))); |
|
if (nds.l.node) { |
|
/* the left node is new, try to narrow the result */ |
|
if ((nd = _synctex_eq_deepest_container_v3(hitP,nds.l.node)).node) { |
|
nds.l = nd; |
|
} |
|
if((nd = __synctex_closest_deep_child_v2(hitP,nds.l.node)).node) { |
|
nds.l.node = nd.node; |
|
} |
|
} |
|
if (nds.r.node) { |
|
/* the right node is new, try to narrow the result */ |
|
if ((nd = _synctex_eq_deepest_container_v3(hitP,nds.r.node)).node) { |
|
nds.r = nd; |
|
} |
|
if((nd = __synctex_closest_deep_child_v2(hitP,nds.r.node)).node) { |
|
nds.r.node = nd.node; |
|
} |
|
} |
|
} |
|
return nds; |
|
} |
|
|
|
#if 0 |
|
SYNCTEX_INLINE static synctex_nd_lr_s __synctex_eq_get_closest_children_in_hbox_v3(synctex_point_p hitP, synctex_node_p nodeP) { |
|
synctex_nd_s nd = SYNCTEX_ND_0; |
|
synctex_nd_lr_s nds = {SYNCTEX_ND_0,SYNCTEX_ND_0}; |
|
if ((nd.node = _synctex_tree_child(nodeP))) { |
|
do { |
|
nd = _synctex_point_h_ordered_distance_v2(hitP,nd.node); |
|
if (nd.distance > 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 (nds.r.distance > nd.distance) { |
|
nds.r = nd; |
|
} else if (nds.r.distance == nd.distance && nds.r.node) { |
|
if (_synctex_data_tag(nds.r.node) == _synctex_data_tag(nd.node) |
|
&& (_synctex_data_line(nds.r.node) > _synctex_data_line(nd.node) |
|
|| (_synctex_data_line(nds.r.node) == _synctex_data_line(nd.node) |
|
&& _synctex_data_column(nds.r.node) > _synctex_data_column(nd.node)))) { |
|
nds.r = nd; |
|
} |
|
} |
|
} else if (nd.distance == 0) { |
|
/* hit point is inside node. */ |
|
nds.l = nd; |
|
} else { /* here nd.d < 0, the hit point is to the right of node */ |
|
nd.distance = -nd.distance; |
|
if (nds.l.distance > nd.distance) { |
|
nds.l = nd; |
|
} else if (nds.l.distance == nd.distance && nds.l.node) { |
|
if (_synctex_data_tag(nds.l.node) == _synctex_data_tag(nd.node) |
|
&& (_synctex_data_line(nds.l.node) > _synctex_data_line(nd.node) |
|
|| (_synctex_data_line(nds.l.node) == _synctex_data_line(nd.node) |
|
&& _synctex_data_column(nds.l.node) > _synctex_data_column(nd.node)))) { |
|
nds.l = nd; |
|
} |
|
} |
|
} |
|
} while((nd.node = __synctex_tree_sibling(nd.node))); |
|
if (nds.l.node) { |
|
/* the left node is new, try to narrow the result */ |
|
if ((nd.node = _synctex_eq_deepest_container_v2(hitP,nds.l.node))) { |
|
nds.l.node = nd.node; |
|
} |
|
if((nd = _synctex_eq_closest_child_v2(hitP,nds.l.node)).node) { |
|
nds.l.node = nd.node; |
|
} |
|
} |
|
if (nds.r.node) { |
|
/* the right node is new, try to narrow the result */ |
|
if ((nd.node = _synctex_eq_deepest_container_v2(hitP,nds.r.node))) { |
|
nds.r.node = nd.node; |
|
} |
|
if((nd = _synctex_eq_closest_child_v2(hitP,nds.r.node)).node) { |
|
nds.r.node = nd.node; |
|
} |
|
} |
|
} |
|
return nds; |
|
} |
|
#endif |
|
SYNCTEX_INLINE static synctex_nd_lr_s __synctex_eq_get_closest_children_in_vbox_v2(synctex_point_p hitP, synctex_node_p nodeP) { |
|
(void)nodeP; /* unused */ |
|
synctex_nd_lr_s nds = {SYNCTEX_ND_0,SYNCTEX_ND_0}; |
|
synctex_nd_s nd = SYNCTEX_ND_0; |
|
if ((nd.node = synctex_node_child(nd.node))) { |
|
do { |
|
nd = _synctex_point_v_ordered_distance_v2(hitP,nd.node); |
|
/* this is what makes the difference with the h version above */ |
|
if (nd.distance > 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 (nds.r.distance > nd.distance) { |
|
nds.r = nd; |
|
} else if (nds.r.distance == nd.distance && nds.r.node) { |
|
if (_synctex_data_tag(nds.r.node) == _synctex_data_tag(nd.node) |
|
&& (_synctex_data_line(nds.r.node) > _synctex_data_line(nd.node) |
|
|| (_synctex_data_line(nds.r.node) == _synctex_data_line(nd.node) |
|
&& _synctex_data_column(nds.r.node) > _synctex_data_column(nd.node)))) { |
|
nds.r = nd; |
|
} |
|
} |
|
} else if (nd.distance == 0) { |
|
nds.l = nd; |
|
} else { /* here nd < 0 */ |
|
nd.distance = -nd.distance; |
|
if (nds.l.distance > nd.distance) { |
|
nds.l = nd; |
|
} else if (nds.l.distance == nd.distance && nds.l.node) { |
|
if (_synctex_data_tag(nds.l.node) == _synctex_data_tag(nd.node) |
|
&& (_synctex_data_line(nds.l.node) > _synctex_data_line(nd.node) |
|
|| (_synctex_data_line(nds.l.node) == _synctex_data_line(nd.node) |
|
&& _synctex_data_column(nds.l.node) > _synctex_data_column(nd.node)))) { |
|
nds.l = nd; |
|
} |
|
} |
|
} |
|
} while((nd.node = synctex_node_sibling(nd.node))); |
|
if (nds.l.node) { |
|
if ((nd.node = _synctex_eq_deepest_container_v2(hitP,nds.l.node))) { |
|
nds.l.node = nd.node; |
|
} |
|
if((nd = _synctex_eq_closest_child_v2(hitP,nds.l.node)).node) { |
|
nds.l.node = nd.node; |
|
} |
|
} |
|
if (nds.r.node) { |
|
if ((nd.node = _synctex_eq_deepest_container_v2(hitP,nds.r.node))) { |
|
nds.r.node = nd.node; |
|
} |
|
if((nd = _synctex_eq_closest_child_v2(hitP,nds.r.node)).node) { |
|
nds.r.node = nd.node; |
|
} |
|
} |
|
} |
|
return nds; |
|
} |
|
|
|
/** |
|
* Get the child closest to the hit point. |
|
* - parameter: hit point |
|
* - parameter: containing node |
|
* - returns: the child and the distance to the hit point. |
|
* SYNCTEX_ND_0 if the parameter node has no children. |
|
* - note: recursive call. |
|
*/ |
|
static synctex_nd_s __synctex_closest_deep_child_v2(synctex_point_p hitP, synctex_node_p node) { |
|
synctex_nd_s best = SYNCTEX_ND_0; |
|
synctex_node_p child = NULL; |
|
if ((child = synctex_node_child(node))) { |
|
#if defined(SYNCTEX_DEBUG) |
|
printf("Closest deep child on box at line %i\n", |
|
SYNCTEX_LINEINDEX(node)); |
|
#endif |
|
do { |
|
#if defined SYNCTEX_DEBUG && SYNCTEX_DEBUG>500 |
|
synctex_node_display(child); |
|
#endif |
|
synctex_nd_s nd = SYNCTEX_ND_0; |
|
if (_synctex_node_is_box(child)) { |
|
nd = __synctex_closest_deep_child_v2(hitP,child); |
|
} else { |
|
nd = (synctex_nd_s) {child, _synctex_point_node_distance_v2(hitP,child)}; |
|
} |
|
if (nd.distance < best.distance ||(nd.distance == best.distance |
|
&& synctex_node_type(nd.node) != synctex_node_type_kern)) { |
|
#if defined(SYNCTEX_DEBUG) |
|
if(nd.node) { |
|
printf("New best %i<=%i line %i\n",nd.distance, |
|
best.distance,SYNCTEX_LINEINDEX(nd.node)); |
|
} |
|
#endif |
|
best = nd; |
|
} |
|
} while((child = synctex_node_sibling(child))); |
|
#if defined(SYNCTEX_DEBUG) |
|
if(best.node) { |
|
printf("Found new best %i line %i\n",best.distance,SYNCTEX_LINEINDEX(best.node)); |
|
} |
|
#endif |
|
} |
|
return best; |
|
} |
|
|
|
/** |
|
* Return the closest child. |
|
* - parameter: a pointer to the hit point, |
|
* - parameter: the container |
|
* - return: SYNCTEX_ND_0 if node has no child, |
|
* the __synctex_closest_deep_child_v2 otherwise. |
|
*/ |
|
static synctex_nd_s _synctex_eq_closest_child_v2(synctex_point_p hitP, synctex_node_p node) { |
|
synctex_nd_s nd = SYNCTEX_ND_0; |
|
if (_synctex_node_is_box(node)) { |
|
nd = __synctex_closest_deep_child_v2(hitP, node); |
|
if (_synctex_node_is_box(nd.node)) { |
|
synctex_node_p child = NULL; |
|
if ((child = synctex_node_child(nd.node))) { |
|
synctex_nd_s best = {child,_synctex_point_node_distance_v2(hitP,child)}; |
|
while((child = synctex_node_sibling(child))) { |
|
int d = _synctex_point_node_distance_v2(hitP,child); |
|
if (d < best.distance) { |
|
best = (synctex_nd_s){child,d}; |
|
} else if (d == best.distance && synctex_node_type(child) != synctex_node_type_kern) { |
|
best.node = child; |
|
} |
|
} |
|
return best; |
|
} |
|
} |
|
return nd; |
|
} |
|
return SYNCTEX_ND_0; |
|
} |
|
SYNCTEX_INLINE static synctex_nd_lr_s _synctex_eq_get_closest_children_in_box_v2(synctex_point_p hitP, synctex_node_p node) { |
|
synctex_nd_lr_s nds = {SYNCTEX_ND_0,SYNCTEX_ND_0}; |
|
if(_synctex_tree_has_child(node)) { /* node != NULL */ |
|
if (node->class->type==synctex_node_type_hbox || |
|
node->class->type==synctex_node_type_proxy_hbox) { |
|
return __synctex_eq_get_closest_children_in_hbox_v2(hitP,node); |
|
} else { |
|
return __synctex_eq_get_closest_children_in_vbox_v2(hitP,node); |
|
} |
|
} |
|
return nds; |
|
} |
|
|
|
#ifndef SYNCTEX_NO_UPDATER |
|
|
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Updater |
|
# endif |
|
|
|
typedef int (*synctex_print_f)(synctex_updater_p, const char * , ...); /* print formatted to either FILE * or gzFile */ |
|
typedef void (*synctex_close_f)(synctex_updater_p); /* close FILE * or gzFile */ |
|
|
|
# define SYNCTEX_BITS_PER_BYTE 8 |
|
|
|
typedef union { |
|
gzFile as_gzFile; |
|
FILE * as_FILE_p; |
|
void * as_ptr; |
|
} syncex_file_u; |
|
|
|
struct synctex_updater_t { |
|
syncex_file_u file; |
|
synctex_print_f print; |
|
synctex_close_f close; |
|
int length; /* the number of chars appended */ |
|
}; |
|
|
|
static int _synctex_updater_print(synctex_updater_p updater, const char * format, ...) SYNCTEX_PRINTF_FORMAT(2, 3); |
|
static int _synctex_updater_print(synctex_updater_p updater, const char * format, ...) { |
|
int result = 0; |
|
if (updater) { |
|
va_list va; |
|
va_start(va, format); |
|
result = vfprintf(updater->file.as_FILE_p, |
|
format, |
|
va); |
|
va_end(va); |
|
} |
|
return result; |
|
} |
|
#if defined(_MSC_VER) |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <stdarg.h> |
|
|
|
static int vasprintf(char **ret, |
|
const char *format, |
|
va_list ap) |
|
{ |
|
int len; |
|
len = _vsnprintf(NULL, 0, format, ap); |
|
if (len < 0) return -1; |
|
*ret = malloc(len + 1); |
|
if (!*ret) return -1; |
|
_vsnprintf(*ret, len+1, format, ap); |
|
(*ret)[len] = '\0'; |
|
return len; |
|
} |
|
|
|
#endif |
|
|
|
/** |
|
* gzvprintf is not available until OSX 10.10 |
|
*/ |
|
static int _synctex_updater_print_gz(synctex_updater_p updater, const char * format, ...) SYNCTEX_PRINTF_FORMAT(2, 3); |
|
static int _synctex_updater_print_gz(synctex_updater_p updater, const char * format, ...) { |
|
int result = 0; |
|
if (updater) { |
|
char * buffer; |
|
va_list va; |
|
va_start(va, format); |
|
if (vasprintf(&buffer, format, va) < 0) { |
|
_synctex_error("Out of memory..."); |
|
} else if ((result = (int)strlen(buffer))) { |
|
result = gzwrite(updater->file.as_gzFile, buffer, (unsigned)result); |
|
} |
|
va_end(va); |
|
free(buffer); |
|
} |
|
return result; |
|
} |
|
|
|
static void _synctex_updater_close(synctex_updater_p updater) { |
|
if (updater) { |
|
fclose(updater->file.as_FILE_p); |
|
} |
|
} |
|
|
|
static void _synctex_updater_close_gz(synctex_updater_p updater) { |
|
if (updater) { |
|
gzclose(updater->file.as_gzFile); |
|
} |
|
} |
|
|
|
synctex_updater_p synctex_updater_new_with_output_file(const char * output, const char * build_directory) { |
|
synctex_updater_p updater = NULL; |
|
const char * mode = NULL; |
|
synctex_open_s open; |
|
/* prepare the updater, the memory is the only one dynamically allocated */ |
|
updater = (synctex_updater_p)_synctex_malloc(sizeof(synctex_updater_s)); |
|
if (NULL == updater) { |
|
_synctex_error("! synctex_updater_new_with_file: malloc problem"); |
|
return NULL; |
|
} |
|
open = _synctex_open_v2(output,build_directory,0,synctex_ADD_QUOTES); |
|
if (open.status < SYNCTEX_STATUS_OK) { |
|
open = _synctex_open_v2(output,build_directory,0,synctex_DONT_ADD_QUOTES); |
|
if (open.status < SYNCTEX_STATUS_OK) { |
|
return_on_error: |
|
_synctex_free(updater); |
|
return updater = 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(open.file); |
|
updater->file.as_ptr = NULL; |
|
mode = _synctex_get_io_mode_name(open.io_mode|synctex_io_append_mask);/* either "a" or "ab", depending on the file extension */ |
|
if (open.io_mode&synctex_io_gz_mask) { |
|
if (NULL == (updater->file.as_FILE_p = fopen(open.synctex,mode))) { |
|
no_write_error: |
|
_synctex_error("! synctex_updater_new_with_file: Can't append to %s",open.synctex); |
|
free(open.synctex); |
|
goto return_on_error; |
|
} |
|
updater->print = &_synctex_updater_print; |
|
updater->close = &_synctex_updater_close; |
|
} else { |
|
if (NULL == (updater->file.as_gzFile = gzopen(open.synctex,mode))) { |
|
goto no_write_error; |
|
} |
|
updater->print = &_synctex_updater_print_gz; |
|
updater->close = &_synctex_updater_close_gz; |
|
} |
|
printf("SyncTeX: updating %s...",open.synctex); |
|
_synctex_free(open.synctex); |
|
return updater; |
|
} |
|
|
|
void synctex_updater_append_magnification(synctex_updater_p updater, char * magnification){ |
|
if (NULL==updater) { |
|
return; |
|
} |
|
if (magnification && strlen(magnification)) { |
|
updater->length += |
|
updater->print(updater,"Magnification:%s\n",magnification); |
|
} |
|
} |
|
|
|
void synctex_updater_append_x_offset(synctex_updater_p updater, char * x_offset){ |
|
if (NULL==updater) { |
|
return; |
|
} |
|
if (x_offset && strlen(x_offset)) { |
|
updater->length += updater->print(updater,"X Offset:%s\n",x_offset); |
|
} |
|
} |
|
|
|
void synctex_updater_append_y_offset(synctex_updater_p updater, char * y_offset){ |
|
if (NULL==updater) { |
|
return; |
|
} |
|
if (y_offset && strlen(y_offset)) { |
|
updater->length += updater->print(updater,"Y Offset:%s\n",y_offset); |
|
} |
|
} |
|
|
|
void synctex_updater_free(synctex_updater_p updater){ |
|
if (NULL==updater) { |
|
return; |
|
} |
|
if (updater->length>0) { |
|
updater->print(updater,"!%i\n",updater->length); |
|
} |
|
updater->close(updater); |
|
_synctex_free(updater); |
|
printf("... done.\n"); |
|
return; |
|
} |
|
#endif |
|
|
|
#if defined(SYNCTEX_TESTING) |
|
# ifdef SYNCTEX_NOTHING |
|
# pragma mark - |
|
# pragma mark Testers |
|
# endif |
|
static int _synctex_input_copy_name(synctex_node_p input, char * name) { |
|
char * copy = _synctex_malloc(strlen(name)+1); |
|
memcpy(copy,name,strlen(name)+1); |
|
_synctex_data_set_name(input,copy); |
|
return 0; |
|
} |
|
int synctex_test_setup_scanner_sheets_421(synctex_scanner_p scanner) { |
|
int TC = 0; |
|
synctex_node_p sheet = synctex_node_new(scanner,synctex_node_type_sheet); |
|
_synctex_data_set_page(sheet,4); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_page(sheet)==4,""); |
|
synctex_node_free(scanner->sheet); |
|
scanner->sheet = sheet; |
|
sheet = synctex_node_new(scanner,synctex_node_type_sheet); |
|
_synctex_data_set_page(sheet,2); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_page(sheet)==2,""); |
|
__synctex_tree_set_sibling(sheet, scanner->sheet); |
|
scanner->sheet = sheet; |
|
sheet = synctex_node_new(scanner,synctex_node_type_sheet); |
|
_synctex_data_set_page(sheet,1); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_page(sheet)==1,""); |
|
__synctex_tree_set_sibling(sheet, scanner->sheet); |
|
scanner->sheet = sheet; |
|
return TC; |
|
} |
|
int synctex_test_input(synctex_scanner_p scanner) { |
|
int TC = 0; |
|
synctex_node_p input = synctex_node_new(scanner,synctex_node_type_input); |
|
_synctex_data_set_tag(input,421); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_tag(input)==421,""); |
|
_synctex_data_set_tag(input,124); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_tag(input)==124,""); |
|
_synctex_data_set_line(input,421); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_line(input)==421,""); |
|
_synctex_data_set_line(input,214); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_line(input)==214,""); |
|
_synctex_data_set_line(input,214); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_line(input)==214,""); |
|
_synctex_input_copy_name(input,"214"); |
|
SYNCTEX_TEST_BODY(TC, 0==memcmp(_synctex_data_name(input),"214",4),""); |
|
_synctex_input_copy_name(input,"421421"); |
|
|
|
SYNCTEX_TEST_BODY(TC, |
|
0==memcmp(_synctex_data_name(input), |
|
"421421", |
|
4), |
|
""); |
|
synctex_node_free(input); |
|
return TC; |
|
} |
|
int synctex_test_proxy(synctex_scanner_p scanner) { |
|
int TC = 0; |
|
synctex_node_p proxy = synctex_node_new(scanner,synctex_node_type_proxy); |
|
synctex_node_p target = synctex_node_new(scanner,synctex_node_type_rule); |
|
_synctex_tree_set_target(proxy,target); |
|
_synctex_data_set_tag(target,421); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_tag(target)==421,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_tag(target)==421,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_tag(proxy)==421,""); |
|
synctex_node_free(proxy); |
|
synctex_node_free(target); |
|
return TC; |
|
} |
|
int synctex_test_handle(synctex_scanner_p scanner) { |
|
int TC = 0; |
|
synctex_node_p handle = synctex_node_new(scanner,synctex_node_type_handle); |
|
synctex_node_p proxy = synctex_node_new(scanner, synctex_node_type_proxy); |
|
synctex_node_p target = synctex_node_new(scanner,synctex_node_type_rule); |
|
_synctex_tree_set_target(handle,target); |
|
_synctex_data_set_tag(target,421); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_tag(target)==421,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_tag(target)==421,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_tag(handle)==421,""); |
|
_synctex_data_set_line(target,214); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_line(target)==214,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_line(target)==214,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_line(handle)==214,""); |
|
_synctex_data_set_column(target,142); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_column(target)==142,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_column(target)==142,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_column(handle)==142,""); |
|
_synctex_tree_set_target(proxy,target); |
|
_synctex_tree_set_target(handle,proxy); |
|
_synctex_data_set_tag(target,412); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_tag(target)==412,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_tag(target)==412,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_tag(handle)==412,""); |
|
_synctex_data_set_line(target,124); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_line(target)==124,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_line(target)==124,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_line(handle)==124,""); |
|
_synctex_data_set_column(target,241); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_column(target)==241,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_column(target)==241,""); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_column(handle)==241,""); |
|
synctex_node_free(handle); |
|
synctex_node_free(proxy); |
|
synctex_node_free(target); |
|
return TC; |
|
} |
|
int synctex_test_setup_scanner_input(synctex_scanner_p scanner) { |
|
int TC = 0; |
|
synctex_node_p input = synctex_node_new(scanner,synctex_node_type_input); |
|
_synctex_data_set_tag(input,4); |
|
_synctex_input_copy_name(input,"21"); |
|
_synctex_data_set_line(input,421); |
|
synctex_node_free(scanner->input); |
|
scanner->input = input; |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_tag(input)==4,""); |
|
SYNCTEX_TEST_BODY(TC, strcmp(_synctex_data_name(input),"21")==0,""); |
|
SYNCTEX_TEST_BODY(TC, _synctex_data_line(input)==421,""); |
|
return TC; |
|
} |
|
int synctex_test_setup_nodes(synctex_scanner_p scanner, synctex_node_r nodes) { |
|
int TC = 0; |
|
int n; |
|
for (n=0;n<synctex_node_number_of_types;++n) { |
|
nodes[n] = synctex_node_new(scanner,n); |
|
SYNCTEX_TEST_BODY(TC, nodes[n]!=NULL,""); |
|
} |
|
return TC; |
|
} |
|
int synctex_test_teardown_nodes(synctex_scanner_p scanner, synctex_node_r nodes) { |
|
int n; |
|
for (n=0;n<synctex_node_number_of_types;++n) { |
|
synctex_node_free(nodes[n]); |
|
nodes[n]=NULL; |
|
} |
|
return 1; |
|
} |
|
int synctex_test_tree(synctex_scanner_p scanner) { |
|
int TC = 0; |
|
synctex_node_p nodes1[synctex_node_number_of_types]; |
|
synctex_node_p nodes2[synctex_node_number_of_types]; |
|
synctex_node_p nodes3[synctex_node_number_of_types]; |
|
int i,j; |
|
TC += synctex_test_setup_nodes(scanner,nodes1); |
|
TC += synctex_test_setup_nodes(scanner,nodes2); |
|
TC += synctex_test_setup_nodes(scanner,nodes3); |
|
/* Every node has a sibling */ |
|
for (i=0;i<synctex_node_number_of_types;++i) { |
|
for (j=0;j<synctex_node_number_of_types;++j) { |
|
_synctex_tree_set_sibling(nodes1[i],nodes2[i]); |
|
SYNCTEX_TEST_BODY(TC, nodes2[i]==synctex_node_sibling(nodes1[i]),""); |
|
} |
|
} |
|
synctex_test_teardown_nodes(scanner,nodes3); |
|
synctex_test_teardown_nodes(scanner,nodes2); |
|
synctex_test_teardown_nodes(scanner,nodes1); |
|
return TC; |
|
} |
|
int synctex_test_page(synctex_scanner_p scanner) { |
|
int TC = synctex_test_setup_scanner_sheets_421(scanner); |
|
synctex_node_p sheet = scanner->sheet; |
|
synctex_node_p node = synctex_node_new(scanner,synctex_node_type_rule); |
|
_synctex_data_set_tag(node,4); |
|
_synctex_data_set_line(node,21); |
|
synctex_node_free(_synctex_node_set_child(sheet,node)); |
|
SYNCTEX_TEST_BODY(TC, synctex_node_page(node)==synctex_node_page(sheet),""); |
|
return TC; |
|
} |
|
int synctex_test_display_query(synctex_scanner_p scanner) { |
|
int TC = synctex_test_setup_scanner_sheets_421(scanner); |
|
synctex_node_p sheet = scanner->sheet; |
|
synctex_node_p node = synctex_node_new(scanner,synctex_node_type_rule); |
|
_synctex_data_set_tag(node,4); |
|
_synctex_data_set_line(node,21); |
|
synctex_node_free(_synctex_node_set_child(sheet,node)); |
|
SYNCTEX_TEST_BODY(TC, node==synctex_node_child(sheet),""); |
|
__synctex_node_make_friend_tlc(node); |
|
SYNCTEX_TEST_BODY(TC, _synctex_scanner_friend(scanner, 25)==node,""); |
|
sheet = __synctex_tree_sibling(sheet); |
|
node = synctex_node_new(scanner,synctex_node_type_rule); |
|
_synctex_data_set_tag(node,4); |
|
_synctex_data_set_line(node,21); |
|
synctex_node_free(_synctex_node_set_child(sheet,node)); |
|
SYNCTEX_TEST_BODY(TC, node==synctex_node_child(sheet),""); |
|
__synctex_node_make_friend_tlc(node); |
|
SYNCTEX_TEST_BODY(TC, _synctex_scanner_friend(scanner, 25)==node,""); |
|
sheet = __synctex_tree_sibling(sheet); |
|
node = synctex_node_new(scanner,synctex_node_type_rule); |
|
_synctex_data_set_tag(node,4); |
|
_synctex_data_set_line(node,21); |
|
synctex_node_free(_synctex_node_set_child(sheet,node)); |
|
SYNCTEX_TEST_BODY(TC, node==synctex_node_child(sheet),""); |
|
__synctex_node_make_friend_tlc(node); |
|
SYNCTEX_TEST_BODY(TC, (_synctex_scanner_friend(scanner, 25)==node),""); |
|
synctex_test_setup_scanner_input(scanner); |
|
scanner->flags.has_parsed = synctex_YES; |
|
#if 1 |
|
SYNCTEX_TEST_BODY(TC, (synctex_display_query(scanner,"21",21,4,-1)==3),""); |
|
#endif |
|
return TC; |
|
} |
|
typedef struct { |
|
int s; /* status */ |
|
char n[25]; /* name */ |
|
} synctex_test_sn_s; |
|
|
|
synctex_test_sn_s synctex_test_tmp_sn(char * content) { |
|
synctex_test_sn_s sn = {0, "/tmp/test.XXXXXX.synctex"}; |
|
FILE *sfp; |
|
int fd = mkstemps(sn.n,8); |
|
if (fd < 0) { |
|
fprintf(stderr, "%s: %s\n", sn.n, strerror(errno)); |
|
sn.s = -1; |
|
return sn; |
|
} |
|
if ((sfp = fdopen(fd, "w+")) == NULL) { |
|
unlink(sn.n); |
|
close(fd); |
|
fprintf(stderr, "%s: %s\n", sn.n, strerror(errno)); |
|
sn.s = -2; |
|
return sn; |
|
} |
|
sn.s = fputs(content,sfp); |
|
printf("temp:%s\n%i\n",sn.n,sn.s); |
|
fclose(sfp); |
|
if (sn.s==0) { |
|
sn.s = -2; |
|
unlink(sn.n); |
|
} |
|
return sn; |
|
} |
|
int synctex_test_sheet_1() { |
|
int TC = 0; |
|
char * content = |
|
"SyncTeX Version:1 \n" /*00-19*/ |
|
"Input:1:./1.tex \n" /*20-39*/ |
|
"Output:pdf \n" /*40-59*/ |
|
"Magnification:100000000 \n" /*60-89*/ |
|
"Unit:1 \n" /*90-99*/ |
|
"X Offset:0 \n" /*00-19*/ |
|
"Y Offset:0 \n" /*20-39*/ |
|
"Content: \n" /*40-49*/ |
|
"{1 \n" /*50-59*/ |
|
"[1,10:20,350:330,330,0 \n" /*60-89*/ |
|
"] \n" /*90-99*/ |
|
"} \n" /*00-09*/ |
|
"Postamble:\n"; |
|
synctex_test_sn_s sn = synctex_test_tmp_sn(content); |
|
if (sn.s>0) { |
|
synctex_scanner_p scanner = synctex_scanner_new_with_output_file(sn.n, NULL, synctex_YES); |
|
synctex_node_p node = synctex_scanner_handle(scanner); |
|
printf("Created nodes:\n"); |
|
while (node) { |
|
printf("%s\n",_synctex_node_abstract(node)); |
|
node = synctex_node_next(node); |
|
} |
|
synctex_scanner_free(scanner); |
|
unlink(sn.n); |
|
} else { |
|
++TC; |
|
} |
|
return TC; |
|
} |
|
int synctex_test_sheet_2() { |
|
int TC = 0; |
|
char * content = |
|
"SyncTeX Version:1 \n" /*00-19*/ |
|
"Input:1:./1.tex \n" /*20-39*/ |
|
"Output:pdf \n" /*40-59*/ |
|
"Magnification:100000000 \n" /*60-89*/ |
|
"Unit:1 \n" /*90-99*/ |
|
"X Offset:0 \n" /*00-19*/ |
|
"Y Offset:0 \n" /*20-39*/ |
|
"Content: \n" /*40-49*/ |
|
"{1 \n" /*50-59*/ |
|
"(1,10:20,350:330,330,0 \n" /*60-89*/ |
|
") \n" /*90-99*/ |
|
"} \n" /*00-09*/ |
|
"Postamble:\n"; |
|
synctex_test_sn_s sn = synctex_test_tmp_sn(content); |
|
if (sn.s>0) { |
|
synctex_scanner_p scanner = synctex_scanner_new_with_output_file(sn.n, NULL, synctex_YES); |
|
synctex_node_p node = synctex_scanner_handle(scanner); |
|
printf("Created nodes:\n"); |
|
while (node) { |
|
printf("%s\n",_synctex_node_abstract(node)); |
|
node = _synctex_node_next(node); |
|
} |
|
TC += synctex_scanner_free(scanner); |
|
unlink(sn.n); |
|
} else { |
|
++TC; |
|
} |
|
return TC; |
|
} |
|
int synctex_test_charindex() { |
|
int TC = 0; |
|
char * content = |
|
"SyncTeX Version:1 \n" /*00-19*/ |
|
"Input:1:./1.tex \n" /*20-39*/ |
|
"Output:pdf \n" /*40-59*/ |
|
"Magnification:100000000 \n" /*60-89*/ |
|
"Unit:1 \n" /*90-99*/ |
|
"X Offset:0 \n" /*00-19*/ |
|
"Y Offset:0 \n" /*20-39*/ |
|
"Content: \n" /*40-49*/ |
|
"{1 \n" /*50-59*/ |
|
"[1,10:20,350:330,330,0 \n" /*60-89*/ |
|
"(1,58:20,100:250,10,5 \n" /*90-119*/ |
|
"f1000:50,100 \n" /*20-39*/ |
|
") \n" /*40-49*/ |
|
"] \n" /*50-59*/ |
|
"} \n" /*60-69*/ |
|
"Postamble:\n"; |
|
synctex_test_sn_s sn = synctex_test_tmp_sn(content); |
|
if (sn.s>0) { |
|
synctex_scanner_p scanner = synctex_scanner_new_with_output_file(sn.n, NULL, synctex_YES); |
|
synctex_node_p node = synctex_scanner_handle(scanner); |
|
printf("Created nodes:\n"); |
|
while (node) { |
|
printf("%s\n",_synctex_node_abstract(node)); |
|
node = synctex_node_next(node); |
|
} |
|
TC += synctex_scanner_free(scanner); |
|
unlink(sn.n); |
|
} else { |
|
++TC; |
|
} |
|
return TC; |
|
} |
|
int synctex_test_form() { |
|
int TC = 0; |
|
char * content = |
|
"SyncTeX Version:1 \n" /*00-19*/ |
|
"Input:1:./1.tex \n" /*20-39*/ |
|
"Output:pdf \n" /*40-59*/ |
|
"Magnification:100000000 \n" /*60-89*/ |
|
"Unit:1 \n" /*90-99*/ |
|
"X Offset:0 \n" /*00-19*/ |
|
"Y Offset:0 \n" /*20-39*/ |
|
"Content: \n" /*40-49*/ |
|
"{1 \n" /*50-59*/ |
|
"[1,10:20,350:330,330,0 \n" /*60-89*/ |
|
"(1,58:20,100:250,10,5 \n" /*90-119*/ |
|
"f1000:50,100 \n" /*20-39*/ |
|
") \n" /*40-49*/ |
|
"] \n" /*50-59*/ |
|
"} \n" /*60-69*/ |
|
"<1000 \n" /*70-79*/ |
|
"(1,63:0,0:100,8,3 \n" /*80-99*/ |
|
") \n" /*00-09*/ |
|
"> \n" /*10-19*/ |
|
"Postamble:\n"; |
|
synctex_test_sn_s sn = synctex_test_tmp_sn(content); |
|
if (sn.s>0) { |
|
synctex_scanner_p scanner = synctex_scanner_new_with_output_file(sn.n, NULL, synctex_YES); |
|
synctex_node_p node = synctex_scanner_handle(scanner); |
|
while (node) { |
|
printf("%s\n",_synctex_node_abstract(node)); |
|
node = _synctex_node_next(node); |
|
} |
|
TC += synctex_scanner_free(scanner); |
|
unlink(sn.n); |
|
} else { |
|
++TC; |
|
} |
|
return TC; |
|
} |
|
#endif
|
|
|