diff --git a/Makefile b/Makefile index f7b5ad2..73365e8 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ include config.mk include colors.mk include common.mk -OSOURCE = $(filter-out css-definitions.c, $(filter-out dbus-interface-definitions.c, $(wildcard *.c))) -HEADER = $(wildcard *.h) +OSOURCE = $(filter-out css-definitions.c, $(filter-out dbus-interface-definitions.c, $(wildcard *.c))) $(wildcard synctex/*.c) +HEADER = $(wildcard *.h) $(wildcard synctex/*.h) HEADERINST = version.h document.h macros.h page.h types.h plugin-api.h links.h ifneq (${WITH_SQLITE},0) @@ -36,6 +36,13 @@ endif ifeq (,$(findstring -DLOCALEDIR,${CPPFLAGS})) CPPFLAGS += -DLOCALEDIR=\"${LOCALEDIR}\" endif +ifeq (,$(findstring -Isynctex,${CPPFLAGS})) +CPPFLAGS += -Isynctex +endif +ifeq (,$(findstring -DSYNCTEX_VERBOSE=0,${CPPFLAGS})) +CPPFLAGS += -DSYNCTEX_VERBOSE=0 +endif + OBJECTS = $(patsubst %.c, %.o, $(SOURCE)) dbus-interface-definitions.o css-definitions.o DOBJECTS = $(patsubst %.o, %.do, $(OBJECTS)) @@ -83,7 +90,7 @@ css-definitions.c: data/zathura.css_t %.o: %.c $(call colorecho,CC,$<) - $(QUIET) mkdir -p .depend + $(QUIET) mkdir -p $(shell dirname .depend/$@.dep) $(QUIET)${CC} -c ${CPPFLAGS} ${CFLAGS} -o $@ $< -MMD -MF .depend/$@.dep %.do: %.c diff --git a/adjustment.c b/adjustment.c index 8d2b513..1cd31b3 100644 --- a/adjustment.c +++ b/adjustment.c @@ -63,9 +63,17 @@ position_to_page_number(zathura_document_t* document, double pos_x, unsigned int c0 = zathura_document_get_first_page_column(document); unsigned int npag = zathura_document_get_number_of_pages(document); unsigned int ncol = zathura_document_get_pages_per_row(document); - unsigned int nrow = (npag + c0 - 1 + ncol - 1) / ncol; /* number of rows */ + unsigned int nrow = 0; unsigned int pad = zathura_document_get_page_padding(document); + if (c0 == 1) { + /* There is no offset, so this is easy. */ + nrow = (npag + ncol - 1) / ncol; + } else { + /* If there is a offset, we handle the first row extra. */ + nrow = 1 + (npag - (ncol - c0 - 1) + (ncol - 1)) / ncol; + } + unsigned int col = floor(pos_x * (double)doc_width / (double)(cell_width + pad)); unsigned int row = floor(pos_y * (double)doc_height / (double)(cell_height + pad)); @@ -73,7 +81,7 @@ position_to_page_number(zathura_document_t* document, double pos_x, if (page < c0 - 1) { return 0; } else { - return page - (c0 - 1); + return MIN(page - (c0 - 1), npag - 1); } } diff --git a/callbacks.c b/callbacks.c index a13e50d..43d47fe 100644 --- a/callbacks.c +++ b/callbacks.c @@ -137,32 +137,45 @@ cb_view_vadjustment_value_changed(GtkAdjustment* adjustment, gpointer data) statusbar_page_number_update(zathura); } +static void +cb_view_adjustment_changed(GtkAdjustment* adjustment, zathura_t* zathura, + bool width) +{ + /* Do nothing in index mode */ + if (girara_mode_get(zathura->ui.session) == zathura->modes.index) { + return; + } + + const zathura_adjust_mode_t adjust_mode = + zathura_document_get_adjust_mode(zathura->document); + + /* Don't scroll, we're focusing the inputbar. */ + if (adjust_mode == ZATHURA_ADJUST_INPUTBAR) { + return; + } + + /* Save the viewport size */ + unsigned int size = (unsigned int)floor(gtk_adjustment_get_page_size(adjustment)); + if (width == true) { + zathura_document_set_viewport_width(zathura->document, size); + } else { + zathura_document_set_viewport_height(zathura->document, size); + } + + /* reset the adjustment, in case bounds have changed */ + const double ratio = width == true ? + zathura_document_get_position_x(zathura->document) : + zathura_document_get_position_x(zathura->document); + zathura_adjustment_set_value_from_ratio(adjustment, ratio); +} + void cb_view_hadjustment_changed(GtkAdjustment* adjustment, gpointer data) { zathura_t* zathura = data; g_return_if_fail(zathura != NULL); - zathura_adjust_mode_t adjust_mode = - zathura_document_get_adjust_mode(zathura->document); - - /* Do nothing in index mode */ - if (girara_mode_get(zathura->ui.session) == zathura->modes.index) { - return; - } - - /* Don't scroll we're focusing the inputbar. */ - if (adjust_mode == ZATHURA_ADJUST_INPUTBAR) { - return; - } - - /* save the viewport size */ - unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(adjustment)); - zathura_document_set_viewport_width(zathura->document, view_width); - - /* reset the adjustment, in case bounds have changed */ - double ratio = zathura_document_get_position_x(zathura->document); - zathura_adjustment_set_value_from_ratio(adjustment, ratio); + cb_view_adjustment_changed(adjustment, zathura, true); } void @@ -171,26 +184,7 @@ cb_view_vadjustment_changed(GtkAdjustment* adjustment, gpointer data) zathura_t* zathura = data; g_return_if_fail(zathura != NULL); - zathura_adjust_mode_t adjust_mode = - zathura_document_get_adjust_mode(zathura->document); - - /* Do nothing in index mode */ - if (girara_mode_get(zathura->ui.session) == zathura->modes.index) { - return; - } - - /* Don't scroll we're focusing the inputbar. */ - if (adjust_mode == ZATHURA_ADJUST_INPUTBAR) { - return; - } - - /* save the viewport size */ - unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(adjustment)); - zathura_document_set_viewport_height(zathura->document, view_height); - - /* reset the adjustment, in case bounds have changed */ - double ratio = zathura_document_get_position_y(zathura->document); - zathura_adjustment_set_value_from_ratio(adjustment, ratio); + cb_view_adjustment_changed(adjustment, zathura, false); } void @@ -233,7 +227,7 @@ cb_page_layout_value_changed(girara_session_t* session, const char* name, girara if (pages_per_row == 0) { pages_per_row = 1; girara_setting_set(session, name, &pages_per_row); - girara_notify(session, GIRARA_WARNING, _("'%s' must not be 0. Set to 1.."), name); + girara_notify(session, GIRARA_WARNING, _("'%s' must not be 0. Set to 1."), name); return; } } diff --git a/config.mk b/config.mk index f3dccc4..253dded 100644 --- a/config.mk +++ b/config.mk @@ -77,8 +77,11 @@ MAGIC_INC ?= MAGIC_LIB ?= -lmagic endif -INCS = ${GIRARA_INC} ${GTK_INC} ${GTHREAD_INC} ${GMODULE_INC} ${GLIB_INC} -LIBS = ${GIRARA_LIB} ${GTK_LIB} ${GTHREAD_LIB} ${GMODULE_LIB} ${GLIB_LIB} -lpthread -lm +ZLIB_INC ?= $(shell pkg-config --cflags zlib) +ZLIB_LIB ?= $(shell pkg-config --libs zlib) + +INCS = ${GIRARA_INC} ${GTK_INC} ${GTHREAD_INC} ${GMODULE_INC} ${GLIB_INC} $(ZLIB_INC) +LIBS = ${GIRARA_LIB} ${GTK_LIB} ${GTHREAD_LIB} ${GMODULE_LIB} ${GLIB_LIB} $(ZLIB_LIB) -lpthread -lm # flags CFLAGS += -std=c99 -pedantic -Wall -Wno-format-zero-length -Wextra $(INCS) diff --git a/dbus-interface.c b/dbus-interface.c index e9409ab..5e965f6 100644 --- a/dbus-interface.c +++ b/dbus-interface.c @@ -545,17 +545,17 @@ zathura_dbus_goto_page_and_highlight(const char* filename, unsigned int page, } bool -zathura_dbus_synctex_position(const char* filename, const char* position, - pid_t hint) +zathura_dbus_synctex_position(const char* filename, const char* input_file, + int line, int column, pid_t hint) { - if (filename == NULL || position == NULL) { + if (filename == NULL || input_file == NULL) { return false; } unsigned int page = 0; girara_list_t* secondary_rects = NULL; girara_list_t* rectangles = synctex_rectangles_from_position( - filename, position, &page, &secondary_rects); + filename, input_file, line, column, &page, &secondary_rects); if (rectangles == NULL) { return false; } diff --git a/dbus-interface.h b/dbus-interface.h index 4e0f929..1f5b2c5 100644 --- a/dbus-interface.h +++ b/dbus-interface.h @@ -57,7 +57,7 @@ bool zathura_dbus_goto_page_and_highlight(const char* filename, unsigned int page, girara_list_t* rectangles, girara_list_t* secondary_rects, pid_t pidhint); -bool zathura_dbus_synctex_position(const char* filename, const char* position, - pid_t pidhint); +bool zathura_dbus_synctex_position(const char* filename, const char* input_file, + int line, int column, pid_t hint); #endif diff --git a/main.c b/main.c index 002860e..4762371 100644 --- a/main.c +++ b/main.c @@ -106,11 +106,25 @@ main(int argc, char* argv[]) return -1; } - if (zathura_dbus_synctex_position(real_path, synctex_fwd, synctex_pid) == true) { + char** split_fwd = g_strsplit(synctex_fwd, ":", 0); + if (split_fwd == NULL || split_fwd[0] == NULL || split_fwd[1] == NULL || + split_fwd[2] == NULL || split_fwd[3] != NULL) { + girara_error("Failed to parse argument to --synctex-forward."); + free(real_path); + g_strfreev(split_fwd); + return -1; + } + + const int line = MIN(INT_MAX, g_ascii_strtoll(split_fwd[0], NULL, 10)); + const int column = MIN(INT_MAX, g_ascii_strtoll(split_fwd[1], NULL, 10)); + const bool ret = zathura_dbus_synctex_position(real_path, split_fwd[2], line, column, synctex_pid); + g_strfreev(split_fwd); + + if (ret == true) { free(real_path); return 0; } else { - girara_error("Could not find open instance for '%s' or got no usable output from 'synctex view'.", real_path); + girara_error("Could not find open instance for '%s' or got no usable data from synctex.", real_path); free(real_path); return -1; } diff --git a/synctex.c b/synctex.c index 949ac54..c71c4b6 100644 --- a/synctex.c +++ b/synctex.c @@ -8,49 +8,12 @@ #include "page.h" #include "document.h" #include "utils.h" - -enum { - SYNCTEX_RESULT_BEGIN = 1, - SYNCTEX_RESULT_END, - SYNCTEX_PROP_PAGE, - SYNCTEX_PROP_H, - SYNCTEX_PROP_V, - SYNCTEX_PROP_WIDTH, - SYNCTEX_PROP_HEIGHT, -}; - -typedef struct token_s { - const char* name; - guint token; -} token_t; - -static token_t scanner_tokens[] = { - {"SyncTeX result begin", SYNCTEX_RESULT_BEGIN}, - {"SyncTeX result end", SYNCTEX_RESULT_END}, - {"Page:", SYNCTEX_PROP_PAGE}, - {"h:", SYNCTEX_PROP_H}, - {"v:", SYNCTEX_PROP_V}, - {"W:", SYNCTEX_PROP_WIDTH}, - {"H:", SYNCTEX_PROP_HEIGHT}, - {NULL, 0} -}; - -static GScannerConfig scanner_config = { - .cset_skip_characters = "\n\r", - .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z, - .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ": ", - .cpair_comment_single = NULL, - .case_sensitive = TRUE, - .scan_identifier = TRUE, - .scan_symbols = TRUE, - .scan_float = TRUE, - .numbers_2_int = TRUE, -}; +#include "synctex/synctex_parser.h" void synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y) { - if (zathura == NULL || page == NULL) { + if (zathura == NULL || page == NULL || zathura->synctex.editor == NULL) { return; } @@ -64,212 +27,123 @@ synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y) return; } - char** argv = g_try_malloc0(sizeof(char*) * (zathura->synctex.editor != NULL ? - 7 : 5)); - if (argv == NULL) { + synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1); + if (scanner == NULL) { + girara_debug("Failed to create synctex scanner."); return; } - argv[0] = g_strdup("synctex"); - argv[1] = g_strdup("edit"); - argv[2] = g_strdup("-o"); - argv[3] = g_strdup_printf("%d:%d:%d:%s", zathura_page_get_index(page) + 1, x, - y, filename); - if (zathura->synctex.editor != NULL) { - argv[4] = g_strdup("-x"); - argv[5] = g_strdup(zathura->synctex.editor); + synctex_scanner_t temp = synctex_scanner_parse(scanner); + if (temp == NULL) { + girara_debug("Failed to parse synctex file."); + synctex_scanner_free(scanner); + return; } - g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); - g_strfreev(argv); -} + if (synctex_edit_query(scanner, zathura_page_get_index(page) + 1, x, y) > 0) { + /* Assume that a backward search returns either at most one result. */ + synctex_node_t node = synctex_next_result(scanner); + if (node != NULL) { + const char* input_file = synctex_scanner_get_name(scanner, synctex_node_tag(node)); + const int line = synctex_node_line(node); + const int column = synctex_node_column (node); -static double -scan_float(GScanner* scanner) -{ - switch (g_scanner_get_next_token(scanner)) { - case G_TOKEN_FLOAT: - return g_scanner_cur_value(scanner).v_float; - case G_TOKEN_INT: - return g_scanner_cur_value(scanner).v_int; - default: - return 0.0; + char* linestr = g_strdup_printf("%d", line); + char* columnstr = g_strdup_printf("%d", column); + + gchar** argv = NULL; + gint argc = 0; + if (g_shell_parse_argv(zathura->synctex.editor, &argc, &argv, NULL) == TRUE) { + for (gint i = 0; i != argc; ++i) { + char* temp = girara_replace_substring(argv[i], "%{line}", linestr); + g_free(argv[i]); + argv[i] = temp; + temp = girara_replace_substring(argv[i], "%{column}", columnstr); + g_free(argv[i]); + argv[i] = temp; + temp = girara_replace_substring(argv[i], "%{input}", input_file); + g_free(argv[i]); + argv[i] = temp; + } + + g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); + g_strfreev(argv); + } + + g_free(linestr); + g_free(columnstr); + } } + + synctex_scanner_free(scanner); } girara_list_t* -synctex_rectangles_from_position(const char* filename, const char* position, - unsigned int* page, +synctex_rectangles_from_position(const char* filename, const char* input_file, + int line, int column, unsigned int* page, girara_list_t** secondary_rects) { - if (filename == NULL || position == NULL || page == NULL) { + if (filename == NULL || input_file == NULL || page == NULL) { return NULL; } - char** argv = g_try_malloc0(sizeof(char*) * 7); - if (argv == NULL) { + synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1); + if (scanner == NULL) { + girara_debug("Failed to create synctex scanner."); return NULL; } - argv[0] = g_strdup("synctex"); - argv[1] = g_strdup("view"); - argv[2] = g_strdup("-i"); - argv[3] = g_strdup(position); - argv[4] = g_strdup("-o"); - argv[5] = g_strdup(filename); - - gint output = -1; - bool ret = g_spawn_async_with_pipes(NULL, argv, NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL, - &output, NULL, NULL); - g_strfreev(argv); - - if (ret == false) { - return false; + synctex_scanner_t temp = synctex_scanner_parse(scanner); + if (temp == NULL) { + girara_debug("Failed to parse synctex file."); + synctex_scanner_free(scanner); + return NULL; } - GScanner* scanner = g_scanner_new(&scanner_config); - token_t* tokens = scanner_tokens; - while (tokens->name != NULL) { - g_scanner_add_symbol(scanner, tokens->name, GINT_TO_POINTER(tokens->token)); - tokens++; - } - - g_scanner_input_file(scanner, output); - - bool found_begin = false, found_end = false; - while (found_begin == false && found_end == false) { - switch (g_scanner_get_next_token(scanner)) { - case G_TOKEN_EOF: - found_end = true; - break; - - case G_TOKEN_SYMBOL: - switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) { - case SYNCTEX_RESULT_BEGIN: - found_begin = true; - break; - } - break; - - default: - /* skip everything else */ - break; - } - } - - ret = false; - unsigned int rpage = 0; - unsigned int current_page = 0; girara_list_t* hitlist = girara_list_new2(g_free); girara_list_t* other_rects = girara_list_new2(g_free); - bool got_rect = false; - zathura_rectangle_t rectangle; - while (found_end == false) { - switch (g_scanner_get_next_token(scanner)) { - case G_TOKEN_EOF: - found_end = true; - break; + if (synctex_display_query(scanner, input_file, line, column) > 0) { + synctex_node_t node = NULL; + bool got_page = false; - case G_TOKEN_SYMBOL: - switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) { - case SYNCTEX_RESULT_END: - found_end = true; - break; - - case SYNCTEX_PROP_PAGE: - if (g_scanner_get_next_token(scanner) == G_TOKEN_INT) { - current_page = g_scanner_cur_value(scanner).v_int - 1; - if (ret == false) { - ret = true; - rpage = current_page; - } - - if (got_rect == false) { - continue; - } - got_rect = false; - - if (*page == current_page) { - zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t)); - if (real_rect == NULL) { - continue; - } - - *real_rect = rectangle; - girara_list_append(hitlist, real_rect); - } else { - synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t)); - if (page_rect == NULL) { - continue; - } - - page_rect->page = current_page; - page_rect->rect = rectangle; - - girara_list_append(other_rects, page_rect); - } - } - break; - - case SYNCTEX_PROP_H: - rectangle.x1 = scan_float(scanner); - got_rect = true; - break; - - case SYNCTEX_PROP_V: - rectangle.y2 = scan_float(scanner); - got_rect = true; - break; - - case SYNCTEX_PROP_WIDTH: - rectangle.x2 = rectangle.x1 + scan_float(scanner); - got_rect = true; - break; - - case SYNCTEX_PROP_HEIGHT: - rectangle.y1 = rectangle.y2 - scan_float(scanner); - got_rect = true; - break; - } - break; - - default: - break; - } - } - - if (got_rect == true) { - if (current_page == rpage) { - zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t)); - if (real_rect != NULL) { - *real_rect = rectangle; - girara_list_append(hitlist, real_rect); + while ((node = synctex_next_result (scanner)) != NULL) { + const unsigned int current_page = synctex_node_page(node) - 1; + if (got_page == false) { + got_page = true; + *page = current_page; } - } else { - synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t)); - if (page_rect != NULL) { + + zathura_rectangle_t rect = { 0, 0, 0, 0 }; + rect.x1 = synctex_node_box_visible_h(node); + rect.y1 = synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node); + rect.x2 = rect.x1 + synctex_node_box_visible_width(node); + rect.y2 = synctex_node_box_visible_depth(node) + synctex_node_box_visible_height (node) + rect.y1; + + if (*page == current_page) { + zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t)); + if (real_rect == NULL) { + continue; + } + + *real_rect = rect; + girara_list_append(hitlist, real_rect); + } else { + synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t)); + if (page_rect == NULL) { + continue; + } + page_rect->page = current_page; - page_rect->rect = rectangle; + page_rect->rect = rect; + girara_list_append(other_rects, page_rect); } } } - g_scanner_destroy(scanner); - close(output); + synctex_scanner_free(scanner); - if (ret == false) { - girara_debug("Got no usable output from running synctex view."); - girara_list_free(hitlist); - girara_list_free(other_rects); - return NULL; - } - - if (page != NULL) { - *page = rpage; - } if (secondary_rects != NULL) { *secondary_rects = other_rects; } else { diff --git a/synctex.h b/synctex.h index a81ddfb..427994c 100644 --- a/synctex.h +++ b/synctex.h @@ -12,6 +12,7 @@ typedef struct synctex_page_rect_s { void synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y); girara_list_t* synctex_rectangles_from_position(const char* filename, - const char* position, unsigned int* page, girara_list_t** secondary_rects); + const char* input_file, int line, int column, unsigned int* page, + girara_list_t** secondary_rects); #endif diff --git a/synctex/LICENSE b/synctex/LICENSE new file mode 100644 index 0000000..c739c44 --- /dev/null +++ b/synctex/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2008, 2009, 2010, 2011 jerome DOT laurens AT u-bourgogne DOT fr + +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. diff --git a/synctex/Makefile b/synctex/Makefile new file mode 100644 index 0000000..a26a879 --- /dev/null +++ b/synctex/Makefile @@ -0,0 +1,7 @@ +FILES=synctex_parser.c synctex_parser.h synctex_parser_utils.c synctex_parser_utils.h synctex_parser_version.txt + +.PHONY: update +update: + set -e; for FILE in $(FILES) ; do \ + wget -O "$$FILE" "http://www.tug.org/svn/texlive/trunk/Build/source/texk/web2c/synctexdir/$$FILE?view=co" ; \ + done diff --git a/synctex/synctex_parser.c b/synctex/synctex_parser.c new file mode 100644 index 0000000..f4d9633 --- /dev/null +++ b/synctex/synctex_parser.c @@ -0,0 +1,4429 @@ +/* +Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr + +This file is part of the SyncTeX package. + +Latest Revision: Tue Jun 14 08:23:30 UTC 2011 + +Version: 1.17 + +See synctex_parser_readme.txt for more details + +License: +-------- +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE + +Except as contained in this notice, the name of the copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in this Software without prior written +authorization from the copyright holder. + +Acknowledgments: +---------------- +The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh, +and significant help from XeTeX developer Jonathan Kew + +Nota Bene: +---------- +If you include or use a significant part of the synctex package into a software, +I would appreciate to be listed as contributor and see "SyncTeX" highlighted. + +Version 1 +Thu Jun 19 09:39:21 UTC 2008 + +*/ + +/* We assume that high level application like pdf viewers will want + * to embed this code as is. We assume that they also have locale.h and setlocale. + * For other tools such as TeXLive tools, you must define SYNCTEX_USE_LOCAL_HEADER, + * when building. You also have to create and customize synctex_parser_local.h to fit your system. + * In particular, the HAVE_LOCALE_H and HAVE_SETLOCALE macros should be properly defined. + * With this design, you should not need to edit this file. */ + +# if defined(SYNCTEX_USE_LOCAL_HEADER) +# include "synctex_parser_local.h" +# else +# define HAVE_LOCALE_H 1 +# define HAVE_SETLOCALE 1 +# if defined(_MSC_VER) +# define SYNCTEX_INLINE __inline +# else +# define SYNCTEX_INLINE inline +# endif +# endif + +#include +#include +#include +#include +#include + +#if defined(HAVE_LOCALE_H) +#include +#endif + +/* The data is organized in a graph with multiple entries. + * The root object is a scanner, it is created with the contents on a synctex file. + * Each leaf of the tree is a synctex_node_t object. + * There are 3 subtrees, two of them sharing the same leaves. + * The first tree is the list of input records, where input file names are associated with tags. + * The second tree is the box tree as given by TeX when shipping pages out. + * First level objects are sheets, containing boxes, glues, kerns... + * The third tree allows to browse leaves according to tag and line. + */ + +#include "synctex_parser.h" +#include "synctex_parser_utils.h" + +/* These are the possible extensions of the synctex file */ +const char * synctex_suffix = ".synctex"; +const char * synctex_suffix_gz = ".gz"; + +/* each synctex node has a class */ +typedef struct __synctex_class_t _synctex_class_t; +typedef _synctex_class_t * synctex_class_t; + + +/* synctex_node_t is a pointer to a node + * _synctex_node is the target of the synctex_node_t pointer + * It is a pseudo object oriented program. + * class is a pointer to the class object the node belongs to. + * implementation is meant to contain the private data of the node + * basically, there are 2 kinds of information: navigation information and + * synctex information. Both will depend on the type of the node, + * thus different nodes will have different private data. + * There is no inheritancy overhead. + */ +typedef union _synctex_info_t { + int INT; + char * PTR; +} synctex_info_t; + +# if defined(SYNCTEX_USE_CHARINDEX) +# define SYNCTEX_DECLARE_CHARINDEX synctex_charindex_t char_index; +# define SYNCTEX_CHARINDEX(NODE) (NODE->char_index) +# define SYNCTEX_PRINT_CHARINDEX printf("#%i\n",SYNCTEX_CHARINDEX(node)) +# define SYNCTEX_DECLARE_CHAR_OFFSET synctex_charindex_t charindex_offset +# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION) NODE->char_index = (synctex_charindex_t)(scanner->charindex_offset+SYNCTEX_CUR-SYNCTEX_START+(CORRECTION)); +# else +# define SYNCTEX_DECLARE_CHARINDEX +# define SYNCTEX_CHARINDEX(NODE) 0 +# define SYNCTEX_PRINT_CHARINDEX printf("\n") +# define SYNCTEX_DECLARE_CHAR_OFFSET synctex_charindex_t charindex_offset +# define SYNCTEX_IMPLEMENT_CHARINDEX(NODE,CORRECTION) +# endif + +struct _synctex_node { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t * implementation; +}; + +/* Each node of the tree, except the scanner itself belongs to a class. + * The class object is just a struct declaring the owning scanner + * This is a pointer to the scanner as root of the tree. + * The type is used to identify the kind of node. + * The class declares pointers to a creator and a destructor method. + * The log and display fields are used to log and display the node. + * display will also display the child, sibling and parent sibling. + * parent, child and sibling are used to navigate the tree, + * from TeX box hierarchy point of view. + * The friend field points to a method which allows to navigate from friend to friend. + * A friend is a node with very close tag and line numbers. + * Finally, the info field point to a method giving the private node info offset. + */ + +typedef synctex_node_t *(*_synctex_node_getter_t)(synctex_node_t); +typedef synctex_info_t *(*_synctex_info_getter_t)(synctex_node_t); + +struct __synctex_class_t { + synctex_scanner_t scanner; + int type; + synctex_node_t (*new)(synctex_scanner_t scanner); + void (*free)(synctex_node_t); + void (*log)(synctex_node_t); + void (*display)(synctex_node_t); + _synctex_node_getter_t parent; + _synctex_node_getter_t child; + _synctex_node_getter_t sibling; + _synctex_node_getter_t friend; + _synctex_node_getter_t next_hbox; + _synctex_info_getter_t info; +}; + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Abstract OBJECTS and METHODS +# endif + +/* These macros are shortcuts + * This macro checks if a message can be sent. + */ +# define SYNCTEX_CAN_PERFORM(NODE,SELECTOR)\ + (NULL!=((((NODE)->class))->SELECTOR)) + +/* This macro is some kind of objc_msg_send. + * It takes care of sending the proper message if possible. + */ +# define SYNCTEX_MSG_SEND(NODE,SELECTOR) if (NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR)) {\ + (*((((NODE)->class))->SELECTOR))(NODE);\ + } + +/* read only safe getter + */ +# define SYNCTEX_GET(NODE,SELECTOR)((NODE && SYNCTEX_CAN_PERFORM(NODE,SELECTOR))?SYNCTEX_GETTER(NODE,SELECTOR)[0]:(NULL)) + +/* read/write getter + */ +# define SYNCTEX_GETTER(NODE,SELECTOR)\ + ((synctex_node_t *)((*((((NODE)->class))->SELECTOR))(NODE))) + +# define SYNCTEX_FREE(NODE) SYNCTEX_MSG_SEND(NODE,free); + +/* Parent getter and setter + */ +# define SYNCTEX_PARENT(NODE) SYNCTEX_GET(NODE,parent) +# define SYNCTEX_SET_PARENT(NODE,NEW_PARENT) if (NODE && NEW_PARENT && SYNCTEX_CAN_PERFORM(NODE,parent)){\ + SYNCTEX_GETTER(NODE,parent)[0]=NEW_PARENT;\ + } + +/* Child getter and setter + */ +# define SYNCTEX_CHILD(NODE) SYNCTEX_GET(NODE,child) +# define SYNCTEX_SET_CHILD(NODE,NEW_CHILD) if (NODE && NEW_CHILD){\ + SYNCTEX_GETTER(NODE,child)[0]=NEW_CHILD;\ + SYNCTEX_GETTER(NEW_CHILD,parent)[0]=NODE;\ + } + +/* Sibling getter and setter + */ +# define SYNCTEX_SIBLING(NODE) SYNCTEX_GET(NODE,sibling) +# define SYNCTEX_SET_SIBLING(NODE,NEW_SIBLING) if (NODE && NEW_SIBLING) {\ + SYNCTEX_GETTER(NODE,sibling)[0]=NEW_SIBLING;\ + if (SYNCTEX_CAN_PERFORM(NEW_SIBLING,parent) && SYNCTEX_CAN_PERFORM(NODE,parent)) {\ + SYNCTEX_GETTER(NEW_SIBLING,parent)[0]=SYNCTEX_GETTER(NODE,parent)[0];\ + }\ + } +/* Friend getter and setter. A friend is a kern, math, glue or void box node which tag and line numbers are similar. + * This is a first filter on the nodes that avoids testing all of them. + * Friends are used mainly in forward synchronization aka from source to output. + */ +# define SYNCTEX_FRIEND(NODE) SYNCTEX_GET(NODE,friend) +# define SYNCTEX_SET_FRIEND(NODE,NEW_FRIEND) if (NODE && NEW_FRIEND){\ + SYNCTEX_GETTER(NODE,friend)[0]=NEW_FRIEND;\ + } + +/* Next box getter and setter. The box tree can be traversed from one horizontal box to the other. + * Navigation starts with the deeper boxes. + */ +# define SYNCTEX_NEXT_hbox(NODE) SYNCTEX_GET(NODE,next_hbox) +# define SYNCTEX_SET_NEXT_hbox(NODE,NEXT_HBOX) if (NODE && NEXT_HBOX){\ + SYNCTEX_GETTER(NODE,next_hbox)[0]=NEXT_HBOX;\ + } + +void _synctex_free_node(synctex_node_t node); +void _synctex_free_leaf(synctex_node_t node); + +/* A node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for all nodes with children. + */ +void _synctex_free_node(synctex_node_t node) { + if (node) { + (*((node->class)->sibling))(node); + SYNCTEX_FREE(SYNCTEX_SIBLING(node)); + SYNCTEX_FREE(SYNCTEX_CHILD(node)); + free(node); + } + return; +} + +/* A node is meant to own its child and sibling. + * It is not owned by its parent, unless it is its first child. + * This destructor is for nodes with no child. + * The first sheet is onwned by the scanner. + */ +void _synctex_free_leaf(synctex_node_t node) { + if (node) { + SYNCTEX_FREE(SYNCTEX_SIBLING(node)); + free(node); + } + return; +} +# ifdef __SYNCTEX_WORK__ +# include "/usr/include/zlib.h" +# else +# include +# endif + +/* The synctex scanner is the root object. + * Is is initialized with the contents of a text file or a gzipped file. + * The buffer_? are first used to parse the text. + */ +struct __synctex_scanner_t { + gzFile file; /* The (possibly compressed) file */ + SYNCTEX_DECLARE_CHAR_OFFSET; + char * buffer_cur; /* current location in the buffer */ + char * buffer_start; /* start of the buffer */ + char * buffer_end; /* end of the buffer */ + char * output_fmt; /* dvi or pdf, not yet used */ + char * output; /* the output name used to create the scanner */ + char * synctex; /* the .synctex or .synctex.gz name used to create the scanner */ + int version; /* 1, not yet used */ + struct { + unsigned has_parsed:1; /* Whether the scanner has parsed its underlying synctex file. */ + unsigned reserved:sizeof(unsigned)-1; /* alignment */ + } flags; + int pre_magnification; /* magnification from the synctex preamble */ + int pre_unit; /* unit from the synctex preamble */ + int pre_x_offset; /* X offste from the synctex preamble */ + int pre_y_offset; /* Y offset from the synctex preamble */ + int count; /* Number of records, from the synctex postamble */ + float unit; /* real unit, from synctex preamble or post scriptum */ + float x_offset; /* X offset, from synctex preamble or post scriptum */ + float y_offset; /* Y Offset, from synctex preamble or post scriptum */ + synctex_node_t sheet; /* The first sheet node, its siblings are the other sheet nodes */ + synctex_node_t input; /* The first input node, its siblings are the other input nodes */ + int number_of_lists; /* The number of friend lists */ + synctex_node_t * lists_of_friends;/* The friend lists */ + _synctex_class_t class[synctex_node_number_of_types]; /* The classes of the nodes of the scanner */ +}; + +/* SYNCTEX_CUR, SYNCTEX_START and SYNCTEX_END are convenient shortcuts + */ +# define SYNCTEX_CUR (scanner->buffer_cur) +# define SYNCTEX_START (scanner->buffer_start) +# define SYNCTEX_END (scanner->buffer_end) + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark OBJECTS, their creators and destructors. +# endif + +/* Here, we define the indices for the different informations. + * They are used to declare the size of the implementation. + * For example, if one object uses SYNCTEX_HORIZ_IDX is its size, + * then its info will contain a tag, line, column, horiz but no width nor height nor depth + */ + +/* The sheet is a first level node. + * It has no parent (the parent is the scanner itself) + * Its sibling points to another sheet. + * Its child points to its first child, in general a box. + * A sheet node contains only one synctex information: the page. + * This is the 1 based page index as given by TeX. + */ +/* The next macros are used to access the node info + * SYNCTEX_INFO(node) points to the first synctex integer or pointer data of node + * SYNCTEX_INFO(node)[index] is the information at index + * for example, the page of a sheet is stored in SYNCTEX_INFO(sheet)[SYNCTEX_PAGE_IDX] + */ +# define SYNCTEX_INFO(NODE) ((*((((NODE)->class))->info))(NODE)) +# define SYNCTEX_PAGE_IDX 0 +# define SYNCTEX_PAGE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_PAGE_IDX].INT + +/* This macro defines implementation offsets + * It is only used for pointer values + */ +# define SYNCTEX_MAKE_GET(SYNCTEX_GETTER,OFFSET)\ +synctex_node_t * SYNCTEX_GETTER (synctex_node_t node);\ +synctex_node_t * SYNCTEX_GETTER (synctex_node_t node) {\ + return node?(synctex_node_t *)((&((node)->implementation))+OFFSET):NULL;\ +} +SYNCTEX_MAKE_GET(_synctex_implementation_0,0) +SYNCTEX_MAKE_GET(_synctex_implementation_1,1) +SYNCTEX_MAKE_GET(_synctex_implementation_2,2) +SYNCTEX_MAKE_GET(_synctex_implementation_3,3) +SYNCTEX_MAKE_GET(_synctex_implementation_4,4) +SYNCTEX_MAKE_GET(_synctex_implementation_5,5) + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[3+SYNCTEX_PAGE_IDX+1];/* child, sibling, next box, + * SYNCTEX_PAGE_IDX */ +} synctex_node_sheet_t; + +synctex_node_t _synctex_new_sheet(synctex_scanner_t scanner); +void _synctex_display_sheet(synctex_node_t node); +void _synctex_log_sheet(synctex_node_t node); + +static _synctex_class_t synctex_class_sheet = { + NULL, /* No scanner yet */ + synctex_node_type_sheet, /* Node type */ + &_synctex_new_sheet, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_sheet, /* log */ + &_synctex_display_sheet, /* display */ + NULL, /* No parent */ + &_synctex_implementation_0, /* child */ + &_synctex_implementation_1, /* sibling */ + NULL, /* No friend */ + &_synctex_implementation_2, /* Next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 /* info */ +}; + +/* sheet node creator */ + +#define DEFINE_synctex_new_NODE(NAME)\ +synctex_node_t _synctex_new_##NAME(synctex_scanner_t scanner) {\ + if (scanner) {\ + synctex_node_t node = _synctex_malloc(sizeof(synctex_node_##NAME##_t));\ + if (node) {\ + SYNCTEX_IMPLEMENT_CHARINDEX(node,0);\ + ++SYNCTEX_CUR;\ + node->class = scanner->class+synctex_node_type_##NAME;\ + }\ + return node;\ + }\ + return NULL;\ +} +DEFINE_synctex_new_NODE(sheet) + +/* A box node contains navigation and synctex information + * There are different kind of boxes. + * Only horizontal boxes are treated differently because of their visible size. + */ +# define SYNCTEX_TAG_IDX 0 +# define SYNCTEX_LINE_IDX (SYNCTEX_TAG_IDX+1) +# define SYNCTEX_COLUMN_IDX (SYNCTEX_LINE_IDX+1) +# define SYNCTEX_HORIZ_IDX (SYNCTEX_COLUMN_IDX+1) +# define SYNCTEX_VERT_IDX (SYNCTEX_HORIZ_IDX+1) +# define SYNCTEX_WIDTH_IDX (SYNCTEX_VERT_IDX+1) +# define SYNCTEX_HEIGHT_IDX (SYNCTEX_WIDTH_IDX+1) +# define SYNCTEX_DEPTH_IDX (SYNCTEX_HEIGHT_IDX+1) +/* the corresponding info accessors */ +# define SYNCTEX_TAG(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_TAG_IDX].INT +# define SYNCTEX_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_LINE_IDX].INT +# define SYNCTEX_COLUMN(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_COLUMN_IDX].INT +# define SYNCTEX_HORIZ(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_IDX].INT +# define SYNCTEX_VERT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_IDX].INT +# define SYNCTEX_WIDTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_IDX].INT +# define SYNCTEX_HEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_IDX].INT +# define SYNCTEX_DEPTH(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_IDX].INT +# define SYNCTEX_ABS_WIDTH(NODE) ((SYNCTEX_WIDTH(NODE)>0?SYNCTEX_WIDTH(NODE):-SYNCTEX_WIDTH(NODE))) +# define SYNCTEX_ABS_HEIGHT(NODE) ((SYNCTEX_HEIGHT(NODE)>0?SYNCTEX_HEIGHT(NODE):-SYNCTEX_HEIGHT(NODE))) +# define SYNCTEX_ABS_DEPTH(NODE) ((SYNCTEX_DEPTH(NODE)>0?SYNCTEX_DEPTH(NODE):-SYNCTEX_DEPTH(NODE))) + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[5+SYNCTEX_DEPTH_IDX+1]; /* parent,child,sibling,friend,next box, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH */ +} synctex_node_vbox_t; + +synctex_node_t _synctex_new_vbox(synctex_scanner_t scanner); +void _synctex_log_vbox(synctex_node_t node); +void _synctex_display_vbox(synctex_node_t node); + +/* These are static class objects, each scanner will make a copy of them and setup the scanner field. + */ +static _synctex_class_t synctex_class_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_vbox, /* Node type */ + &_synctex_new_vbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_vbox, /* log */ + &_synctex_display_vbox, /* display */ + &_synctex_implementation_0, /* parent */ + &_synctex_implementation_1, /* child */ + &_synctex_implementation_2, /* sibling */ + &_synctex_implementation_3, /* friend */ + &_synctex_implementation_4, /* next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_5 +}; + +/* vertical box node creator */ +DEFINE_synctex_new_NODE(vbox) + +/* Horizontal boxes must contain visible size, because 0 width does not mean emptiness. + * They also contain an average of the line numbers of the containing nodes. */ +# define SYNCTEX_MEAN_LINE_IDX (SYNCTEX_DEPTH_IDX+1) +# define SYNCTEX_NODE_WEIGHT_IDX (SYNCTEX_MEAN_LINE_IDX+1) +# define SYNCTEX_HORIZ_V_IDX (SYNCTEX_NODE_WEIGHT_IDX+1) +# define SYNCTEX_VERT_V_IDX (SYNCTEX_HORIZ_V_IDX+1) +# define SYNCTEX_WIDTH_V_IDX (SYNCTEX_VERT_V_IDX+1) +# define SYNCTEX_HEIGHT_V_IDX (SYNCTEX_WIDTH_V_IDX+1) +# define SYNCTEX_DEPTH_V_IDX (SYNCTEX_HEIGHT_V_IDX+1) +/* the corresponding info accessors */ +# define SYNCTEX_MEAN_LINE(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_MEAN_LINE_IDX].INT +# define SYNCTEX_NODE_WEIGHT(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NODE_WEIGHT_IDX].INT +# define SYNCTEX_HORIZ_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HORIZ_V_IDX].INT +# define SYNCTEX_VERT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_VERT_V_IDX].INT +# define SYNCTEX_WIDTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_WIDTH_V_IDX].INT +# define SYNCTEX_HEIGHT_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_HEIGHT_V_IDX].INT +# define SYNCTEX_DEPTH_V(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_DEPTH_V_IDX].INT +# define SYNCTEX_ABS_WIDTH_V(NODE) ((SYNCTEX_WIDTH_V(NODE)>0?SYNCTEX_WIDTH_V(NODE):-SYNCTEX_WIDTH_V(NODE))) +# define SYNCTEX_ABS_HEIGHT_V(NODE) ((SYNCTEX_HEIGHT_V(NODE)>0?SYNCTEX_HEIGHT_V(NODE):-SYNCTEX_HEIGHT_V(NODE))) +# define SYNCTEX_ABS_DEPTH_V(NODE) ((SYNCTEX_DEPTH_V(NODE)>0?SYNCTEX_DEPTH_V(NODE):-SYNCTEX_DEPTH_V(NODE))) + +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[5+SYNCTEX_DEPTH_V_IDX+1]; /*parent,child,sibling,friend,next box, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH, + * SYNCTEX_MEAN_LINE,SYNCTEX_NODE_WEIGHT, + * SYNCTEX_HORIZ_V,SYNCTEX_VERT_V,SYNCTEX_WIDTH_V,SYNCTEX_HEIGHT_V,SYNCTEX_DEPTH_V*/ +} synctex_node_hbox_t; + +synctex_node_t _synctex_new_hbox(synctex_scanner_t scanner); +void _synctex_display_hbox(synctex_node_t node); +void _synctex_log_hbox(synctex_node_t node); + + +static _synctex_class_t synctex_class_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_hbox, /* Node type */ + &_synctex_new_hbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_hbox, /* log */ + &_synctex_display_hbox, /* display */ + &_synctex_implementation_0, /* parent */ + &_synctex_implementation_1, /* child */ + &_synctex_implementation_2, /* sibling */ + &_synctex_implementation_3, /* friend */ + &_synctex_implementation_4, /* next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_5 +}; + +/* horizontal box node creator */ +DEFINE_synctex_new_NODE(hbox) + +/* This void box node implementation is either horizontal or vertical + * It does not contain a child field. + */ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[3+SYNCTEX_DEPTH_IDX+1]; /* parent,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH,SYNCTEX_HEIGHT,SYNCTEX_DEPTH*/ +} synctex_node_void_vbox_t; + +synctex_node_t _synctex_new_void_vbox(synctex_scanner_t scanner); +void _synctex_log_void_box(synctex_node_t node); +void _synctex_display_void_vbox(synctex_node_t node); + +static _synctex_class_t synctex_class_void_vbox = { + NULL, /* No scanner yet */ + synctex_node_type_void_vbox,/* Node type */ + &_synctex_new_void_vbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_void_box, /* log */ + &_synctex_display_void_vbox,/* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +/* vertical void box node creator */ +DEFINE_synctex_new_NODE(void_vbox) + +typedef synctex_node_void_vbox_t synctex_node_void_hbox_t; + +synctex_node_t _synctex_new_void_hbox(synctex_scanner_t scanner); +void _synctex_display_void_hbox(synctex_node_t node); + +static _synctex_class_t synctex_class_void_hbox = { + NULL, /* No scanner yet */ + synctex_node_type_void_hbox,/* Node type */ + &_synctex_new_void_hbox, /* creator */ + &_synctex_free_node, /* destructor */ + &_synctex_log_void_box, /* log */ + &_synctex_display_void_hbox,/* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +/* horizontal void box node creator */ +DEFINE_synctex_new_NODE(void_hbox) + +/* The medium nodes correspond to kern, glue, penalty and math nodes. + * In LuaTeX, the size of the nodes may have changed. */ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[3+SYNCTEX_WIDTH_IDX+1]; /* parent,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT,SYNCTEX_WIDTH */ +} synctex_node_medium_t; + +#define SYNCTEX_IS_BOX(NODE)\ + ((NODE->class->type == synctex_node_type_vbox)\ + || (NODE->class->type == synctex_node_type_void_vbox)\ + || (NODE->class->type == synctex_node_type_hbox)\ + || (NODE->class->type == synctex_node_type_void_hbox)) + +#define SYNCTEX_HAS_CHILDREN(NODE) (NODE && SYNCTEX_CHILD(NODE)) + +void _synctex_log_medium_node(synctex_node_t node); + +typedef synctex_node_medium_t synctex_node_math_t; + +/* math node creator */ +synctex_node_t _synctex_new_math(synctex_scanner_t scanner); +void _synctex_display_math(synctex_node_t node); + +static _synctex_class_t synctex_class_math = { + NULL, /* No scanner yet */ + synctex_node_type_math, /* Node type */ + &_synctex_new_math, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_medium_node, /* log */ + &_synctex_display_math, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +DEFINE_synctex_new_NODE(math) + +typedef synctex_node_medium_t synctex_node_kern_t; + +/* kern node creator */ +synctex_node_t _synctex_new_kern(synctex_scanner_t scanner); +void _synctex_display_kern(synctex_node_t node); + +static _synctex_class_t synctex_class_kern = { + NULL, /* No scanner yet */ + synctex_node_type_kern, /* Node type */ + &_synctex_new_kern, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_medium_node, /* log */ + &_synctex_display_kern, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +DEFINE_synctex_new_NODE(kern) + +/* The small nodes correspond to glue and boundary nodes. */ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[3+SYNCTEX_VERT_IDX+1]; /* parent,sibling,friend, + * SYNCTEX_TAG,SYNCTEX_LINE,SYNCTEX_COLUMN, + * SYNCTEX_HORIZ,SYNCTEX_VERT */ +} synctex_node_small_t; + +void _synctex_log_small_node(synctex_node_t node); + +/* glue node creator */ +typedef synctex_node_small_t synctex_node_glue_t; +synctex_node_t _synctex_new_glue(synctex_scanner_t scanner); +void _synctex_display_glue(synctex_node_t node); + +static _synctex_class_t synctex_class_glue = { + NULL, /* No scanner yet */ + synctex_node_type_glue, /* Node type */ + &_synctex_new_glue, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_medium_node, /* log */ + &_synctex_display_glue, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; +DEFINE_synctex_new_NODE(glue) + +/* boundary node creator */ +typedef synctex_node_small_t synctex_node_boundary_t; +synctex_node_t _synctex_new_boundary(synctex_scanner_t scanner); +void _synctex_display_boundary(synctex_node_t node); + +static _synctex_class_t synctex_class_boundary = { + NULL, /* No scanner yet */ + synctex_node_type_boundary, /* Node type */ + &_synctex_new_boundary, /* creator */ + &_synctex_free_leaf, /* destructor */ + &_synctex_log_small_node, /* log */ + &_synctex_display_boundary, /* display */ + &_synctex_implementation_0, /* parent */ + NULL, /* No child */ + &_synctex_implementation_1, /* sibling */ + &_synctex_implementation_2, /* friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_3 +}; + +DEFINE_synctex_new_NODE(boundary) + +# define SYNCTEX_NAME_IDX (SYNCTEX_TAG_IDX+1) +# define SYNCTEX_NAME(NODE) SYNCTEX_INFO(NODE)[SYNCTEX_NAME_IDX].PTR + +/* Input nodes only know about their sibling, which is another input node. + * The synctex information is the SYNCTEX_TAG and SYNCTEX_NAME*/ +typedef struct { + SYNCTEX_DECLARE_CHARINDEX + synctex_class_t class; + synctex_info_t implementation[1+SYNCTEX_NAME_IDX+1]; /* sibling, + * SYNCTEX_TAG,SYNCTEX_NAME */ +} synctex_input_t; + +synctex_node_t _synctex_new_input(synctex_scanner_t scanner); +void _synctex_free_input(synctex_node_t node); +void _synctex_display_input(synctex_node_t node); +void _synctex_log_input(synctex_node_t node); + +static _synctex_class_t synctex_class_input = { + NULL, /* No scanner yet */ + synctex_node_type_input, /* Node type */ + &_synctex_new_input, /* creator */ + &_synctex_free_input, /* destructor */ + &_synctex_log_input, /* log */ + &_synctex_display_input, /* display */ + NULL, /* No parent */ + NULL, /* No child */ + &_synctex_implementation_0, /* sibling */ + NULL, /* No friend */ + NULL, /* No next hbox */ + (_synctex_info_getter_t)&_synctex_implementation_1 +}; + +# define SYNCTEX_INPUT_MARK "Input:" + +synctex_node_t _synctex_new_input(synctex_scanner_t scanner) { + if (scanner) { + synctex_node_t node = _synctex_malloc(sizeof(synctex_input_t)); + if (node) { + SYNCTEX_IMPLEMENT_CHARINDEX(node,strlen(SYNCTEX_INPUT_MARK)); + node->class = scanner->class+synctex_node_type_input; + } + return node; + } + return NULL; +} +void _synctex_free_input(synctex_node_t node){ + if (node) { + SYNCTEX_FREE(SYNCTEX_SIBLING(node)); + free(SYNCTEX_NAME(node)); + free(node); + } +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Navigation +# endif +synctex_node_t synctex_node_parent(synctex_node_t node) +{ + return SYNCTEX_PARENT(node); +} +synctex_node_t synctex_node_sheet(synctex_node_t node) +{ + while(node && node->class->type != synctex_node_type_sheet) { + node = SYNCTEX_PARENT(node); + } + /* exit the while loop either when node is NULL or node is a sheet */ + return node; +} +synctex_node_t synctex_node_child(synctex_node_t node) +{ + return SYNCTEX_CHILD(node); +} +synctex_node_t synctex_node_sibling(synctex_node_t node) +{ + return SYNCTEX_SIBLING(node); +} +synctex_node_t synctex_node_next(synctex_node_t node) { + if (SYNCTEX_CHILD(node)) { + return SYNCTEX_CHILD(node); + } +sibling: + if (SYNCTEX_SIBLING(node)) { + return SYNCTEX_SIBLING(node); + } + if ((node = SYNCTEX_PARENT(node))) { + if (node->class->type == synctex_node_type_sheet) {/* EXC_BAD_ACCESS? */ + return NULL; + } + goto sibling; + } + return NULL; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark CLASS +# endif + +/* Public node accessor: the type */ +synctex_node_type_t synctex_node_type(synctex_node_t node) { + if (node) { + return (((node)->class))->type; + } + return synctex_node_type_error; +} + +/* Public node accessor: the human readable type */ +const char * synctex_node_isa(synctex_node_t node) { +static const char * isa[synctex_node_number_of_types] = + {"Not a node","input","sheet","vbox","void vbox","hbox","void hbox","kern","glue","math","boundary"}; + return isa[synctex_node_type(node)]; +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SYNCTEX_LOG +# endif + +# define SYNCTEX_LOG(NODE) SYNCTEX_MSG_SEND(NODE,log) + +/* Public node logger */ +void synctex_node_log(synctex_node_t node) { + SYNCTEX_LOG(node); +} + +void _synctex_log_input(synctex_node_t node) { + if (node) { + printf("%s:%i,%s",synctex_node_isa(node),SYNCTEX_TAG(node),SYNCTEX_NAME(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + } +} + +void _synctex_log_sheet(synctex_node_t node) { + if (node) { + printf("%s:%i",synctex_node_isa(node),SYNCTEX_PAGE(node)); + SYNCTEX_PRINT_CHARINDEX; + printf("SELF:%p",(void *)node); + printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node)); + printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node)); + } +} + +void _synctex_log_small_node(synctex_node_t node) { + if (node) { + printf("%s:%i,%i:%i,%i", + synctex_node_isa(node), + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node)); + SYNCTEX_PRINT_CHARINDEX; + printf("SELF:%p",(void *)node); + printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node)); + } +} + +void _synctex_log_medium_node(synctex_node_t node) { + if (node) { + printf("%s:%i,%i:%i,%i:%i", + synctex_node_isa(node), + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node)); + SYNCTEX_PRINT_CHARINDEX; + printf("SELF:%p",(void *)node); + printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node)); + } +} + +void _synctex_log_void_box(synctex_node_t node) { + if (node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%i",SYNCTEX_LINE(node)); + printf(",%i",0); + printf(":%i",SYNCTEX_HORIZ(node)); + printf(",%i",SYNCTEX_VERT(node)); + printf(":%i",SYNCTEX_WIDTH(node)); + printf(",%i",SYNCTEX_HEIGHT(node)); + printf(",%i",SYNCTEX_DEPTH(node)); + SYNCTEX_PRINT_CHARINDEX; + printf("SELF:%p",(void *)node); + printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p\n",(void *)SYNCTEX_FRIEND(node)); + } +} + +void _synctex_log_vbox(synctex_node_t node) { + if (node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%i",SYNCTEX_LINE(node)); + printf(",%i",0); + printf(":%i",SYNCTEX_HORIZ(node)); + printf(",%i",SYNCTEX_VERT(node)); + printf(":%i",SYNCTEX_WIDTH(node)); + printf(",%i",SYNCTEX_HEIGHT(node)); + printf(",%i",SYNCTEX_DEPTH(node)); + SYNCTEX_PRINT_CHARINDEX; + printf("SELF:%p",(void *)node); + printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node)); + printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node)); + } +} + +void _synctex_log_hbox(synctex_node_t node) { + if (node) { + printf("%s",synctex_node_isa(node)); + printf(":%i",SYNCTEX_TAG(node)); + printf(",%i~%i*%i",SYNCTEX_LINE(node),SYNCTEX_MEAN_LINE(node),SYNCTEX_NODE_WEIGHT(node)); + printf(",%i",0); + printf(":%i",SYNCTEX_HORIZ(node)); + printf(",%i",SYNCTEX_VERT(node)); + printf(":%i",SYNCTEX_WIDTH(node)); + printf(",%i",SYNCTEX_HEIGHT(node)); + printf(",%i",SYNCTEX_DEPTH(node)); + printf("/%i",SYNCTEX_HORIZ_V(node)); + printf(",%i",SYNCTEX_VERT_V(node)); + printf(":%i",SYNCTEX_WIDTH_V(node)); + printf(",%i",SYNCTEX_HEIGHT_V(node)); + printf(",%i",SYNCTEX_DEPTH_V(node)); + SYNCTEX_PRINT_CHARINDEX; + printf("SELF:%p",(void *)node); + printf(" SYNCTEX_PARENT:%p",(void *)SYNCTEX_PARENT(node)); + printf(" SYNCTEX_CHILD:%p",(void *)SYNCTEX_CHILD(node)); + printf(" SYNCTEX_SIBLING:%p",(void *)SYNCTEX_SIBLING(node)); + printf(" SYNCTEX_FRIEND:%p",(void *)SYNCTEX_FRIEND(node)); + printf(" SYNCTEX_NEXT_hbox:%p\n",(void *)SYNCTEX_NEXT_hbox(node)); + } +} + +# define SYNCTEX_DISPLAY(NODE) SYNCTEX_MSG_SEND(NODE,display) + +void synctex_node_display(synctex_node_t node) { + SYNCTEX_DISPLAY(node); +} + +void _synctex_display_input(synctex_node_t node) { + if (node) { + printf("....Input:%i:%s", + SYNCTEX_TAG(node), + SYNCTEX_NAME(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_sheet(synctex_node_t node) { + if (node) { + printf("....{%i",SYNCTEX_PAGE(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_CHILD(node)); + printf("....}\n"); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_vbox(synctex_node_t node) { + if (node) { + printf("....[%i,%i:%i,%i:%i,%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_CHILD(node)); + printf("....]\n"); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_hbox(synctex_node_t node) { + if (node) { + printf("....(%i,%i~%i*%i:%i,%i:%i,%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_MEAN_LINE(node), + SYNCTEX_NODE_WEIGHT(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_CHILD(node)); + printf("....)\n"); + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_void_vbox(synctex_node_t node) { + if (node) { + printf("....v%i,%i;%i,%i:%i,%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_void_hbox(synctex_node_t node) { + if (node) { + printf("....h%i,%i:%i,%i:%i,%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node), + SYNCTEX_HEIGHT(node), + SYNCTEX_DEPTH(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_glue(synctex_node_t node) { + if (node) { + printf("....glue:%i,%i:%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_math(synctex_node_t node) { + if (node) { + printf("....math:%i,%i:%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_kern(synctex_node_t node) { + if (node) { + printf("....kern:%i,%i:%i,%i:%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node), + SYNCTEX_WIDTH(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +void _synctex_display_boundary(synctex_node_t node) { + if (node) { + printf("....boundary:%i,%i:%i,%i", + SYNCTEX_TAG(node), + SYNCTEX_LINE(node), + SYNCTEX_HORIZ(node), + SYNCTEX_VERT(node)); + SYNCTEX_PRINT_CHARINDEX; + SYNCTEX_DISPLAY(SYNCTEX_SIBLING(node)); + } +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark SCANNER +# endif + +/* Here are gathered all the possible status that the next scanning functions will return. + * All these functions return a status, and pass their result through pointers. + * Negative values correspond to errors. + * The management of the buffer is causing some significant overhead. + * Every function that may access the buffer returns a status related to the buffer and file state. + * status >= SYNCTEX_STATUS_OK means the function worked as expected + * status < SYNCTEX_STATUS_OK means the function did not work as expected + * status == SYNCTEX_STATUS_NOT_OK means the function did not work as expected but there is still some material to parse. + * status == SYNCTEX_STATUS_EOF means the function did not work as expected and there is no more material. + * statusfile) + +/* Actually, the minimum buffer size is driven by integer and float parsing. + * ±0.123456789e123 + */ +# define SYNCTEX_BUFFER_MIN_SIZE 16 +# define SYNCTEX_BUFFER_SIZE 32768 + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Prototypes +# endif +synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr); +synctex_status_t _synctex_next_line(synctex_scanner_t scanner); +synctex_status_t _synctex_match_string(synctex_scanner_t scanner, const char * the_string); +synctex_status_t _synctex_decode_int(synctex_scanner_t scanner, int* value_ref); +synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref); +synctex_status_t _synctex_scan_input(synctex_scanner_t scanner); +synctex_status_t _synctex_scan_preamble(synctex_scanner_t scanner); +synctex_status_t _synctex_scan_float_and_dimension(synctex_scanner_t scanner, float * value_ref); +synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner); +int _synctex_scan_postamble(synctex_scanner_t scanner); +synctex_status_t _synctex_setup_visible_box(synctex_node_t box); +synctex_status_t _synctex_hbox_setup_visible(synctex_node_t node,int h, int v); +synctex_status_t _synctex_scan_sheet(synctex_scanner_t scanner, synctex_node_t parent); +synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner); +synctex_status_t _synctex_scan_content(synctex_scanner_t scanner); +int synctex_scanner_pre_x_offset(synctex_scanner_t scanner); +int synctex_scanner_pre_y_offset(synctex_scanner_t scanner); +const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner); +int _synctex_node_is_box(synctex_node_t node); +int _synctex_bail(void); + +/* Try to ensure that the buffer contains at least size bytes. + * Passing a huge size argument means the whole buffer length. + * Passing a null size argument means return the available buffer length, without reading the file. + * In that case, the return status is always SYNCTEX_STATUS_OK unless the given scanner is NULL, + * in which case, SYNCTEX_STATUS_BAD_ARGUMENT is returned. + * The value returned in size_ptr is the number of bytes now available in the buffer. + * This is a nonnegative integer, it may take the value 0. + * It is the responsibility of the caller to test whether this size is conforming to its needs. + * Negative values may return in case of error, actually + * when there was an error reading the synctex file. */ +synctex_status_t _synctex_buffer_get_available_size(synctex_scanner_t scanner, size_t * size_ptr) { + size_t available = 0; + if (NULL == scanner || NULL == size_ptr) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +# define size (* size_ptr) + if (size>SYNCTEX_BUFFER_SIZE){ + size = SYNCTEX_BUFFER_SIZE; + } + available = SYNCTEX_END - SYNCTEX_CUR; /* available is the number of unparsed chars in the buffer */ + if (size<=available) { + /* There are already sufficiently many characters in the buffer */ + size = available; + return SYNCTEX_STATUS_OK; + } + if (SYNCTEX_FILE) { + /* Copy the remaining part of the buffer to the beginning, + * then read the next part of the file */ + int already_read = 0; +# if defined(SYNCTEX_USE_CHARINDEX) + scanner->charindex_offset += SYNCTEX_CUR - SYNCTEX_START; +# endif + if (available) { + memmove(SYNCTEX_START, SYNCTEX_CUR, available); + } + SYNCTEX_CUR = SYNCTEX_START + available; /* the next character after the move, will change. */ + /* Fill the buffer up to its end */ + already_read = gzread(SYNCTEX_FILE,(void *)SYNCTEX_CUR,SYNCTEX_BUFFER_SIZE - available); + if (already_read>0) { + /* We assume that 0already_read) { + /* There is a possible error in reading the file */ + int errnum = 0; + const char * error_string = gzerror(SYNCTEX_FILE, &errnum); + if (Z_ERRNO == errnum) { + /* There is an error in zlib caused by the file system */ + _synctex_error("gzread error from the file system (%i)",errno); + return SYNCTEX_STATUS_ERROR; + } else if (errnum) { + _synctex_error("gzread error (%i:%i,%s)",already_read,errnum,error_string); + return SYNCTEX_STATUS_ERROR; + } + } + /* Nothing was read, we are at the end of the file. */ + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + SYNCTEX_END = SYNCTEX_CUR; + SYNCTEX_CUR = SYNCTEX_START; + * SYNCTEX_END = '\0';/* Terminate the string properly.*/ + size = SYNCTEX_END - SYNCTEX_CUR; + return SYNCTEX_STATUS_EOF; /* there might be a bit of text left */ + } + /* We cannot enlarge the buffer because the end of the file was reached. */ + size = available; + return SYNCTEX_STATUS_EOF; +# undef size +} + +/* Used when parsing the synctex file. + * Advance to the next character starting a line. + * Actually, only '\n' is recognized as end of line marker. + * On normal completion, the returned value is the number of unparsed characters available in the buffer. + * In general, it is a positive value, 0 meaning that the end of file was reached. + * -1 is returned in case of error, actually because there was an error while feeding the buffer. + * When the function returns with no error, SYNCTEX_CUR points to the first character of the next line, if any. + * J. Laurens: Sat May 10 07:52:31 UTC 2008 + */ +synctex_status_t _synctex_next_line(synctex_scanner_t scanner) { + synctex_status_t status = SYNCTEX_STATUS_OK; + size_t available = 0; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +infinite_loop: + while(SYNCTEX_CUR=remaining_len) { + /* The buffer is sufficiently big to hold the expected number of characters. */ + if (strncmp((char *)SYNCTEX_CUR,the_string,remaining_len)) { + return SYNCTEX_STATUS_NOT_OK; + } +return_OK: + /* Advance SYNCTEX_CUR to the next character after the_string. */ + SYNCTEX_CUR += remaining_len; + return SYNCTEX_STATUS_OK; + } else if (strncmp((char *)SYNCTEX_CUR,the_string,available)) { + /* No need to go further, this is not the expected string in the buffer. */ + return SYNCTEX_STATUS_NOT_OK; + } else if (SYNCTEX_FILE) { + /* The buffer was too small to contain remaining_len characters. + * We have to cut the string into pieces. */ + z_off_t offset = 0L; + /* the first part of the string is found, advance the_string to the next untested character. */ + the_string += available; + /* update the remaining length and the parsed length. */ + remaining_len -= available; + tested_len += available; + SYNCTEX_CUR += available; /* We validate the tested characters. */ + if (0 == remaining_len) { + /* Nothing left to test, we have found the given string, we return the length. */ + return tested_len; + } + /* We also have to record the current state of the file cursor because + * if the_string does not match, all this should be a totally blank operation, + * for which the file and buffer states should not be modified at all. + * In fact, the states of the buffer before and after this function are in general different + * but they are totally equivalent as long as the values of the buffer before SYNCTEX_CUR + * can be safely discarded. */ + offset = gztell(SYNCTEX_FILE); + /* offset now corresponds to the first character of the file that was not buffered. */ + available = SYNCTEX_CUR - SYNCTEX_START; /* available can be used as temporary placeholder. */ + /* available now corresponds to the number of chars that where already buffered and + * that match the head of the_string. If in fine the_string does not match, all these chars must be recovered + * because the buffer contents is completely replaced by _synctex_buffer_get_available_size. + * They were buffered from offset-len location in the file. */ + offset -= available; +more_characters: + /* There is still some work to be done, so read another bunch of file. + * This is the second call to _synctex_buffer_get_available_size, + * which means that the actual contents of the buffer will be discarded. + * We will definitely have to recover the previous state in case we do not find the expected string. */ + available = remaining_len; + status = _synctex_buffer_get_available_size(scanner,&available); + if (statusptr) { + SYNCTEX_CUR = end; + if (value_ref) { + * value_ref = result; + } + return SYNCTEX_STATUS_OK;/* Successfully scanned an int */ + } + return SYNCTEX_STATUS_NOT_OK;/* Could not scan an int */ +} + +/* The purpose of this function is to read a string. + * A string is an array of characters from the current parser location + * and before the next '\n' character. + * If a string was properly decoded, it is returned in value_ref and + * the cursor points to the new line marker. + * The returned string was alloced on the heap, the caller is the owner and + * is responsible to free it in due time. + * If no string is parsed, * value_ref is undefined. + * The maximum length of a string that a scanner can decode is platform dependent, namely UINT_MAX. + * If you just want to blindly parse the file up to the end of the current line, + * use _synctex_next_line instead. + * On return, the scanner cursor is unchanged if a string could not be scanned or + * points to the terminating '\n' character otherwise. As a consequence, + * _synctex_next_line is necessary after. + * If either scanner or value_ref is NULL, it is considered as an error and + * SYNCTEX_STATUS_BAD_ARGUMENT is returned. + */ +synctex_status_t _synctex_decode_string(synctex_scanner_t scanner, char ** value_ref) { + char * end = NULL; + size_t current_size = 0; + size_t new_size = 0; + size_t len = 0;/* The number of bytes to copy */ + size_t available = 0; + synctex_status_t status = 0; + if (NULL == scanner || NULL == value_ref) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* The buffer must at least contain one character: the '\n' end of line marker */ + if (SYNCTEX_CUR>=SYNCTEX_END) { + available = 1; + status = _synctex_buffer_get_available_size(scanner,&available); + if (status < 0) { + return status; + } + if (0 == available) { + return SYNCTEX_STATUS_EOF; + } + } + /* Now we are sure that there is at least one available character, either because + * SYNCTEX_CUR was already < SYNCTEX_END, or because the buffer has been properly filled. */ + /* end will point to the next unparsed '\n' character in the file, when mapped to the buffer. */ + end = SYNCTEX_CUR; + * value_ref = NULL;/* Initialize, it will be realloc'ed */ + /* We scan all the characters up to the next '\n' */ +next_character: + if (endUINT_MAX-len-1) { + /* But we have reached the limit: we do not have current_size+len+1>UINT_MAX. + * We return the missing amount of memory. + * This will never occur in practice. */ + return UINT_MAX-len-1 - current_size; + } + new_size = current_size+len; + /* We have current_size+len+1<=UINT_MAX + * or equivalently new_sizeUINT_MAX-len-1) { + /* We have reached the limit. */ + _synctex_error("limit reached (missing %i).",current_size-(UINT_MAX-len-1)); + return SYNCTEX_STATUS_ERROR; + } + new_size = current_size+len; + if ((* value_ref = realloc(* value_ref,new_size+1)) != NULL) { + if (memcpy((*value_ref)+current_size,SYNCTEX_CUR,len)) { + (* value_ref)[new_size]='\0'; /* Terminate the string */ + SYNCTEX_CUR = SYNCTEX_END;/* Advance the cursor to the end of the bufer */ + return SYNCTEX_STATUS_OK; + } + free(* value_ref); + * value_ref = NULL; + _synctex_error("could not copy memory (2)."); + return SYNCTEX_STATUS_ERROR; + } + /* Huge memory problem */ + _synctex_error("could not allocate memory (2)."); + return SYNCTEX_STATUS_ERROR; + } +} + +/* Used when parsing the synctex file. + * Read an Input record. + */ +synctex_status_t _synctex_scan_input(synctex_scanner_t scanner) { + synctex_status_t status = 0; + size_t available = 0; + synctex_node_t input = NULL; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + status = _synctex_match_string(scanner,SYNCTEX_INPUT_MARK); + if (statusinput); + scanner->input = input; +# if SYNCTEX_VERBOSE + synctex_node_log(input); +# endif + return _synctex_next_line(scanner);/* read the line termination character, if any */ + /* Now, set up the path */ +} + +typedef synctex_status_t (*synctex_decoder_t)(synctex_scanner_t,void *); + +synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder); + +/* Used when parsing the synctex file. + * Read one of the settings. + * On normal completion, returns SYNCTEX_STATUS_OK. + * On error, returns SYNCTEX_STATUS_ERROR. + * Both arguments must not be NULL. + * On return, the scanner points to the next character after the decoded object whatever it is. + * It is the responsibility of the caller to prepare the scanner for the next line. + */ +synctex_status_t _synctex_scan_named(synctex_scanner_t scanner,const char * name,void * value_ref,synctex_decoder_t decoder) { + synctex_status_t status = 0; + if (NULL == scanner || NULL == name || NULL == value_ref || NULL == decoder) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } +not_found: + status = _synctex_match_string(scanner,name); + if (statusversion),(synctex_decoder_t)&_synctex_decode_int); + if (statusoutput_fmt),(synctex_decoder_t)&_synctex_decode_string); + if (statuspre_magnification),(synctex_decoder_t)&_synctex_decode_int); + if (statuspre_unit),(synctex_decoder_t)&_synctex_decode_int); + if (statuspre_x_offset),(synctex_decoder_t)&_synctex_decode_int); + if (statuspre_y_offset),(synctex_decoder_t)&_synctex_decode_int); + if (status= SYNCTEX_STATUS_OK) { + f *= 72.27f*65536; + } else if (status= SYNCTEX_STATUS_OK) { + f *= 72.27f*65536/2.54f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"mm")) >= SYNCTEX_STATUS_OK) { + f *= 72.27f*65536/25.4f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"pt")) >= SYNCTEX_STATUS_OK) { + f *= 65536.0f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"bp")) >= SYNCTEX_STATUS_OK) { + f *= 72.27f/72*65536.0f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"pc")) >= SYNCTEX_STATUS_OK) { + f *= 12.0*65536.0f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"sp")) >= SYNCTEX_STATUS_OK) { + f *= 1.0f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"dd")) >= SYNCTEX_STATUS_OK) { + f *= 1238.0f/1157*65536.0f; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"cc")) >= SYNCTEX_STATUS_OK) { + f *= 14856.0f/1157*65536; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"nd")) >= SYNCTEX_STATUS_OK) { + f *= 685.0f/642*65536; + } else if (status<0) { + goto report_unit_error; + } else if ((status = _synctex_match_string(scanner,"nc")) >= SYNCTEX_STATUS_OK) { + f *= 1370.0f/107*65536; + } else if (status<0) { + goto report_unit_error; + } + *value_ref = f; + return SYNCTEX_STATUS_OK; +} + +/* parse the post scriptum + * SYNCTEX_STATUS_OK is returned on completion + * a negative error is returned otherwise */ +synctex_status_t _synctex_scan_post_scriptum(synctex_scanner_t scanner) { + synctex_status_t status = 0; + char * endptr = NULL; +#ifdef HAVE_SETLOCALE + char * loc = setlocale(LC_NUMERIC, NULL); +#endif + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* Scan the file until a post scriptum line is found */ +post_scriptum_not_found: + status = _synctex_match_string(scanner,"Post scriptum:"); + if (statusunit = 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 (statusx_offset)); + if (statusy_offset)); + if (statuscount),(synctex_decoder_t)&_synctex_decode_int); + if (status < SYNCTEX_STATUS_EOF) { + return status; /* forward the error */ + } else if (status < SYNCTEX_STATUS_OK) { /* No Count record found */ + status = _synctex_next_line(scanner); /* Advance one more line */ + if (statusclass->type) { + case synctex_node_type_hbox: + if (SYNCTEX_INFO(box) != NULL) { + SYNCTEX_HORIZ_V(box) = SYNCTEX_HORIZ(box); + SYNCTEX_VERT_V(box) = SYNCTEX_VERT(box); + SYNCTEX_WIDTH_V(box) = SYNCTEX_WIDTH(box); + SYNCTEX_HEIGHT_V(box) = SYNCTEX_HEIGHT(box); + SYNCTEX_DEPTH_V(box) = SYNCTEX_DEPTH(box); + return SYNCTEX_STATUS_OK; + } + return SYNCTEX_STATUS_ERROR; + } + } + return SYNCTEX_STATUS_BAD_ARGUMENT; +} + +/* This method is sent to an horizontal box to setup the visible size + * Some box have 0 width but do contain text material. + * With this method, one can enlarge the box to contain the given point (h,v). + */ +synctex_status_t _synctex_hbox_setup_visible(synctex_node_t node,int h, int v) { +# ifdef __DARWIN_UNIX03 +# pragma unused(v) +# endif + int itsBtm, itsTop; + if (NULL == node || node->class->type != synctex_node_type_hbox) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + if (SYNCTEX_WIDTH_V(node)<0) { + itsBtm = SYNCTEX_HORIZ_V(node); + itsTop = SYNCTEX_HORIZ_V(node)-SYNCTEX_WIDTH_V(node); + if (hitsTop) { + SYNCTEX_WIDTH_V(node) = SYNCTEX_HORIZ_V(node) - h; + } + } else { + itsBtm = SYNCTEX_HORIZ_V(node); + itsTop = SYNCTEX_HORIZ_V(node)+SYNCTEX_WIDTH_V(node); + if (hitsTop) { + SYNCTEX_WIDTH_V(node) = h - SYNCTEX_HORIZ_V(node); + } + } + return SYNCTEX_STATUS_OK; +} + +/* Here are the control characters that strat each line of the synctex output file. + * Their values define the meaning of the line. + */ +# define SYNCTEX_CHAR_BEGIN_SHEET '{' +# define SYNCTEX_CHAR_END_SHEET '}' +# define SYNCTEX_CHAR_BEGIN_VBOX '[' +# define SYNCTEX_CHAR_END_VBOX ']' +# define SYNCTEX_CHAR_BEGIN_HBOX '(' +# define SYNCTEX_CHAR_END_HBOX ')' +# define SYNCTEX_CHAR_ANCHOR '!' +# define SYNCTEX_CHAR_VOID_VBOX 'v' +# define SYNCTEX_CHAR_VOID_HBOX 'h' +# define SYNCTEX_CHAR_KERN 'k' +# define SYNCTEX_CHAR_GLUE 'g' +# define SYNCTEX_CHAR_MATH '$' +# define SYNCTEX_CHAR_BOUNDARY 'x' + +# define SYNCTEX_RETURN(STATUS) return STATUS; + +/* Used when parsing the synctex file. A '{' character has just been parsed. + * The purpose is to gobble everything until the closing '}'. + * Actually only one nesting depth has been observed when using the clip option + * of \includegraphics option. Here we use arbitrary level of depth. + */ +synctex_status_t _synctex_scan_nested_sheet(synctex_scanner_t scanner) { + unsigned int depth = 0; +deeper: + ++depth; + if (_synctex_next_line(scanner)0) { + goto scan_next_line; + } else { + SYNCTEX_RETURN(SYNCTEX_STATUS_OK); + } + } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_BEGIN_SHEET) { + ++SYNCTEX_CUR; + goto deeper; + + } else if (_synctex_next_line(scanner)class->type != synctex_node_type_sheet + || _synctex_next_line(scanner)0){ + _synctex_error("Uncomplete sheet(0)"); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + goto prepare_loop; + } + _synctex_bail(); +/* The child loop means that we go down one level, when we just created a box node, + * the next node created is a child of this box. */ +child_loop: + if (SYNCTEX_CURclass->type == synctex_node_type_vbox) { + #define SYNCTEX_UPDATE_BOX_FRIEND(NODE)\ + friend_index = ((SYNCTEX_INFO(NODE))[SYNCTEX_TAG_IDX].INT+(SYNCTEX_INFO(NODE))[SYNCTEX_LINE_IDX].INT)%(scanner->number_of_lists);\ + SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\ + (scanner->lists_of_friends)[friend_index] = NODE; + if (NULL == SYNCTEX_CHILD(parent)) { + /* only void boxes are friends */ + SYNCTEX_UPDATE_BOX_FRIEND(parent); + } + child = parent; + parent = SYNCTEX_PARENT(child); + } else { + _synctex_error("Unexpected end of vbox, ignored."); + } + if (_synctex_next_line(scanner)class->type == synctex_node_type_hbox) { + /* Update the mean line number */ + synctex_node_t node = SYNCTEX_CHILD(parent); + if (node) { + unsigned int node_weight = 0; + unsigned int cumulated_line_numbers = 0; + do { + if (synctex_node_type(node)==synctex_node_type_hbox) { + if (SYNCTEX_NODE_WEIGHT(node)) { + node_weight += SYNCTEX_NODE_WEIGHT(node); + cumulated_line_numbers += SYNCTEX_MEAN_LINE(node)*SYNCTEX_NODE_WEIGHT(node); + } else { + ++node_weight; + cumulated_line_numbers += SYNCTEX_MEAN_LINE(node); + } + } else { + ++node_weight; + cumulated_line_numbers += SYNCTEX_LINE(node); + } + } while ((node = SYNCTEX_SIBLING(node))); + SYNCTEX_MEAN_LINE(parent)=(cumulated_line_numbers + node_weight/2)/node_weight; + SYNCTEX_NODE_WEIGHT(parent)=node_weight; + } else { + SYNCTEX_MEAN_LINE(parent)=SYNCTEX_LINE(parent); + SYNCTEX_NODE_WEIGHT(parent)=1; + } + if (NULL == child) { + /* Only boxes with no children are friends, + * boxes with children are indirectly friends through one of their contained nodes. */ + SYNCTEX_UPDATE_BOX_FRIEND(parent); + } + /* setting the next horizontal box at the end ensures that a child is recorded before any of its ancestors. */ + SYNCTEX_SET_NEXT_hbox(box,parent); + box = parent; + child = parent; + parent = SYNCTEX_PARENT(child); + } else { + _synctex_error("Unexpected end of hbox, ignored."); + } + if (_synctex_next_line(scanner)number_of_lists);\ + SYNCTEX_SET_FRIEND(NODE,(scanner->lists_of_friends)[friend_index]);\ + (scanner->lists_of_friends)[friend_index] = NODE; + SYNCTEX_UPDATE_FRIEND(child); +# if SYNCTEX_VERBOSE + synctex_node_log(child); +# endif + goto sibling_loop; + } else { + _synctex_error("Can't create vbox record."); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + } else if (*SYNCTEX_CUR == SYNCTEX_CHAR_VOID_HBOX) { + if (NULL != (child = _synctex_new_void_hbox(scanner)) + && NULL != (info = SYNCTEX_INFO(child))) { + if (SYNCTEX_DECODE_FAILED(SYNCTEX_TAG_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_LINE_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HORIZ_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_VERT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_WIDTH_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_HEIGHT_IDX) + || SYNCTEX_DECODE_FAILED(SYNCTEX_DEPTH_IDX) + || _synctex_next_line(scanner)0){ + _synctex_error("Uncomplete sheet(0)"); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } else { + goto child_loop; + } + } + _synctex_bail(); +/* The vertical loop means that we are on the same level, for example when we just ended a box. + * If a node is created now, it will be a sibling of the current node, sharing the same parent. */ +sibling_loop: + if (SYNCTEX_CUR0){ + goto sibling_loop; + } else { + _synctex_error("Uncomplete sheet(2)"); + SYNCTEX_RETURN(SYNCTEX_STATUS_ERROR); + } + } +# undef SYNCTEX_DECODE_FAILED +} + +# define SYNCTEX_APPEND_SHEET(SCANNER,SHEET) if (SCANNER->sheet) {\ + synctex_node_t last_sheet = SCANNER->sheet;\ + synctex_node_t next_sheet = NULL;\ + while ((next_sheet = SYNCTEX_SIBLING(last_sheet))) {\ + last_sheet = next_sheet;\ + }\ + SYNCTEX_SET_SIBLING(last_sheet,SHEET);\ + } else {\ + SCANNER->sheet = SHEET;\ + } + +/* Used when parsing the synctex file + */ +synctex_status_t _synctex_scan_content(synctex_scanner_t scanner) { + synctex_node_t sheet = NULL; + synctex_status_t status = 0; + if (NULL == scanner) { + return SYNCTEX_STATUS_BAD_ARGUMENT; + } + /* set up the lists of friends */ + if (NULL == scanner->lists_of_friends) { + scanner->number_of_lists = 1024; + scanner->lists_of_friends = (synctex_node_t *)_synctex_malloc(scanner->number_of_lists*sizeof(synctex_node_t)); + if (NULL == scanner->lists_of_friends) { + _synctex_error("malloc:2"); + return SYNCTEX_STATUS_ERROR; + } + } + /* Find where this section starts */ +content_not_found: + status = _synctex_match_string(scanner,"Content:"); + if (status= SYNCTEX_STATUS_OK); + goto next_sheet; +} + +int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_modeRef); + +/* Where the synctex scanner is created. */ +synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse) { + gzFile file = NULL; + char * synctex = NULL; + synctex_scanner_t scanner = NULL; + synctex_io_mode_t io_mode = 0; + /* Here we assume that int are smaller than void * */ + if (sizeof(int)>sizeof(void*)) { + _synctex_error("INTERNAL INCONSISTENCY: int's are unexpectedly bigger than pointers, bailing out."); + return NULL; + } + /* We ensure that SYNCTEX_BUFFER_SIZE < UINT_MAX, I don't know if it makes sense... */ + if (SYNCTEX_BUFFER_SIZE >= UINT_MAX) { + _synctex_error("Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (1)"); + return NULL; + } + /* for integers: */ + if (SYNCTEX_BUFFER_SIZE < SYNCTEX_BUFFER_MIN_SIZE) { + _synctex_error("Internal inconsistency, bad SYNCTEX_BUFFER_SIZE (2)"); + return NULL; + } + /* now open the synctex file */ + if (_synctex_open(output,build_directory,&synctex,&file,synctex_ADD_QUOTES,&io_mode) || !file) { + if (_synctex_open(output,build_directory,&synctex,&file,synctex_DONT_ADD_QUOTES,&io_mode) || !file) { + return NULL; + } + } + scanner = (synctex_scanner_t)_synctex_malloc(sizeof(_synctex_scanner_t)); + if (NULL == scanner) { + _synctex_error("malloc problem"); + free(synctex); + gzclose(file); + return NULL; + } + /* make a private copy of output for the scanner */ + if (NULL == (scanner->output = (char *)malloc(strlen(output)+1))){ + _synctex_error("! synctex_scanner_new_with_output_file: Memory problem (2), scanner's output is not reliable."); + } else if (scanner->output != strcpy(scanner->output,output)) { + _synctex_error("! synctex_scanner_new_with_output_file: Copy problem, scanner's output is not reliable."); + } + scanner->synctex = synctex;/* Now the scanner owns synctex */ + SYNCTEX_FILE = file; + return parse? synctex_scanner_parse(scanner):scanner; +} + +int __synctex_open(const char * output, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref); + +/* This functions opens the file at the "output" given location. + * It manages the problem of quoted filenames that appear with pdftex and filenames containing the space character. + * In TeXLive 2008, the synctex file created with pdftex did contain unexpected quotes. + * This function will remove them if possible. + * All the reference arguments will take a value on return. They must be non NULL. + * 0 on success, non 0 on error. */ +int __synctex_open(const char * output, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) { + if (synctex_name_ref && file_ref && io_mode_ref) { + /* 1 local variables that uses dynamic memory */ + char * synctex_name = NULL; + gzFile the_file = NULL; + char * quoteless_synctex_name = NULL; + size_t size = 0; + synctex_io_mode_t io_mode = *io_mode_ref; + const char * mode = _synctex_get_io_mode_name(io_mode); + /* now create the synctex file name */ + size = strlen(output)+strlen(synctex_suffix)+strlen(synctex_suffix_gz)+1; + synctex_name = (char *)malloc(size); + if (NULL == synctex_name) { + _synctex_error("! __synctex_open: Memory problem (1)\n"); + return 1; + } + /* we have reserved for synctex enough memory to copy output (including its 2 eventual quotes), both suffices, + * including the terminating character. size is free now. */ + if (synctex_name != strcpy(synctex_name,output)) { + _synctex_error("! __synctex_open: Copy problem\n"); +return_on_error: + free(synctex_name); + free(quoteless_synctex_name); + return 2; + } + /* remove the last path extension if any */ + _synctex_strip_last_path_extension(synctex_name); + if (!strlen(synctex_name)) { + goto return_on_error; + } + /* now insert quotes. */ + if (add_quotes) { + char * quoted = NULL; + if (_synctex_copy_with_quoting_last_path_component(synctex_name,"ed,size) || (NULL == quoted)) { + /* There was an error or quoting does not make sense: */ + goto return_on_error; + } + quoteless_synctex_name = synctex_name; + synctex_name = quoted; + } + /* Now add to synctex_name the first path extension. */ + if (synctex_name != strcat(synctex_name,synctex_suffix)){ + _synctex_error("! __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix); + goto return_on_error; + } + /* Add to quoteless_synctex_name as well, if relevant. */ + if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix))){ + free(quoteless_synctex_name); + quoteless_synctex_name = NULL; + } + if (NULL == (the_file = gzopen(synctex_name,mode))) { + /* Could not open this file */ + if (errno != ENOENT) { + /* The file does exist, this is a lower level error, I can't do anything. */ + _synctex_error("could not open %s, error %i\n",synctex_name,errno); + goto return_on_error; + } + /* Apparently, there is no uncompressed synctex file. Try the compressed version */ + if (synctex_name != strcat(synctex_name,synctex_suffix_gz)){ + _synctex_error("! __synctex_open: Concatenation problem (can't add suffix '%s')\n",synctex_suffix_gz); + goto return_on_error; + } + io_mode |= synctex_io_gz_mask; + mode = _synctex_get_io_mode_name(io_mode); /* the file is a compressed and is a binary file, this caused errors on Windows */ + /* Add the suffix to the quoteless_synctex_name as well. */ + if (quoteless_synctex_name && (quoteless_synctex_name != strcat(quoteless_synctex_name,synctex_suffix_gz))){ + free(quoteless_synctex_name); + quoteless_synctex_name = NULL; + } + if (NULL == (the_file = gzopen(synctex_name,mode))) { + /* Could not open this file */ + if (errno != ENOENT) { + /* The file does exist, this is a lower level error, I can't do anything. */ + _synctex_error("Could not open %s, error %i\n",synctex_name,errno); + } + goto return_on_error; + } + } + /* At this point, the file is properly open. + * If we are in the add_quotes mode, we change the file name by removing the quotes. */ + if (quoteless_synctex_name) { + gzclose(the_file); + if (rename(synctex_name,quoteless_synctex_name)) { + _synctex_error("Could not rename %s to %s, error %i\n",synctex_name,quoteless_synctex_name,errno); + /* We could not rename, reopen the file with the quoted name. */ + if (NULL == (the_file = gzopen(synctex_name,mode))) { + /* No luck, could not re open this file, something has happened meanwhile */ + if (errno != ENOENT) { + /* The file does not exist any more, it has certainly be removed somehow + * this is a lower level error, I can't do anything. */ + _synctex_error("Could not open again %s, error %i\n",synctex_name,errno); + } + goto return_on_error; + } + } else { + /* The file has been successfully renamed */ + if (NULL == (the_file = gzopen(quoteless_synctex_name,mode))) { + /* Could not open this file */ + if (errno != ENOENT) { + /* The file does exist, this is a lower level error, I can't do anything. */ + _synctex_error("Could not open renamed %s, error %i\n",quoteless_synctex_name,errno); + } + goto return_on_error; + } + /* The quote free file name should replace the old one:*/ + free(synctex_name); + synctex_name = quoteless_synctex_name; + quoteless_synctex_name = NULL; + } + } + /* The operation is successfull, return the arguments by value. */ + * file_ref = the_file; + * io_mode_ref = io_mode; + * synctex_name_ref = synctex_name; + return 0; + } + return 3; /* Bad parameter. */ +} + +/* Opens the ouput file, taking into account the eventual build_directory. + * 0 on success, non 0 on error. */ +int _synctex_open(const char * output, const char * build_directory, char ** synctex_name_ref, gzFile * file_ref, synctex_bool_t add_quotes, synctex_io_mode_t * io_mode_ref) { +# define synctex_name (*synctex_name_ref) +# define the_file (*file_ref) + int result = __synctex_open(output,synctex_name_ref,file_ref,add_quotes,io_mode_ref); + if ((result || !*file_ref) && build_directory && strlen(build_directory)) { + char * build_output; + const char *lpc; + size_t size; + synctex_bool_t is_absolute; + build_output = NULL; + lpc = _synctex_last_path_component(output); + size = strlen(build_directory)+strlen(lpc)+2; /* One for the '/' and one for the '\0'. */ + is_absolute = _synctex_path_is_absolute(build_directory); + if (!is_absolute) { + size += strlen(output); + } + if ((build_output = (char *)malloc(size))) { + if (is_absolute) { + build_output[0] = '\0'; + } else { + if (build_output != strcpy(build_output,output)) { + return -4; + } + build_output[lpc-output]='\0'; + } + if (build_output == strcat(build_output,build_directory)) { + /* Append a path separator if necessary. */ + if (!SYNCTEX_IS_PATH_SEPARATOR(build_output[strlen(build_directory)-1])) { + if (build_output != strcat(build_output,"/")) { + return -2; + } + } + /* Append the last path component of the output. */ + if (build_output != strcat(build_output,lpc)) { + return -3; + } + return __synctex_open(build_output,synctex_name_ref,file_ref,add_quotes,io_mode_ref); + } + } + return -1; + } + return result; +# undef synctex_name +# undef the_file +} + +/* The scanner destructor + */ +void synctex_scanner_free(synctex_scanner_t scanner) { + if (NULL == scanner) { + return; + } + if (SYNCTEX_FILE) { + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + } + SYNCTEX_FREE(scanner->sheet); + SYNCTEX_FREE(scanner->input); + free(SYNCTEX_START); + free(scanner->output_fmt); + free(scanner->output); + free(scanner->synctex); + free(scanner->lists_of_friends); + free(scanner); +} + +/* Where the synctex scanner parses the contents of the file. */ +synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner) { + synctex_status_t status = 0; + if (!scanner || scanner->flags.has_parsed) { + return scanner; + } + scanner->flags.has_parsed=1; + scanner->pre_magnification = 1000; + scanner->pre_unit = 8192; + scanner->pre_x_offset = scanner->pre_y_offset = 578; + /* initialize the offset with a fake unprobable value, + * If there is a post scriptum section, this value will be overriden by the real life value */ + scanner->x_offset = scanner->y_offset = 6.027e23f; +# define DEFINE_synctex_scanner_class(NAME)\ + scanner->class[synctex_node_type_##NAME] = synctex_class_##NAME;\ + (scanner->class[synctex_node_type_##NAME]).scanner = scanner + DEFINE_synctex_scanner_class(sheet); + DEFINE_synctex_scanner_class(input); + DEFINE_synctex_scanner_class(hbox); + DEFINE_synctex_scanner_class(void_hbox); + DEFINE_synctex_scanner_class(vbox); + DEFINE_synctex_scanner_class(void_vbox); + DEFINE_synctex_scanner_class(kern); + DEFINE_synctex_scanner_class(glue); + DEFINE_synctex_scanner_class(math); + DEFINE_synctex_scanner_class(boundary); + SYNCTEX_START = (char *)malloc(SYNCTEX_BUFFER_SIZE+1); /* one more character for null termination */ + if (NULL == SYNCTEX_START) { + _synctex_error("malloc error"); + synctex_scanner_free(scanner); + return NULL; + } + SYNCTEX_END = SYNCTEX_START+SYNCTEX_BUFFER_SIZE; + /* SYNCTEX_END always points to a null terminating character. + * Maybe there is another null terminating character between SYNCTEX_CUR and SYNCTEX_END-1. + * At least, we are sure that SYNCTEX_CUR points to a string covering a valid part of the memory. */ + *SYNCTEX_END = '\0'; + SYNCTEX_CUR = SYNCTEX_END; +# if defined(SYNCTEX_USE_CHARINDEX) + scanner->charindex_offset = -SYNCTEX_BUFFER_SIZE; +# endif + status = _synctex_scan_preamble(scanner); + if (statuspre_unit)/65536 pt = (scanner->pre_unit)/65781.76 bp + * 1 pt = 65536 sp */ + if (scanner->pre_unit<=0) { + scanner->pre_unit = 8192; + } + if (scanner->pre_magnification<=0) { + scanner->pre_magnification = 1000; + } + if (scanner->unit <= 0) { + /* no post magnification */ + scanner->unit = scanner->pre_unit / 65781.76;/* 65781.76 or 65536.0*/ + } else { + /* post magnification */ + scanner->unit *= scanner->pre_unit / 65781.76; + } + scanner->unit *= scanner->pre_magnification / 1000.0; + if (scanner->x_offset > 6e23) { + /* no post offset */ + scanner->x_offset = scanner->pre_x_offset * (scanner->pre_unit / 65781.76); + scanner->y_offset = scanner->pre_y_offset * (scanner->pre_unit / 65781.76); + } else { + /* post offset */ + scanner->x_offset /= 65781.76f; + scanner->y_offset /= 65781.76f; + } + return scanner; + #undef SYNCTEX_FILE +} + +/* Scanner accessors. + */ +int synctex_scanner_pre_x_offset(synctex_scanner_t scanner){ + return scanner?scanner->pre_x_offset:0; +} +int synctex_scanner_pre_y_offset(synctex_scanner_t scanner){ + return scanner?scanner->pre_y_offset:0; +} +int synctex_scanner_x_offset(synctex_scanner_t scanner){ + return scanner?scanner->x_offset:0; +} +int synctex_scanner_y_offset(synctex_scanner_t scanner){ + return scanner?scanner->y_offset:0; +} +float synctex_scanner_magnification(synctex_scanner_t scanner){ + return scanner?scanner->unit:1; +} +void synctex_scanner_display(synctex_scanner_t scanner) { + if (NULL == scanner) { + return; + } + printf("The scanner:\noutput:%s\noutput_fmt:%s\nversion:%i\n",scanner->output,scanner->output_fmt,scanner->version); + printf("pre_unit:%i\nx_offset:%i\ny_offset:%i\n",scanner->pre_unit,scanner->pre_x_offset,scanner->pre_y_offset); + printf("count:%i\npost_magnification:%f\npost_x_offset:%f\npost_y_offset:%f\n", + scanner->count,scanner->unit,scanner->x_offset,scanner->y_offset); + printf("The input:\n"); + SYNCTEX_DISPLAY(scanner->input); + if (scanner->count<1000) { + printf("The sheets:\n"); + SYNCTEX_DISPLAY(scanner->sheet); + printf("The friends:\n"); + if (scanner->lists_of_friends) { + int i = scanner->number_of_lists; + synctex_node_t node; + while(i--) { + printf("Friend index:%i\n",i); + node = (scanner->lists_of_friends)[i]; + while(node) { + printf("%s:%i,%i\n", + synctex_node_isa(node), + SYNCTEX_TAG(node), + SYNCTEX_LINE(node) + ); + node = SYNCTEX_FRIEND(node); + } + } + } + } else { + printf("SyncTeX Warning: Too many objects\n"); + } +} +/* Public*/ +const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag) { + synctex_node_t input = NULL; + if (NULL == scanner) { + return NULL; + } + input = scanner->input; + do { + if (tag == SYNCTEX_TAG(input)) { + return (SYNCTEX_NAME(input)); + } + } while((input = SYNCTEX_SIBLING(input)) != NULL); + return NULL; +} + +int _synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name); +int _synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) { + synctex_node_t input = NULL; + if (NULL == scanner) { + return 0; + } + input = scanner->input; + do { + if (_synctex_is_equivalent_file_name(name,(SYNCTEX_NAME(input)))) { + return SYNCTEX_TAG(input); + } + } while((input = SYNCTEX_SIBLING(input)) != NULL); + // 2011 version + name = _synctex_base_name(name); + input = scanner->input; + do { + if (_synctex_is_equivalent_file_name(name,_synctex_base_name(SYNCTEX_NAME(input)))) { + synctex_node_t other_input = input; + while((other_input = SYNCTEX_SIBLING(other_input)) != NULL) { + if (_synctex_is_equivalent_file_name(name,_synctex_base_name(SYNCTEX_NAME(other_input))) + && (strlen(SYNCTEX_NAME(input))!=strlen(SYNCTEX_NAME(other_input)) + || strncmp(SYNCTEX_NAME(other_input),SYNCTEX_NAME(input),strlen(SYNCTEX_NAME(input))))) { + // There is a second possible candidate + return 0; + } + } + return SYNCTEX_TAG(input); + } + } while((input = SYNCTEX_SIBLING(input)) != NULL); + return 0; +} + +int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name) { + size_t char_index = strlen(name); + if ((scanner = synctex_scanner_parse(scanner)) && (0 < char_index)) { + /* the name is not void */ + char_index -= 1; + if (!SYNCTEX_IS_PATH_SEPARATOR(name[char_index])) { + /* the last character of name is not a path separator */ + int result = _synctex_scanner_get_tag(scanner,name); + if (result) { + return result; + } else { + /* the given name was not the one known by TeX + * try a name relative to the enclosing directory of the scanner->output file */ + const char * relative = name; + const char * ptr = scanner->output; + while((strlen(relative) > 0) && (strlen(ptr) > 0) && (*relative == *ptr)) + { + relative += 1; + ptr += 1; + } + /* Find the last path separator before relative */ + while(relative > name) { + if (SYNCTEX_IS_PATH_SEPARATOR(*(relative-1))) { + break; + } + relative -= 1; + } + if ((relative > name) && (result = _synctex_scanner_get_tag(scanner,relative))) { + return result; + } + if (SYNCTEX_IS_PATH_SEPARATOR(name[0])) { + /* No tag found for the given absolute name, + * Try each relative path starting from the shortest one */ + while(0input:NULL; +} +const char * synctex_scanner_get_output_fmt(synctex_scanner_t scanner) { + return NULL != scanner && scanner->output_fmt?scanner->output_fmt:""; +} +const char * synctex_scanner_get_output(synctex_scanner_t scanner) { + return NULL != scanner && scanner->output?scanner->output:""; +} +const char * synctex_scanner_get_synctex(synctex_scanner_t scanner) { + return NULL != scanner && scanner->synctex?scanner->synctex:""; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Public node attributes +# endif +int synctex_node_h(synctex_node_t node){ + if (!node) { + return 0; + } + return SYNCTEX_HORIZ(node); +} +int synctex_node_v(synctex_node_t node){ + if (!node) { + return 0; + } + return SYNCTEX_VERT(node); +} +int synctex_node_width(synctex_node_t node){ + if (!node) { + return 0; + } + return SYNCTEX_WIDTH(node); +} +int synctex_node_box_h(synctex_node_t node){ + if (!node) { + return 0; + } + if (SYNCTEX_IS_BOX(node)) { +result: + return SYNCTEX_HORIZ(node); + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +int synctex_node_box_v(synctex_node_t node){ + if (!node) { + return 0; + } + if (SYNCTEX_IS_BOX(node)) { +result: + return SYNCTEX_VERT(node); + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +int synctex_node_box_width(synctex_node_t node){ + if (!node) { + return 0; + } + if (SYNCTEX_IS_BOX(node)) { +result: + return SYNCTEX_WIDTH(node); + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +int synctex_node_box_height(synctex_node_t node){ + if (!node) { + return 0; + } + if (SYNCTEX_IS_BOX(node)) { +result: + return SYNCTEX_HEIGHT(node); + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +int synctex_node_box_depth(synctex_node_t node){ + if (!node) { + return 0; + } + if (SYNCTEX_IS_BOX(node)) { +result: + return SYNCTEX_DEPTH(node); + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Public node visible attributes +# endif +float synctex_node_visible_h(synctex_node_t node){ + if (!node) { + return 0; + } + return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset; +} +float synctex_node_visible_v(synctex_node_t node){ + if (!node) { + return 0; + } + return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset; +} +float synctex_node_visible_width(synctex_node_t node){ + if (!node) { + return 0; + } + return SYNCTEX_WIDTH(node)*node->class->scanner->unit; +} +float synctex_node_box_visible_h(synctex_node_t node){ + if (!node) { + return 0; + } + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + return SYNCTEX_HORIZ(node)*node->class->scanner->unit+node->class->scanner->x_offset; + case synctex_node_type_hbox: +result: + return SYNCTEX_HORIZ_V(node)*node->class->scanner->unit+node->class->scanner->x_offset; + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +float synctex_node_box_visible_v(synctex_node_t node){ + if (!node) { + return 0; + } + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + return SYNCTEX_VERT(node)*node->class->scanner->unit+node->class->scanner->y_offset; + case synctex_node_type_hbox: +result: + return SYNCTEX_VERT_V(node)*node->class->scanner->unit+node->class->scanner->y_offset; + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +float synctex_node_box_visible_width(synctex_node_t node){ + if (!node) { + return 0; + } + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + return SYNCTEX_WIDTH(node)*node->class->scanner->unit; + case synctex_node_type_hbox: +result: + return SYNCTEX_WIDTH_V(node)*node->class->scanner->unit; + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +float synctex_node_box_visible_height(synctex_node_t node){ + if (!node) { + return 0; + } + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + return SYNCTEX_HEIGHT(node)*node->class->scanner->unit; + case synctex_node_type_hbox: +result: + return SYNCTEX_HEIGHT_V(node)*node->class->scanner->unit; + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +float synctex_node_box_visible_depth(synctex_node_t node){ + if (!node) { + return 0; + } + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + return SYNCTEX_DEPTH(node)*node->class->scanner->unit; + case synctex_node_type_hbox: +result: + return SYNCTEX_DEPTH_V(node)*node->class->scanner->unit; + } + if ((node = SYNCTEX_PARENT(node)) && (node->class->type != synctex_node_type_sheet)) { + goto result; + } + return 0; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Other public node attributes +# endif + +int synctex_node_page(synctex_node_t node){ + synctex_node_t parent = NULL; + if (!node) { + return -1; + } + parent = SYNCTEX_PARENT(node); + while(parent) { + node = parent; + parent = SYNCTEX_PARENT(node); + } + if (node->class->type == synctex_node_type_sheet) { + return SYNCTEX_PAGE(node); + } + return -1; +} +synctex_charindex_t synctex_node_charindex(synctex_node_t node) { + return node?SYNCTEX_CHARINDEX(node):0; +} +int synctex_node_tag(synctex_node_t node) { + return node?SYNCTEX_TAG(node):-1; +} +int synctex_node_line(synctex_node_t node) { + return node?SYNCTEX_LINE(node):-1; +} +int synctex_node_mean_line(synctex_node_t node) { + return node?(node->class->type==synctex_node_type_hbox?SYNCTEX_MEAN_LINE(node):SYNCTEX_LINE(node)):-1; +} +int synctex_node_child_count(synctex_node_t node) { + return node?(node->class->type==synctex_node_type_hbox?SYNCTEX_NODE_WEIGHT(node):0):-1; +} +int synctex_node_column(synctex_node_t node) { +# ifdef __DARWIN_UNIX03 +# pragma unused(node) +# endif + return -1; +} +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Sheet +# endif + +synctex_node_t synctex_sheet(synctex_scanner_t scanner,int page) { + if (scanner) { + synctex_node_t sheet = scanner->sheet; + while(sheet) { + if (page == SYNCTEX_PAGE(sheet)) { + return sheet; + } + sheet = SYNCTEX_SIBLING(sheet); + } + } + return NULL; +} + +synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page) { + if (scanner) { + return SYNCTEX_CHILD(synctex_sheet(scanner,page)); + } + return NULL; +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Query +# endif + +int synctex_display_query(synctex_scanner_t scanner,const char * name,int line,int column) { +# ifdef __DARWIN_UNIX03 +# pragma unused(column) +# endif + int tag = synctex_scanner_get_tag(scanner,name); + size_t size = 0; + int friend_index = 0; + int max_line = 0; + synctex_node_t node = NULL; + if (tag == 0) { + printf("SyncTeX Warning: No tag for %s\n",name); + return -1; + } + free(SYNCTEX_START); + SYNCTEX_CUR = SYNCTEX_END = SYNCTEX_START = NULL; + max_line = line < INT_MAX-scanner->number_of_lists ? line+scanner->number_of_lists:INT_MAX; + while(linenumber_of_lists); + if ((node = (scanner->lists_of_friends)[friend_index])) { + do { + if ((synctex_node_type(node)>=synctex_node_type_boundary) + && (tag == SYNCTEX_TAG(node)) + && (line == SYNCTEX_LINE(node))) { + if (SYNCTEX_CUR == SYNCTEX_END) { + size += 16; + SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *)); + SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START; + SYNCTEX_START = SYNCTEX_END; + SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *); + } + *(synctex_node_t *)SYNCTEX_CUR = node; + SYNCTEX_CUR += sizeof(synctex_node_t); + } + } while ((node = SYNCTEX_FRIEND(node))); + if (SYNCTEX_START == NULL) { + /* We did not find any matching boundary, retry with glue or kern */ + node = (scanner->lists_of_friends)[friend_index];/* no need to test it again, already done */ + do { + if ((synctex_node_type(node)>=synctex_node_type_kern) + && (tag == SYNCTEX_TAG(node)) + && (line == SYNCTEX_LINE(node))) { + if (SYNCTEX_CUR == SYNCTEX_END) { + size += 16; + SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *)); + SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START; + SYNCTEX_START = SYNCTEX_END; + SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *); + } + *(synctex_node_t *)SYNCTEX_CUR = node; + SYNCTEX_CUR += sizeof(synctex_node_t); + } + } while ((node = SYNCTEX_FRIEND(node))); + if (SYNCTEX_START == NULL) { + /* We did not find any matching glue or kern, retry with boxes */ + node = (scanner->lists_of_friends)[friend_index];/* no need to test it again, already done */ + do { + if ((tag == SYNCTEX_TAG(node)) + && (line == SYNCTEX_LINE(node))) { + if (SYNCTEX_CUR == SYNCTEX_END) { + size += 16; + SYNCTEX_END = realloc(SYNCTEX_START,size*sizeof(synctex_node_t *)); + SYNCTEX_CUR += SYNCTEX_END - SYNCTEX_START; + SYNCTEX_START = SYNCTEX_END; + SYNCTEX_END = SYNCTEX_START + size*sizeof(synctex_node_t *); + } + *(synctex_node_t *)SYNCTEX_CUR = node; + SYNCTEX_CUR += sizeof(synctex_node_t); + } + } while((node = SYNCTEX_FRIEND(node))); + } + } + SYNCTEX_END = SYNCTEX_CUR; + /* Now reverse the order to have nodes in display order, and then keep just a few nodes. + * Order first the best node. */ + if ((SYNCTEX_START) && (SYNCTEX_END)) { + unsigned int best_match = -1; + unsigned int next_match = -1; + unsigned int best_weight = 0; + synctex_node_t * best_ref = NULL; + synctex_node_t * start_ref = (synctex_node_t *)SYNCTEX_START; + synctex_node_t * end_ref = (synctex_node_t *)SYNCTEX_END; + --end_ref; + while (start_ref < end_ref) { + node = *start_ref; + *start_ref = *end_ref; + *end_ref = node; + ++start_ref; + --end_ref; + } + /* Now reorder the nodes to put first the one which fits best. + * The idea is to walk along the list of nodes and pick up the first one + * which line info is exactly the mean line of its parent, or at least very close. + * Then we choose among all such node the one with the maximum number of child nodes. + * Then we switch with the first node. + */ + best_ref = start_ref = (synctex_node_t *)SYNCTEX_START; + node = *start_ref; + best_match = abs(SYNCTEX_LINE(node)-SYNCTEX_MEAN_LINE(SYNCTEX_PARENT(node))); + end_ref = (synctex_node_t *)SYNCTEX_END; + while (++start_refbest_weight)) { + best_match = next_match; + best_ref = start_ref; + best_weight = SYNCTEX_NODE_WEIGHT(parent); + } + } + node = *best_ref; + *best_ref = *(synctex_node_t *)SYNCTEX_START; + *(synctex_node_t *)SYNCTEX_START = node; + /* Basically, we keep the first node for each parent. + * More precisely, we keep only nodes that are not children of + * their predecessor's parent. */ + start_ref = (synctex_node_t *)SYNCTEX_START; + end_ref = (synctex_node_t *)SYNCTEX_START; +next_end: + end_ref += 1; /* we allways have start_ref<= end_ref*/ + if (end_ref < (synctex_node_t *)SYNCTEX_END) { + node = *end_ref; + while ((node = SYNCTEX_PARENT(node))) { + if (SYNCTEX_PARENT(*start_ref) == node) { + goto next_end; + } + } + start_ref += 1; + *start_ref = *end_ref; + goto next_end; + } + start_ref += 1; + SYNCTEX_END = (char *)start_ref; + SYNCTEX_CUR = NULL;// added on behalf of Jose Alliste + return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t);// added on behalf Jan Sundermeyer + } + SYNCTEX_CUR = NULL; + // return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); removed on behalf Jan Sundermeyer + } +# if defined(__SYNCTEX_STRONG_DISPLAY_QUERY__) + break; +# else + ++line; +# endif + } + return 0; +} + +synctex_node_t synctex_next_result(synctex_scanner_t scanner) { + if (NULL == SYNCTEX_CUR) { + SYNCTEX_CUR = SYNCTEX_START; + } else { + SYNCTEX_CUR+=sizeof(synctex_node_t); + } + if (SYNCTEX_CUR= scanner->unit) {/* scanner->unit must be >0 */ + return 0; + } + /* Convert the given point to scanner integer coordinates */ + hitPoint.h = (h-scanner->x_offset)/scanner->unit; + hitPoint.v = (v-scanner->y_offset)/scanner->unit; + /* We will store in the scanner's buffer the result of the query. */ + free(SYNCTEX_START); + SYNCTEX_START = SYNCTEX_END = SYNCTEX_CUR = NULL; + /* Find the proper sheet */ + sheet = scanner->sheet; + while((sheet) && SYNCTEX_PAGE(sheet) != page) { + sheet = SYNCTEX_SIBLING(sheet); + } + if (NULL == sheet) { + return -1; + } + /* Now sheet points to the sheet node with proper page number */ + /* Here is how we work: + * At first we do not consider the visible box dimensions. This will cover the most frequent cases. + * Then we try with the visible box dimensions. + * We try to find a non void box containing the hit point. + * We browse all the horizontal boxes until we find one containing the hit point. */ + if ((node = SYNCTEX_NEXT_hbox(sheet))) { + do { + if (_synctex_point_in_box(hitPoint,node,synctex_YES)) { + /* Maybe the hitPoint belongs to a contained vertical box. */ +end: + /* This trick is for catching overlapping boxes */ + if ((other_node = SYNCTEX_NEXT_hbox(node))) { + do { + if (_synctex_point_in_box(hitPoint,other_node,synctex_YES)) { + node = _synctex_smallest_container(other_node,node); + } + } while((other_node = SYNCTEX_NEXT_hbox(other_node))); + } + /* node is the smallest horizontal box that contains hitPoint. */ + if ((bestContainer = _synctex_eq_deepest_container(hitPoint,node,synctex_YES))) { + node = bestContainer; + } + _synctex_eq_get_closest_children_in_box(hitPoint,node,&bestNodes,&bestDistances,synctex_YES); + if (bestNodes.right && bestNodes.left) { + if ((SYNCTEX_TAG(bestNodes.right)!=SYNCTEX_TAG(bestNodes.left)) + || (SYNCTEX_LINE(bestNodes.right)!=SYNCTEX_LINE(bestNodes.left)) + || (SYNCTEX_COLUMN(bestNodes.right)!=SYNCTEX_COLUMN(bestNodes.left))) { + if ((SYNCTEX_START = malloc(2*sizeof(synctex_node_t)))) { + if (bestDistances.left>bestDistances.right) { + ((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.right; + ((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.left; + } else { + ((synctex_node_t *)SYNCTEX_START)[0] = bestNodes.left; + ((synctex_node_t *)SYNCTEX_START)[1] = bestNodes.right; + } + SYNCTEX_END = SYNCTEX_START + 2*sizeof(synctex_node_t); + SYNCTEX_CUR = NULL; + return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); + } + return SYNCTEX_STATUS_ERROR; + } + /* both nodes have the same input coordinates + * We choose the one closest to the hit point */ + if (bestDistances.left>bestDistances.right) { + bestNodes.left = bestNodes.right; + } + bestNodes.right = NULL; + } else if (bestNodes.right) { + bestNodes.left = bestNodes.right; + } else if (!bestNodes.left){ + bestNodes.left = node; + } + if ((SYNCTEX_START = malloc(sizeof(synctex_node_t)))) { + * (synctex_node_t *)SYNCTEX_START = bestNodes.left; + SYNCTEX_END = SYNCTEX_START + sizeof(synctex_node_t); + SYNCTEX_CUR = NULL; + return (SYNCTEX_END-SYNCTEX_START)/sizeof(synctex_node_t); + } + return SYNCTEX_STATUS_ERROR; + } + } while ((node = SYNCTEX_NEXT_hbox(node))); + /* All the horizontal boxes have been tested, + * None of them contains the hit point. + */ + } + /* We are not lucky */ + if ((node = SYNCTEX_CHILD(sheet))) { + goto end; + } + return 0; +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Utilities +# endif + +int _synctex_bail(void) { + _synctex_error("SyncTeX ERROR\n"); + return -1; +} +/* Rougly speaking, this is: + * node's h coordinate - hitPoint's h coordinate. + * If node is to the right of the hit point, then this distance is positive, + * if node is to the left of the hit point, this distance is negative.*/ +int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible); +int _synctex_point_h_distance(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) { + if (node) { + int min,med,max; + switch(node->class->type) { + /* The distance between a point and a box is special. + * It is not the euclidian distance, nor something similar. + * We have to take into account the particular layout, + * and the box hierarchy. + * Given a box, there are 9 regions delimited by the lines of the edges of the box. + * The origin being at the top left corner of the page, + * we also give names to the vertices of the box. + * + * 1 | 2 | 3 + * ---A---B---> + * 4 | 5 | 6 + * ---C---D---> + * 7 | 8 | 9 + * v v + */ + case synctex_node_type_hbox: + /* getting the box bounds, taking into account negative width, height and depth. */ + min = visible?SYNCTEX_HORIZ_V(node):SYNCTEX_HORIZ(node); + max = min + (visible?SYNCTEX_ABS_WIDTH_V(node):SYNCTEX_ABS_WIDTH(node)); + /* We allways have min <= max */ + if (hitPoint.h 0 */ + } else if (hitPoint.h>max) { + return max - hitPoint.h; /* regions 3+6+9, result is < 0 */ + } else { + return 0; /* regions 2+5+8, inside the box, except for vertical coordinates */ + } + break; + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + /* getting the box bounds, taking into account negative width, height and depth. + * For these boxes, no visible dimension available */ + min = SYNCTEX_HORIZ(node); + max = min + SYNCTEX_ABS_WIDTH(node); + /* We allways have min <= max */ + if (hitPoint.h 0 */ + } else if (hitPoint.h>max) { + return max - hitPoint.h; /* regions 3+6+9, result is < 0 */ + } else { + return 0; /* regions 2+5+8, inside the box, except for vertical coordinates */ + } + break; + case synctex_node_type_kern: + /* IMPORTANT NOTICE: the location of the kern is recorded AFTER the move. + * The distance to the kern is very special, + * in general, there is no text material in the kern, + * this is why we compute the offset relative to the closest edge of the kern.*/ + max = SYNCTEX_WIDTH(node); + if (max<0) { + min = SYNCTEX_HORIZ(node); + max = min - max; + } else { + min = -max; + max = SYNCTEX_HORIZ(node); + min += max; + } + med = (min+max)/2; + /* positive kern: '.' means text, '>' means kern offset + * ............. + * min>>>>med>>>>max + * ............... + * negative kern: '.' means text, '<' means kern offset + * ............................ + * min<<<max) { + return max - hitPoint.h - 1; /* same kind of penalty */ + } else if (hitPoint.h>med) { + /* do things like if the node had 0 width and was placed at the max edge + 1*/ + return max - hitPoint.h + 1; /* positive, the kern is to the right of the hitPoint */ + } else { + return min - hitPoint.h - 1; /* negative, the kern is to the left of the hitPoint */ + } + case synctex_node_type_glue: + case synctex_node_type_math: + return SYNCTEX_HORIZ(node) - hitPoint.h; + } + } + return INT_MAX;/* We always assume that the node is faraway to the right*/ +} +/* Rougly speaking, this is: + * node's v coordinate - hitPoint's v coordinate. + * If node is at the top of the hit point, then this distance is positive, + * if node is at the bottom of the hit point, this distance is negative.*/ +int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible); +int _synctex_point_v_distance(synctex_point_t hitPoint, synctex_node_t node,synctex_bool_t visible) { +# ifdef __DARWIN_UNIX03 +# pragma unused(visible) +# endif + if (node) { + int min,max; + switch(node->class->type) { + /* The distance between a point and a box is special. + * It is not the euclidian distance, nor something similar. + * We have to take into account the particular layout, + * and the box hierarchy. + * Given a box, there are 9 regions delimited by the lines of the edges of the box. + * The origin being at the top left corner of the page, + * we also give names to the vertices of the box. + * + * 1 | 2 | 3 + * ---A---B---> + * 4 | 5 | 6 + * ---C---D---> + * 7 | 8 | 9 + * v v + */ + case synctex_node_type_hbox: + /* getting the box bounds, taking into account negative width, height and depth. */ + min = SYNCTEX_VERT_V(node); + max = min + SYNCTEX_ABS_DEPTH_V(node); + min -= SYNCTEX_ABS_HEIGHT_V(node); + /* We allways have min <= max */ + if (hitPoint.v 0 */ + } else if (hitPoint.v>max) { + return max - hitPoint.v; /* regions 7+8+9, result is < 0 */ + } else { + return 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */ + } + break; + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_void_hbox: + /* getting the box bounds, taking into account negative width, height and depth. */ + min = SYNCTEX_VERT(node); + max = min + SYNCTEX_ABS_DEPTH(node); + min -= SYNCTEX_ABS_HEIGHT(node); + /* We allways have min <= max */ + if (hitPoint.v 0 */ + } else if (hitPoint.v>max) { + return max - hitPoint.v; /* regions 7+8+9, result is < 0 */ + } else { + return 0; /* regions 4.5.6, inside the box, except for horizontal coordinates */ + } + break; + case synctex_node_type_kern: + case synctex_node_type_glue: + case synctex_node_type_math: + return SYNCTEX_VERT(node) - hitPoint.v; + } + } + return INT_MAX;/* We always assume that the node is faraway to the top*/ +} + +SYNCTEX_INLINE static synctex_node_t _synctex_smallest_container(synctex_node_t node, synctex_node_t other_node) { + float height, other_height; + if (SYNCTEX_ABS_WIDTH(node)SYNCTEX_ABS_WIDTH(other_node)) { + return other_node; + } + height = SYNCTEX_ABS_DEPTH(node) + SYNCTEX_ABS_HEIGHT(node); + other_height = SYNCTEX_ABS_DEPTH(other_node) + SYNCTEX_ABS_HEIGHT(other_node); + if (heightother_height) { + return other_node; + } + return node; +} + +synctex_bool_t _synctex_point_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) { + if (node) { + if (0 == _synctex_point_h_distance(hitPoint,node,visible) + && 0 == _synctex_point_v_distance(hitPoint,node,visible)) { + return synctex_YES; + } + } + return synctex_NO; +} + +int _synctex_node_distance_to_point(synctex_point_t hitPoint, synctex_node_t node, synctex_bool_t visible) { +# ifdef __DARWIN_UNIX03 +# pragma unused(visible) +# endif + int result = INT_MAX; /* when the distance is meaning less (sheet, input...) */ + if (node) { + int minH,maxH,minV,maxV; + switch(node->class->type) { + /* The distance between a point and a box is special. + * It is not the euclidian distance, nor something similar. + * We have to take into account the particular layout, + * and the box hierarchy. + * Given a box, there are 9 regions delimited by the lines of the edges of the box. + * The origin being at the top left corner of the page, + * we also give names to the vertices of the box. + * + * 1 | 2 | 3 + * ---A---B---> + * 4 | 5 | 6 + * ---C---D---> + * 7 | 8 | 9 + * v v + * In each region, there is a different formula. + * In the end we have a continuous distance which may not be a mathematical distance but who cares. */ + case synctex_node_type_vbox: + case synctex_node_type_void_vbox: + case synctex_node_type_hbox: + case synctex_node_type_void_hbox: + /* getting the box bounds, taking into account negative widths. */ + minH = SYNCTEX_HORIZ(node); + maxH = minH + SYNCTEX_ABS_WIDTH(node); + minV = SYNCTEX_VERT(node); + maxV = minV + SYNCTEX_ABS_DEPTH(node); + minV -= SYNCTEX_ABS_HEIGHT(node); + /* In what region is the point hitPoint=(H,V) ? */ + if (hitPoint.vminV) { + result = hitPoint.v - minV + minH - hitPoint.h; + } else { + result = minV - hitPoint.v + minH - hitPoint.h; + } + } else if (hitPoint.h>maxH) { + if (hitPoint.v>minV) { + result = hitPoint.v - minV + hitPoint.h - maxH; + } else { + result = minV - hitPoint.v + hitPoint.h - maxH; + } + } else if (hitPoint.v>minV) { + result = hitPoint.v - minV; + } else { + result = minV - hitPoint.v; + } + break; + case synctex_node_type_glue: + case synctex_node_type_math: + minH = SYNCTEX_HORIZ(node); + minV = SYNCTEX_VERT(node); + if (hitPoint.hminV) { + result = hitPoint.v - minV + minH - hitPoint.h; + } else { + result = minV - hitPoint.v + minH - hitPoint.h; + } + } else if (hitPoint.v>minV) { + result = hitPoint.v - minV + hitPoint.h - minH; + } else { + result = minV - hitPoint.v + hitPoint.h - minH; + } + break; + } + } + return result; +} + +static synctex_node_t _synctex_eq_deepest_container(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) { + if (node) { + synctex_node_t result = NULL; + synctex_node_t child = NULL; + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_hbox: + /* test the deep nodes first */ + if ((child = SYNCTEX_CHILD(node))) { + do { + if ((result = _synctex_eq_deepest_container(hitPoint,child,visible))) { + return result; + } + } while((child = SYNCTEX_SIBLING(child))); + } + /* is the hit point inside the box? */ + if (_synctex_point_in_box(hitPoint,node,visible)) { + /* for vboxes we try to use some node inside. + * Walk through the list of siblings until we find the closest one. + * Only consider siblings with children. */ + if ((node->class->type == synctex_node_type_vbox) && (child = SYNCTEX_CHILD(node))) { + int bestDistance = INT_MAX; + do { + if (SYNCTEX_CHILD(child)) { + int distance = _synctex_node_distance_to_point(hitPoint,child,visible); + if (distance < bestDistance) { + bestDistance = distance; + node = child; + } + } + } while((child = SYNCTEX_SIBLING(child))); + } + return node; + } + } + } + return NULL; +} + +/* Compares the locations of the hitPoint with the locations of the various nodes contained in the box. + * As it is an horizontal box, we only compare horizontal coordinates. */ +SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef, synctex_bool_t visible); +SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_hbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef, synctex_bool_t visible) { + int result = 0; + if ((node = SYNCTEX_CHILD(node))) { + do { + int off7 = _synctex_point_h_distance(hitPoint,node,visible); + if (off7 > 0) { + /* node is to the right of the hit point. + * We compare node and the previously recorded one, through the recorded distance. + * If the nodes have the same tag, prefer the one with the smallest line number, + * if the nodes also have the same line number, prefer the one with the smallest column. */ + if (bestDistancesRef->right > off7) { + bestDistancesRef->right = off7; + bestNodesRef->right = node; + result |= SYNCTEX_MASK_RIGHT; + } else if (bestDistancesRef->right == off7 && bestNodesRef->right) { + if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node) + && (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node) + || (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node) + && SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) { + bestNodesRef->right = node; + result |= SYNCTEX_MASK_RIGHT; + } + } + } else if (off7 == 0) { + /* hitPoint is inside node. */ + bestDistancesRef->left = bestDistancesRef->right = 0; + bestNodesRef->left = node; + bestNodesRef->right = NULL; + result |= SYNCTEX_MASK_LEFT; + } else { /* here off7 < 0, hitPoint is to the right of node */ + off7 = -off7; + if (bestDistancesRef->left > off7) { + bestDistancesRef->left = off7; + bestNodesRef->left = node; + result |= SYNCTEX_MASK_LEFT; + } else if (bestDistancesRef->left == off7 && bestNodesRef->left) { + if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node) + && (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node) + || (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node) + && SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) { + bestNodesRef->left = node; + result |= SYNCTEX_MASK_LEFT; + } + } + } + } while((node = SYNCTEX_SIBLING(node))); + if (result & SYNCTEX_MASK_LEFT) { + /* the left node is new, try to narrow the result */ + if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) { + bestNodesRef->left = node; + } + if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) { + bestNodesRef->left = node; + } + } + if (result & SYNCTEX_MASK_RIGHT) { + /* the right node is new, try to narrow the result */ + if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) { + bestNodesRef->right = node; + } + if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) { + bestNodesRef->right = node; + } + } + } + return result; +} +SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef,synctex_bool_t visible); +SYNCTEX_INLINE static int __synctex_eq_get_closest_children_in_vbox(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef,synctex_bool_t visible) { + int result = 0; + if ((node = SYNCTEX_CHILD(node))) { + do { + int off7 = _synctex_point_v_distance(hitPoint,node,visible);/* this is what makes the difference with the h version above */ + if (off7 > 0) { + /* node is to the top of the hit point (below because TeX is oriented from top to bottom. + * We compare node and the previously recorded one, through the recorded distance. + * If the nodes have the same tag, prefer the one with the smallest line number, + * if the nodes also have the same line number, prefer the one with the smallest column. */ + if (bestDistancesRef->right > off7) { + bestDistancesRef->right = off7; + bestNodesRef->right = node; + result |= SYNCTEX_MASK_RIGHT; + } else if (bestDistancesRef->right == off7 && bestNodesRef->right) { + if (SYNCTEX_TAG(bestNodesRef->right) == SYNCTEX_TAG(node) + && (SYNCTEX_LINE(bestNodesRef->right) > SYNCTEX_LINE(node) + || (SYNCTEX_LINE(bestNodesRef->right) == SYNCTEX_LINE(node) + && SYNCTEX_COLUMN(bestNodesRef->right) > SYNCTEX_COLUMN(node)))) { + bestNodesRef->right = node; + result |= SYNCTEX_MASK_RIGHT; + } + } + } else if (off7 == 0) { + bestDistancesRef->left = bestDistancesRef->right = 0; + bestNodesRef->left = node; + bestNodesRef->right = NULL; + result |= SYNCTEX_MASK_LEFT; + } else { /* here off7 < 0 */ + off7 = -off7; + if (bestDistancesRef->left > off7) { + bestDistancesRef->left = off7; + bestNodesRef->left = node; + result |= SYNCTEX_MASK_LEFT; + } else if (bestDistancesRef->left == off7 && bestNodesRef->left) { + if (SYNCTEX_TAG(bestNodesRef->left) == SYNCTEX_TAG(node) + && (SYNCTEX_LINE(bestNodesRef->left) > SYNCTEX_LINE(node) + || (SYNCTEX_LINE(bestNodesRef->left) == SYNCTEX_LINE(node) + && SYNCTEX_COLUMN(bestNodesRef->left) > SYNCTEX_COLUMN(node)))) { + bestNodesRef->left = node; + result |= SYNCTEX_MASK_LEFT; + } + } + } + } while((node = SYNCTEX_SIBLING(node))); + if (result & SYNCTEX_MASK_LEFT) { + /* the left node is new, try to narrow the result */ + if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->left,visible))) { + bestNodesRef->left = node; + } + if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->left,visible))) { + bestNodesRef->left = node; + } + } + if (result & SYNCTEX_MASK_RIGHT) { + /* the right node is new, try to narrow the result */ + if ((node = _synctex_eq_deepest_container(hitPoint,bestNodesRef->right,visible))) { + bestNodesRef->right = node; + } + if ((node = _synctex_eq_closest_child(hitPoint,bestNodesRef->right,visible))) { + bestNodesRef->right = node; + } + } + } + return result; +} +SYNCTEX_INLINE static int _synctex_eq_get_closest_children_in_box(synctex_point_t hitPoint, synctex_node_t node, synctex_node_set_t* bestNodesRef,synctex_distances_t* bestDistancesRef,synctex_bool_t visible) { + if (node) { + switch(node->class->type) { + case synctex_node_type_hbox: + return __synctex_eq_get_closest_children_in_hbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible); + case synctex_node_type_vbox: + return __synctex_eq_get_closest_children_in_vbox(hitPoint, node, bestNodesRef, bestDistancesRef,visible); + } + } + return 0; +} + +SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int* distanceRef, synctex_bool_t visible); +SYNCTEX_INLINE static synctex_node_t __synctex_eq_closest_child(synctex_point_t hitPoint, synctex_node_t node,int* distanceRef, synctex_bool_t visible) { + synctex_node_t best_node = NULL; + if ((node = SYNCTEX_CHILD(node))) { + do { + int distance = _synctex_node_distance_to_point(hitPoint,node,visible); + synctex_node_t candidate = NULL; + if (distance<=*distanceRef) { + *distanceRef = distance; + best_node = node; + } + switch(node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_hbox: + if ((candidate = __synctex_eq_closest_child(hitPoint,node,distanceRef,visible))) { + best_node = candidate; + } + } + } while((node = SYNCTEX_SIBLING(node))); + } + return best_node; +} +SYNCTEX_INLINE static synctex_node_t _synctex_eq_closest_child(synctex_point_t hitPoint,synctex_node_t node, synctex_bool_t visible) { + if (node) { + switch(node->class->type) { + case synctex_node_type_hbox: + case synctex_node_type_vbox: + { + int best_distance = INT_MAX; + synctex_node_t best_node = __synctex_eq_closest_child(hitPoint,node,&best_distance,visible); + if ((best_node)) { + synctex_node_t child = NULL; + switch(best_node->class->type) { + case synctex_node_type_vbox: + case synctex_node_type_hbox: + if ((child = SYNCTEX_CHILD(best_node))) { + best_distance = _synctex_node_distance_to_point(hitPoint,child,visible); + while((child = SYNCTEX_SIBLING(child))) { + int distance = _synctex_node_distance_to_point(hitPoint,child,visible); + if (distance<=best_distance) { + best_distance = distance; + best_node = child; + } + } + } + } + } + return best_node; + } + } + } + return NULL; +} + +# ifdef SYNCTEX_NOTHING +# pragma mark - +# pragma mark Updater +# endif + +typedef int (*synctex_fprintf_t)(void *, const char * , ...); /* print formatted to either FILE * or gzFile */ + +# define SYNCTEX_BITS_PER_BYTE 8 + +struct __synctex_updater_t { + gzFile file; /* the foo.synctex or foo.synctex.gz I/O identifier */ + synctex_fprintf_t fprintf; /* either fprintf or gzprintf */ + int length; /* the number of chars appended */ + struct _flags { + unsigned int no_gz:1; /* Whether zlib is used or not */ + unsigned int reserved:SYNCTEX_BITS_PER_BYTE*sizeof(int)-1; /* Align */ + } flags; +}; +# define SYNCTEX_FILE updater->file +# define SYNCTEX_NO_GZ ((updater->flags).no_gz) +# define SYNCTEX_fprintf (*(updater->fprintf)) + +synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * build_directory) { + synctex_updater_t updater = NULL; + char * synctex = NULL; + synctex_io_mode_t io_mode = 0; + const char * mode = NULL; + /* prepare the updater, the memory is the only one dynamically allocated */ + updater = (synctex_updater_t)_synctex_malloc(sizeof(synctex_updater_t)); + if (NULL == updater) { + _synctex_error("! synctex_updater_new_with_file: malloc problem"); + return NULL; + } + if (_synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_ADD_QUOTES,&io_mode) + && _synctex_open(output,build_directory,&synctex,&SYNCTEX_FILE,synctex_DONT_ADD_QUOTES,&io_mode)) { +return_on_error: + free(updater); + updater = NULL; + return NULL; + } + /* OK, the file exists, we close it and reopen it with the correct mode. + * The receiver is now the owner of the "synctex" variable. */ + gzclose(SYNCTEX_FILE); + SYNCTEX_FILE = NULL; + SYNCTEX_NO_GZ = (io_mode&synctex_io_gz_mask)?synctex_NO:synctex_YES; + mode = _synctex_get_io_mode_name(io_mode|synctex_io_append_mask);/* either "a" or "ab", depending on the file extension */ + if (SYNCTEX_NO_GZ) { + if (NULL == (SYNCTEX_FILE = (void *)fopen(synctex,mode))) { +no_write_error: + _synctex_error("! synctex_updater_new_with_file: Can't append to %s",synctex); + free(synctex); + goto return_on_error; + } + updater->fprintf = (synctex_fprintf_t)(&fprintf); + } else { + if (NULL == (SYNCTEX_FILE = (void *)gzopen(synctex,mode))) { + goto no_write_error; + } + updater->fprintf = (synctex_fprintf_t)(&gzprintf); + } + printf("SyncTeX: updating %s...",synctex); + free(synctex); + return updater; +} + + +void synctex_updater_append_magnification(synctex_updater_t updater, char * magnification){ + if (NULL==updater) { + return; + } + if (magnification && strlen(magnification)) { + updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Magnification:%s\n",magnification); + } +} + +void synctex_updater_append_x_offset(synctex_updater_t updater, char * x_offset){ + if (NULL==updater) { + return; + } + if (x_offset && strlen(x_offset)) { + updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"X Offset:%s\n",x_offset); + } +} + +void synctex_updater_append_y_offset(synctex_updater_t updater, char * y_offset){ + if (NULL==updater) { + return; + } + if (y_offset && strlen(y_offset)) { + updater->length += SYNCTEX_fprintf(SYNCTEX_FILE,"Y Offset:%s\n",y_offset); + } +} + +void synctex_updater_free(synctex_updater_t updater){ + if (NULL==updater) { + return; + } + if (updater->length>0) { + SYNCTEX_fprintf(SYNCTEX_FILE,"!%i\n",updater->length); + } + if (SYNCTEX_NO_GZ) { + fclose((FILE *)SYNCTEX_FILE); + } else { + gzclose((gzFile)SYNCTEX_FILE); + } + free(updater); + printf("... done.\n"); + return; +} diff --git a/synctex/synctex_parser.h b/synctex/synctex_parser.h new file mode 100644 index 0000000..3151707 --- /dev/null +++ b/synctex/synctex_parser.h @@ -0,0 +1,360 @@ +/* +Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr + +This file is part of the SyncTeX package. + +Latest Revision: Tue Jun 14 08:23:30 UTC 2011 + +Version: 1.17 + +See synctex_parser_readme.txt for more details + +License: +-------- +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE + +Except as contained in this notice, the name of the copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in this Software without prior written +authorization from the copyright holder. + +Acknowledgments: +---------------- +The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh, +and significant help from XeTeX developer Jonathan Kew + +Nota Bene: +---------- +If you include or use a significant part of the synctex package into a software, +I would appreciate to be listed as contributor and see "SyncTeX" highlighted. + +Version 1 +Thu Jun 19 09:39:21 UTC 2008 + +*/ + +#ifndef __SYNCTEX_PARSER__ +# define __SYNCTEX_PARSER__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* synctex_node_t is the type for all synctex nodes. + * The synctex file is parsed into a tree of nodes, either sheet, boxes, math nodes... */ +typedef struct _synctex_node * synctex_node_t; + +/* The main synctex object is a scanner + * Its implementation is considered private. + * The basic workflow is + * - create a "synctex scanner" with the contents of a file + * - perform actions on that scanner like display or edit queries + * - free the scanner when the work is done + */ +typedef struct __synctex_scanner_t _synctex_scanner_t; +typedef _synctex_scanner_t * synctex_scanner_t; + +/* This is the designated method to create a new synctex scanner object. + * output is the pdf/dvi/xdv file associated to the synctex file. + * If necessary, it can be the tex file that originated the synctex file + * but this might cause problems if the \jobname has a custom value. + * Despite this method can accept a relative path in practice, + * you should only pass a full path name. + * The path should be encoded by the underlying file system, + * assuming that it is based on 8 bits characters, including UTF8, + * not 16 bits nor 32 bits. + * The last file extension is removed and replaced by the proper extension. + * Then the private method _synctex_scanner_new_with_contents_of_file is called. + * NULL is returned in case of an error or non existent file. + * Once you have a scanner, use the synctex_display_query and synctex_edit_query below. + * The new "build_directory" argument is available since version 1.5. + * It is the directory where all the auxiliary stuff is created. + * Sometimes, the synctex output file and the pdf, dvi or xdv files are not created in the same directory. + * This is the case in MikTeX (I will include this into TeX Live). + * This directory path can be nil, it will be ignored then. + * It can be either absolute or relative to the directory of the output pdf (dvi or xdv) file. + * If no synctex file is found in the same directory as the output file, then we try to find one in the build directory. + * Please note that this new "build_directory" is provided as a convenient argument but should not be used. + * In fact, this is implempented as a work around of a bug in MikTeX where the synctex file does not follow the pdf file. + * The new "parse" argument is available since version 1.5. In general, use 1. + * Use 0 only if you do not want to parse the content but just check the existence. + */ +synctex_scanner_t synctex_scanner_new_with_output_file(const char * output, const char * build_directory, int parse); + +/* This is the designated method to delete a synctex scanner object. + * Frees all the memory, you must call it when you are finished with the scanner. + */ +void synctex_scanner_free(synctex_scanner_t scanner); + +/* Send this message to force the scanner to parse the contents of the synctex output file. + * Nothing is performed if the file was already parsed. + * In each query below, this message is sent, but if you need to access information more directly, + * you must be sure that the parsing did occur. + * Usage: + * if((my_scanner = synctex_scanner_parse(my_scanner))) { + * continue with my_scanner... + * } else { + * there was a problem + * } + */ +synctex_scanner_t synctex_scanner_parse(synctex_scanner_t scanner); + +/* The main entry points. + * Given the file name, a line and a column number, synctex_display_query returns the number of nodes + * satisfying the contrain. Use code like + * + * if(synctex_display_query(scanner,name,line,column)>0) { + * synctex_node_t node; + * while((node = synctex_next_result(scanner))) { + * // do something with node + * ... + * } + * } + * + * For example, one can + * - highlight each resulting node in the output, using synctex_node_h and synctex_node_v + * - highlight all the rectangles enclosing those nodes, using synctex_box_... functions + * - highlight just the character using that information + * + * Given the page and the position in the page, synctex_edit_query returns the number of nodes + * satisfying the contrain. Use code like + * + * if(synctex_edit_query(scanner,page,h,v)>0) { + * synctex_node_t node; + * while(node = synctex_next_result(scanner)) { + * // do something with node + * ... + * } + * } + * + * For example, one can + * - highlight each resulting line in the input, + * - highlight just the character using that information + * + * page is 1 based + * h and v are coordinates in 72 dpi unit, relative to the top left corner of the page. + * If you make a new query, the result of the previous one is discarded. + * If one of this function returns a non positive integer, + * it means that an error occurred. + * + * Both methods are conservative, in the sense that matching is weak. + * If the exact column number is not found, there will be an answer with the whole line. + * + * Sumatra-PDF, Skim, iTeXMac2 and Texworks are examples of open source software that use this library. + * You can browse their code for a concrete implementation. + */ +int synctex_display_query(synctex_scanner_t scanner,const char * name,int line,int column); +int synctex_edit_query(synctex_scanner_t scanner,int page,float h,float v); +synctex_node_t synctex_next_result(synctex_scanner_t scanner); + +/* Display all the information contained in the scanner object. + * If the records are too numerous, only the first ones are displayed. + * This is mainly for informatinal purpose to help developers. + */ +void synctex_scanner_display(synctex_scanner_t scanner); + +/* The x and y offset of the origin in TeX coordinates. The magnification + These are used by pdf viewers that want to display the real box size. + For example, getting the horizontal coordinates of a node would require + synctex_node_box_h(node)*synctex_scanner_magnification(scanner)+synctex_scanner_x_offset(scanner) + Getting its TeX width would simply require + synctex_node_box_width(node)*synctex_scanner_magnification(scanner) + but direct methods are available for that below. + */ +int synctex_scanner_x_offset(synctex_scanner_t scanner); +int synctex_scanner_y_offset(synctex_scanner_t scanner); +float synctex_scanner_magnification(synctex_scanner_t scanner); + +/* Managing the input file names. + * Given a tag, synctex_scanner_get_name will return the corresponding file name. + * Conversely, given a file name, synctex_scanner_get_tag will retur, the corresponding tag. + * The file name must be the very same as understood by TeX. + * For example, if you \input myDir/foo.tex, the file name is myDir/foo.tex. + * No automatic path expansion is performed. + * Finally, synctex_scanner_input is the first input node of the scanner. + * To browse all the input node, use a loop like + * + * if((input_node = synctex_scanner_input(scanner))){ + * do { + * blah + * } while((input_node=synctex_node_sibling(input_node))); + * } + * + * The output is the name that was used to create the scanner. + * The synctex is the real name of the synctex file, + * it was obtained from output by setting the proper file extension. + */ +const char * synctex_scanner_get_name(synctex_scanner_t scanner,int tag); +int synctex_scanner_get_tag(synctex_scanner_t scanner,const char * name); +synctex_node_t synctex_scanner_input(synctex_scanner_t scanner); +const char * synctex_scanner_get_output(synctex_scanner_t scanner); +const char * synctex_scanner_get_synctex(synctex_scanner_t scanner); + +/* Browsing the nodes + * parent, child and sibling are standard names for tree nodes. + * The parent is one level higher, the child is one level deeper, + * and the sibling is at the same level. + * The sheet of a node is the first ancestor, it is of type sheet. + * A node and its sibling have the same parent. + * A node is the parent of its child. + * A node is either the child of its parent, + * or belongs to the sibling chain of its parent's child. + * The next node is either the child, the sibling or the parent's sibling, + * unless the parent is a sheet. + * This allows to navigate through all the nodes of a given sheet node: + * + * synctex_node_t node = sheet; + * while((node = synctex_node_next(node))) { + * // do something with node + * } + * + * With synctex_sheet_content, you can retrieve the sheet node given the page. + * The page is 1 based, according to TeX standards. + * Conversely synctex_node_sheet allows to retrieve the sheet containing a given node. + */ +synctex_node_t synctex_node_parent(synctex_node_t node); +synctex_node_t synctex_node_sheet(synctex_node_t node); +synctex_node_t synctex_node_child(synctex_node_t node); +synctex_node_t synctex_node_sibling(synctex_node_t node); +synctex_node_t synctex_node_next(synctex_node_t node); +synctex_node_t synctex_sheet(synctex_scanner_t scanner,int page); +synctex_node_t synctex_sheet_content(synctex_scanner_t scanner,int page); + +/* These are the types of the synctex nodes */ +typedef enum { + synctex_node_type_error = 0, + synctex_node_type_input, + synctex_node_type_sheet, + synctex_node_type_vbox, + synctex_node_type_void_vbox, + synctex_node_type_hbox, + synctex_node_type_void_hbox, + synctex_node_type_kern, + synctex_node_type_glue, + synctex_node_type_math, + synctex_node_type_boundary, + synctex_node_number_of_types +} synctex_node_type_t; + +/* synctex_node_type gives the type of a given node, + * synctex_node_isa gives the same information as a human readable text. */ +synctex_node_type_t synctex_node_type(synctex_node_t node); +const char * synctex_node_isa(synctex_node_t node); + +/* This is primarily used for debugging purpose. + * The second one logs information for the node and recursively displays information for its next node */ +void synctex_node_log(synctex_node_t node); +void synctex_node_display(synctex_node_t node); + +/* Given a node, access to the location in the synctex file where it is defined. + */ +typedef unsigned int synctex_charindex_t; +synctex_charindex_t synctex_node_charindex(synctex_node_t node); + +/* Given a node, access to its tag, line and column. + * The line and column numbers are 1 based. + * The latter is not yet fully supported in TeX, the default implementation returns 0 which means the whole line. + * When the tag is known, the scanner of the node will give the corresponding file name. + * When the tag is known, the scanner of the node will give the name. + */ +int synctex_node_tag(synctex_node_t node); +int synctex_node_line(synctex_node_t node); +int synctex_node_column(synctex_node_t node); + +/* In order to enhance forward synchronization, + * non void horizontal boxes have supplemental cached information. + * The mean line is the average of the line numbers of the included nodes. + * The child count is the number of chidren. + */ +int synctex_node_mean_line(synctex_node_t node); +int synctex_node_child_count(synctex_node_t node); + +/* This is the page where the node appears. + * This is a 1 based index as given by TeX. + */ +int synctex_node_page(synctex_node_t node); + +/* For quite all nodes, horizontal, vertical coordinates, and width. + * These are expressed in TeX small points coordinates, with origin at the top left corner. + */ +int synctex_node_h(synctex_node_t node); +int synctex_node_v(synctex_node_t node); +int synctex_node_width(synctex_node_t node); + +/* For all nodes, dimensions of the enclosing box. + * These are expressed in TeX small points coordinates, with origin at the top left corner. + * A box is enclosing itself. + */ +int synctex_node_box_h(synctex_node_t node); +int synctex_node_box_v(synctex_node_t node); +int synctex_node_box_width(synctex_node_t node); +int synctex_node_box_height(synctex_node_t node); +int synctex_node_box_depth(synctex_node_t node); + +/* For quite all nodes, horizontal, vertical coordinates, and width. + * The visible dimensions are bigger than real ones to compensate 0 width boxes + * that do contain nodes. + * These are expressed in page coordinates, with origin at the top left corner. + * A box is enclosing itself. + */ +float synctex_node_visible_h(synctex_node_t node); +float synctex_node_visible_v(synctex_node_t node); +float synctex_node_visible_width(synctex_node_t node); +/* For all nodes, visible dimensions of the enclosing box. + * A box is enclosing itself. + * The visible dimensions are bigger than real ones to compensate 0 width boxes + * that do contain nodes. + */ +float synctex_node_box_visible_h(synctex_node_t node); +float synctex_node_box_visible_v(synctex_node_t node); +float synctex_node_box_visible_width(synctex_node_t node); +float synctex_node_box_visible_height(synctex_node_t node); +float synctex_node_box_visible_depth(synctex_node_t node); + +/* The main synctex updater object. + * This object is used to append information to the synctex file. + * Its implementation is considered private. + * It is used by the synctex command line tool to take into account modifications + * that could occur while postprocessing files by dvipdf like filters. + */ +typedef struct __synctex_updater_t _synctex_updater_t; +typedef _synctex_updater_t * synctex_updater_t; + +/* Designated initializer. + * Once you are done with your whole job, + * free the updater */ +synctex_updater_t synctex_updater_new_with_output_file(const char * output, const char * directory); + +/* Use the next functions to append records to the synctex file, + * no consistency tests made on the arguments */ +void synctex_updater_append_magnification(synctex_updater_t updater, char * magnification); +void synctex_updater_append_x_offset(synctex_updater_t updater, char * x_offset); +void synctex_updater_append_y_offset(synctex_updater_t updater, char * y_offset); + +/* You MUST free the updater, once everything is properly appended */ +void synctex_updater_free(synctex_updater_t updater); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/synctex/synctex_parser_utils.c b/synctex/synctex_parser_utils.c new file mode 100644 index 0000000..9061a2c --- /dev/null +++ b/synctex/synctex_parser_utils.c @@ -0,0 +1,502 @@ +/* +Copyright (c) 2008, 2009, 2010 , 2011 jerome DOT laurens AT u-bourgogne DOT fr + +This file is part of the SyncTeX package. + +Latest Revision: Tue Jun 14 08:23:30 UTC 2011 + +Version: 1.17 + +See synctex_parser_readme.txt for more details + +License: +-------- +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE + +Except as contained in this notice, the name of the copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in this Software without prior written +authorization from the copyright holder. + +*/ + +/* In this file, we find all the functions that may depend on the operating system. */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) +#define SYNCTEX_WINDOWS 1 +#endif + +#if defined(__OS2__) +#define SYNCTEX_OS2 1 +#endif + +#ifdef _WIN32_WINNT_WINXP +#define SYNCTEX_RECENT_WINDOWS 1 +#endif + +#ifdef SYNCTEX_WINDOWS +#include +#endif + +void *_synctex_malloc(size_t size) { + void * ptr = malloc(size); + if(ptr) { +/* There used to be a switch to use bzero because it is more secure. JL */ + memset(ptr,0, size); + } + return (void *)ptr; +} + +int _synctex_error(const char * reason,...) { + va_list arg; + int result; + va_start (arg, reason); +# ifdef SYNCTEX_RECENT_WINDOWS + {/* This code is contributed by William Blum. + As it does not work on some older computers, + the _WIN32 conditional here is replaced with a SYNCTEX_RECENT_WINDOWS one. + According to http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx + Minimum supported client Windows 2000 Professional + Minimum supported server Windows 2000 Server + People running Windows 2K standard edition will not have OutputDebugStringA. + JL.*/ + char *buff; + size_t len; + OutputDebugStringA("SyncTeX ERROR: "); + len = _vscprintf(reason, arg) + 1; + buff = (char*)malloc( len * sizeof(char) ); + result = vsprintf(buff, reason, arg) +strlen("SyncTeX ERROR: "); + OutputDebugStringA(buff); + OutputDebugStringA("\n"); + free(buff); + } +# else + result = fprintf(stderr,"SyncTeX ERROR: "); + result += vfprintf(stderr, reason, arg); + result += fprintf(stderr,"\n"); +# endif + va_end (arg); + return result; +} + +/* strip the last extension of the given string, this string is modified! */ +void _synctex_strip_last_path_extension(char * string) { + if(NULL != string){ + char * last_component = NULL; + char * last_extension = NULL; + char * next = NULL; + /* first we find the last path component */ + if(NULL == (last_component = strstr(string,"/"))){ + last_component = string; + } else { + ++last_component; + while((next = strstr(last_component,"/"))){ + last_component = next+1; + } + } +# if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2) + /* On Windows, the '\' is also a path separator. */ + while((next = strstr(last_component,"\\"))){ + last_component = next+1; + } +# endif + /* then we find the last path extension */ + if((last_extension = strstr(last_component,"."))){ + ++last_extension; + while((next = strstr(last_extension,"."))){ + last_extension = next+1; + } + --last_extension;/* back to the "." */ + if(last_extension>last_component){/* filter out paths like ....my/dir/.hidden"*/ + last_extension[0] = '\0'; + } + } + } +} + +synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char ** name_ref) +{ + if (SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) { + do { + (*name_ref) += 2; + while (SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])) { + ++(*name_ref); + } + } while(SYNCTEX_IS_DOT((*name_ref)[0]) && SYNCTEX_IS_PATH_SEPARATOR((*name_ref)[1])); + return synctex_YES; + } + return synctex_NO; +} + +/* The base name is necessary to deal with the 2011 file naming convention... + * path is a '\0' terminated string + * The return value is the trailing part of the argument, + * just following the first occurrence of the regexp pattern "[^|/|\].[\|/]+".*/ +const char * _synctex_base_name(const char *path) { + const char * ptr = path; + do { + if (synctex_ignore_leading_dot_slash_in_path(&ptr)) { + return ptr; + } + do { + if (!*(++ptr)) { + return path; + } + } while (!SYNCTEX_IS_PATH_SEPARATOR(*ptr)); + } while (*(++ptr)); + return path; +} + +/* Compare two file names, windows is sometimes case insensitive... */ +synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs) { + /* Remove the leading regex '(\./+)*' in both rhs and lhs */ + synctex_ignore_leading_dot_slash_in_path(&lhs); + synctex_ignore_leading_dot_slash_in_path(&rhs); +next_character: + if (SYNCTEX_IS_PATH_SEPARATOR(*lhs)) {/* lhs points to a path separator */ + if (!SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* but not rhs */ + return synctex_NO; + } + ++lhs; + ++rhs; + synctex_ignore_leading_dot_slash_in_path(&lhs); + synctex_ignore_leading_dot_slash_in_path(&rhs); + goto next_character; + } else if (SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */ + return synctex_NO; + } else if (SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(*lhs,*rhs)){/* uppercase do not match */ + return synctex_NO; + } else if (!*lhs) {/* lhs is at the end of the string */ + return *rhs ? synctex_NO : synctex_YES; + } else if(!*rhs) {/* rhs is at the end of the string but not lhs */ + return synctex_NO; + } + ++lhs; + ++rhs; + goto next_character; +} + +synctex_bool_t _synctex_path_is_absolute(const char * name) { + if(!strlen(name)) { + return synctex_NO; + } +# if defined(SYNCTEX_WINDOWS) || defined(SYNCTEX_OS2) + if(strlen(name)>2) { + return (name[1]==':' && SYNCTEX_IS_PATH_SEPARATOR(name[2]))?synctex_YES:synctex_NO; + } + return synctex_NO; +# else + return SYNCTEX_IS_PATH_SEPARATOR(name[0])?synctex_YES:synctex_NO; +# endif +} + +/* We do not take care of UTF-8 */ +const char * _synctex_last_path_component(const char * name) { + const char * c = name+strlen(name); + if(c>name) { + if(!SYNCTEX_IS_PATH_SEPARATOR(*c)) { + do { + --c; + if(SYNCTEX_IS_PATH_SEPARATOR(*c)) { + return c+1; + } + } while(c>name); + } + return c;/* the last path component is the void string*/ + } + return c; +} + +int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size) { + const char * lpc; + if(src && dest_ref) { +# define dest (*dest_ref) + dest = NULL; /* Default behavior: no change and sucess. */ + lpc = _synctex_last_path_component(src); + if(strlen(lpc)) { + if(strchr(lpc,' ') && lpc[0]!='"' && lpc[strlen(lpc)-1]!='"') { + /* We are in the situation where adding the quotes is allowed. */ + /* Time to add the quotes. */ + /* Consistency test: we must have dest+size>dest+strlen(dest)+2 + * or equivalently: strlen(dest)+20) { + char * result = NULL; + ++size; + /* Create the memory storage */ + if(NULL!=(result = (char *)malloc(size))) { + char * dest = result; + va_start (arg, first); + temp = first; + do { + if((size = strlen(temp))>0) { + /* There is something to merge */ + if(dest != strncpy(dest,temp,size)) { + _synctex_error("! _synctex_merge_strings: Copy problem"); + free(result); + result = NULL; + return NULL; + } + dest += size; + } + } while( (temp = va_arg(arg, const char *)) != NULL); + va_end(arg); + dest[0]='\0';/* Terminate the merged string */ + return result; + } + _synctex_error("! _synctex_merge_strings: Memory problem"); + return NULL; + } + return NULL; +} + +/* The purpose of _synctex_get_name is to find the name of the synctex file. + * There is a list of possible filenames from which we return the most recent one and try to remove all the others. + * With two runs of pdftex or xetex we are sure the the synctex file is really the most appropriate. + */ +int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref) +{ + if(output && synctex_name_ref && io_mode_ref) { + /* If output is already absolute, we just have to manage the quotes and the compress mode */ + size_t size = 0; + char * synctex_name = NULL; + synctex_io_mode_t io_mode = *io_mode_ref; + const char * base_name = _synctex_last_path_component(output); /* do not free, output is the owner. base name of output*/ + /* Do we have a real base name ? */ + if(strlen(base_name)>0) { + /* Yes, we do. */ + const char * temp = NULL; + char * core_name = NULL; /* base name of output without path extension. */ + char * dir_name = NULL; /* dir name of output */ + char * quoted_core_name = NULL; + char * basic_name = NULL; + char * gz_name = NULL; + char * quoted_name = NULL; + char * quoted_gz_name = NULL; + char * build_name = NULL; + char * build_gz_name = NULL; + char * build_quoted_name = NULL; + char * build_quoted_gz_name = NULL; + struct stat buf; + time_t the_time = 0; + /* Create core_name: let temp point to the dot before the path extension of base_name; + * We start form the \0 terminating character and scan the string upward until we find a dot. + * The leading dot is not accepted. */ + if((temp = strrchr(base_name,'.')) && (size = temp - base_name)>0) { + /* There is a dot and it is not at the leading position */ + if(NULL == (core_name = (char *)malloc(size+1))) { + _synctex_error("! _synctex_get_name: Memory problem 1"); + return -1; + } + if(core_name != strncpy(core_name,base_name,size)) { + _synctex_error("! _synctex_get_name: Copy problem 1"); + free(core_name); + dir_name = NULL; + return -2; + } + core_name[size] = '\0'; + } else { + /* There is no path extension, + * Just make a copy of base_name */ + core_name = _synctex_merge_strings(base_name); + } + /* core_name is properly set up, owned by "self". */ + /* creating dir_name. */ + size = strlen(output)-strlen(base_name); + if(size>0) { + /* output contains more than one path component */ + if(NULL == (dir_name = (char *)malloc(size+1))) { + _synctex_error("! _synctex_get_name: Memory problem"); + free(core_name); + dir_name = NULL; + return -1; + } + if(dir_name != strncpy(dir_name,output,size)) { + _synctex_error("! _synctex_get_name: Copy problem"); + free(dir_name); + dir_name = NULL; + free(core_name); + dir_name = NULL; + return -2; + } + dir_name[size] = '\0'; + } + /* dir_name is properly set up. It ends with a path separator, if non void. */ + /* creating quoted_core_name. */ + if(strchr(core_name,' ')) { + quoted_core_name = _synctex_merge_strings("\"",core_name,"\""); + } + /* quoted_core_name is properly set up. */ + if(dir_name &&strlen(dir_name)>0) { + basic_name = _synctex_merge_strings(dir_name,core_name,synctex_suffix,NULL); + if(quoted_core_name && strlen(quoted_core_name)>0) { + quoted_name = _synctex_merge_strings(dir_name,quoted_core_name,synctex_suffix,NULL); + } + } else { + basic_name = _synctex_merge_strings(core_name,synctex_suffix,NULL); + if(quoted_core_name && strlen(quoted_core_name)>0) { + quoted_name = _synctex_merge_strings(quoted_core_name,synctex_suffix,NULL); + } + } + if(!_synctex_path_is_absolute(output) && build_directory && (size = strlen(build_directory))) { + temp = build_directory + size - 1; + if(_synctex_path_is_absolute(temp)) { + build_name = _synctex_merge_strings(build_directory,basic_name,NULL); + if(quoted_core_name && strlen(quoted_core_name)>0) { + build_quoted_name = _synctex_merge_strings(build_directory,quoted_name,NULL); + } + } else { + build_name = _synctex_merge_strings(build_directory,"/",basic_name,NULL); + if(quoted_core_name && strlen(quoted_core_name)>0) { + build_quoted_name = _synctex_merge_strings(build_directory,"/",quoted_name,NULL); + } + } + } + if(basic_name) { + gz_name = _synctex_merge_strings(basic_name,synctex_suffix_gz,NULL); + } + if(quoted_name) { + quoted_gz_name = _synctex_merge_strings(quoted_name,synctex_suffix_gz,NULL); + } + if(build_name) { + build_gz_name = _synctex_merge_strings(build_name,synctex_suffix_gz,NULL); + } + if(build_quoted_name) { + build_quoted_gz_name = _synctex_merge_strings(build_quoted_name,synctex_suffix_gz,NULL); + } + /* All the others names are properly set up... */ + /* retain the most recently modified file */ +# define TEST(FILENAME,COMPRESS_MODE) \ + if(FILENAME) {\ + if (stat(FILENAME, &buf)) { \ + free(FILENAME);\ + FILENAME = NULL;\ + } else if (buf.st_mtime>the_time) { \ + the_time=buf.st_mtime; \ + synctex_name = FILENAME; \ + if (COMPRESS_MODE) { \ + io_mode |= synctex_io_gz_mask; \ + } else { \ + io_mode &= ~synctex_io_gz_mask; \ + } \ + } \ + } + TEST(basic_name,synctex_DONT_COMPRESS); + TEST(gz_name,synctex_COMPRESS); + TEST(quoted_name,synctex_DONT_COMPRESS); + TEST(quoted_gz_name,synctex_COMPRESS); + TEST(build_name,synctex_DONT_COMPRESS); + TEST(build_gz_name,synctex_COMPRESS); + TEST(build_quoted_name,synctex_DONT_COMPRESS); + TEST(build_quoted_gz_name,synctex_COMPRESS); +# undef TEST + /* Free all the intermediate filenames, except the one that will be used as returned value. */ +# define CLEAN_AND_REMOVE(FILENAME) \ + if(FILENAME && (FILENAME!=synctex_name)) {\ + remove(FILENAME);\ + printf("synctex tool info: %s removed\n",FILENAME);\ + free(FILENAME);\ + FILENAME = NULL;\ + } + CLEAN_AND_REMOVE(basic_name); + CLEAN_AND_REMOVE(gz_name); + CLEAN_AND_REMOVE(quoted_name); + CLEAN_AND_REMOVE(quoted_gz_name); + CLEAN_AND_REMOVE(build_name); + CLEAN_AND_REMOVE(build_gz_name); + CLEAN_AND_REMOVE(build_quoted_name); + CLEAN_AND_REMOVE(build_quoted_gz_name); +# undef CLEAN_AND_REMOVE + /* set up the returned values */ + * synctex_name_ref = synctex_name; + * io_mode_ref = io_mode; + return 0; + } + return -1;/* bad argument */ + } + return -2; +} + +const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode) { + static const char * synctex_io_modes[4] = {"r","rb","a","ab"}; + unsigned index = ((io_mode & synctex_io_gz_mask)?1:0) + ((io_mode & synctex_io_append_mask)?2:0);// bug pointed out by Jose Alliste + return synctex_io_modes[index]; +} diff --git a/synctex/synctex_parser_utils.h b/synctex/synctex_parser_utils.h new file mode 100644 index 0000000..db19b2d --- /dev/null +++ b/synctex/synctex_parser_utils.h @@ -0,0 +1,152 @@ +/* +Copyright (c) 2008, 2009, 2010, 2011 jerome DOT laurens AT u-bourgogne DOT fr + +This file is part of the SyncTeX package. + +Latest Revision: Tue Jun 14 08:23:30 UTC 2011 + +Version: 1.17 + +See synctex_parser_readme.txt for more details + +License: +-------- +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE + +Except as contained in this notice, the name of the copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in this Software without prior written +authorization from the copyright holder. + +*/ + +/* The utilities declared here are subject to conditional implementation. + * All the operating system special stuff goes here. + * The problem mainly comes from file name management: path separator, encoding... + */ + +# define synctex_bool_t int +# define synctex_YES -1 +# define synctex_ADD_QUOTES -1 +# define synctex_COMPRESS -1 +# define synctex_NO 0 +# define synctex_DONT_ADD_QUOTES 0 +# define synctex_DONT_COMPRESS 0 + +#ifndef __SYNCTEX_PARSER_UTILS__ +# define __SYNCTEX_PARSER_UTILS__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +# if defined(_WIN32) || defined(__OS2__) +# define SYNCTEX_CASE_SENSITIVE_PATH 0 +# define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c || '\\' == c) +# else +# define SYNCTEX_CASE_SENSITIVE_PATH 1 +# define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c) +# endif + +# if defined(_WIN32) || defined(__OS2__) +# define SYNCTEX_IS_DOT(c) ('.' == c) +# else +# define SYNCTEX_IS_DOT(c) ('.' == c) +# endif + +# if SYNCTEX_CASE_SENSITIVE_PATH +# define SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(left,right) (left != right) +# else +# define SYNCTEX_ARE_PATH_CHARACTERS_EQUAL(left,right) (toupper(left) != toupper(right)) +# endif + +/* This custom malloc functions initializes to 0 the newly allocated memory. + * There is no bzero function on windows. */ +void *_synctex_malloc(size_t size); + +/* This is used to log some informational message to the standard error stream. + * On Windows, the stderr stream is not exposed and another method is used. + * The return value is the number of characters printed. */ +int _synctex_error(const char * reason,...); + +/* strip the last extension of the given string, this string is modified! + * This function depends on the OS because the path separator may differ. + * This should be discussed more precisely. */ +void _synctex_strip_last_path_extension(char * string); + +/* Compare two file names, windows is sometimes case insensitive... + * The given strings may differ stricto sensu, but represent the same file name. + * It might not be the real way of doing things. + * The return value is an undefined non 0 value when the two file names are equivalent. + * It is 0 otherwise. */ +synctex_bool_t _synctex_is_equivalent_file_name(const char *lhs, const char *rhs); + +/* Description forthcoming.*/ +synctex_bool_t _synctex_path_is_absolute(const char * name); + +/* Description forthcoming...*/ +const char * _synctex_last_path_component(const char * name); + +/* Description forthcoming...*/ +const char * _synctex_base_name(const char *path); + +/* If the core of the last path component of src is not already enclosed with double quotes ('"') + * and contains a space character (' '), then a new buffer is created, the src is copied and quotes are added. + * In all other cases, no destination buffer is created and the src is not copied. + * 0 on success, which means no error, something non 0 means error, mainly due to memory allocation failure, or bad parameter. + * This is used to fix a bug in the first version of pdftex with synctex (1.40.9) for which names with spaces + * were not managed in a standard way. + * On success, the caller owns the buffer pointed to by dest_ref (is any) and + * is responsible of freeing the memory when done. + * The size argument is the size of the src buffer. On return the dest_ref points to a buffer sized size+2.*/ +int _synctex_copy_with_quoting_last_path_component(const char * src, char ** dest_ref, size_t size); + +/* These are the possible extensions of the synctex file */ +extern const char * synctex_suffix; +extern const char * synctex_suffix_gz; + +typedef unsigned int synctex_io_mode_t; + +typedef enum { + synctex_io_append_mask = 1, + synctex_io_gz_mask = synctex_io_append_mask<<1 +} synctex_io_mode_masks_t; + +typedef enum { + synctex_compress_mode_none = 0, + synctex_compress_mode_gz = 1 +} synctex_compress_mode_t; + +int _synctex_get_name(const char * output, const char * build_directory, char ** synctex_name_ref, synctex_io_mode_t * io_mode_ref); + +/* returns the correct mode required by fopen and gzopen from the given io_mode */ +const char * _synctex_get_io_mode_name(synctex_io_mode_t io_mode); + +synctex_bool_t synctex_ignore_leading_dot_slash_in_path(const char ** name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/synctex/synctex_parser_version.txt b/synctex/synctex_parser_version.txt new file mode 100644 index 0000000..68209c0 --- /dev/null +++ b/synctex/synctex_parser_version.txt @@ -0,0 +1 @@ +1.17 \ No newline at end of file