mirror of
https://git.pwmt.org/pwmt/zathura.git
synced 2024-12-29 15:56:00 +01:00
Merge branch 'develop' of pwmt.org:zathura into develop
This commit is contained in:
commit
2f53f09e7d
16 changed files with 5649 additions and 270 deletions
13
Makefile
13
Makefile
|
@ -4,8 +4,8 @@ include config.mk
|
||||||
include colors.mk
|
include colors.mk
|
||||||
include common.mk
|
include common.mk
|
||||||
|
|
||||||
OSOURCE = $(filter-out css-definitions.c, $(filter-out dbus-interface-definitions.c, $(wildcard *.c)))
|
OSOURCE = $(filter-out css-definitions.c, $(filter-out dbus-interface-definitions.c, $(wildcard *.c))) $(wildcard synctex/*.c)
|
||||||
HEADER = $(wildcard *.h)
|
HEADER = $(wildcard *.h) $(wildcard synctex/*.h)
|
||||||
HEADERINST = version.h document.h macros.h page.h types.h plugin-api.h links.h
|
HEADERINST = version.h document.h macros.h page.h types.h plugin-api.h links.h
|
||||||
|
|
||||||
ifneq (${WITH_SQLITE},0)
|
ifneq (${WITH_SQLITE},0)
|
||||||
|
@ -36,6 +36,13 @@ endif
|
||||||
ifeq (,$(findstring -DLOCALEDIR,${CPPFLAGS}))
|
ifeq (,$(findstring -DLOCALEDIR,${CPPFLAGS}))
|
||||||
CPPFLAGS += -DLOCALEDIR=\"${LOCALEDIR}\"
|
CPPFLAGS += -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||||
endif
|
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
|
OBJECTS = $(patsubst %.c, %.o, $(SOURCE)) dbus-interface-definitions.o css-definitions.o
|
||||||
DOBJECTS = $(patsubst %.o, %.do, $(OBJECTS))
|
DOBJECTS = $(patsubst %.o, %.do, $(OBJECTS))
|
||||||
|
@ -83,7 +90,7 @@ css-definitions.c: data/zathura.css_t
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(call colorecho,CC,$<)
|
$(call colorecho,CC,$<)
|
||||||
$(QUIET) mkdir -p .depend
|
$(QUIET) mkdir -p $(shell dirname .depend/$@.dep)
|
||||||
$(QUIET)${CC} -c ${CPPFLAGS} ${CFLAGS} -o $@ $< -MMD -MF .depend/$@.dep
|
$(QUIET)${CC} -c ${CPPFLAGS} ${CFLAGS} -o $@ $< -MMD -MF .depend/$@.dep
|
||||||
|
|
||||||
%.do: %.c
|
%.do: %.c
|
||||||
|
|
12
adjustment.c
12
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 c0 = zathura_document_get_first_page_column(document);
|
||||||
unsigned int npag = zathura_document_get_number_of_pages(document);
|
unsigned int npag = zathura_document_get_number_of_pages(document);
|
||||||
unsigned int ncol = zathura_document_get_pages_per_row(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);
|
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 col = floor(pos_x * (double)doc_width / (double)(cell_width + pad));
|
||||||
unsigned int row = floor(pos_y * (double)doc_height / (double)(cell_height + 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) {
|
if (page < c0 - 1) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return page - (c0 - 1);
|
return MIN(page - (c0 - 1), npag - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
76
callbacks.c
76
callbacks.c
|
@ -137,32 +137,45 @@ cb_view_vadjustment_value_changed(GtkAdjustment* adjustment, gpointer data)
|
||||||
statusbar_page_number_update(zathura);
|
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
|
void
|
||||||
cb_view_hadjustment_changed(GtkAdjustment* adjustment, gpointer data)
|
cb_view_hadjustment_changed(GtkAdjustment* adjustment, gpointer data)
|
||||||
{
|
{
|
||||||
zathura_t* zathura = data;
|
zathura_t* zathura = data;
|
||||||
g_return_if_fail(zathura != NULL);
|
g_return_if_fail(zathura != NULL);
|
||||||
|
|
||||||
zathura_adjust_mode_t adjust_mode =
|
cb_view_adjustment_changed(adjustment, zathura, true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -171,26 +184,7 @@ cb_view_vadjustment_changed(GtkAdjustment* adjustment, gpointer data)
|
||||||
zathura_t* zathura = data;
|
zathura_t* zathura = data;
|
||||||
g_return_if_fail(zathura != NULL);
|
g_return_if_fail(zathura != NULL);
|
||||||
|
|
||||||
zathura_adjust_mode_t adjust_mode =
|
cb_view_adjustment_changed(adjustment, zathura, false);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -233,7 +227,7 @@ cb_page_layout_value_changed(girara_session_t* session, const char* name, girara
|
||||||
if (pages_per_row == 0) {
|
if (pages_per_row == 0) {
|
||||||
pages_per_row = 1;
|
pages_per_row = 1;
|
||||||
girara_setting_set(session, name, &pages_per_row);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,11 @@ MAGIC_INC ?=
|
||||||
MAGIC_LIB ?= -lmagic
|
MAGIC_LIB ?= -lmagic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
INCS = ${GIRARA_INC} ${GTK_INC} ${GTHREAD_INC} ${GMODULE_INC} ${GLIB_INC}
|
ZLIB_INC ?= $(shell pkg-config --cflags zlib)
|
||||||
LIBS = ${GIRARA_LIB} ${GTK_LIB} ${GTHREAD_LIB} ${GMODULE_LIB} ${GLIB_LIB} -lpthread -lm
|
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
|
# flags
|
||||||
CFLAGS += -std=c99 -pedantic -Wall -Wno-format-zero-length -Wextra $(INCS)
|
CFLAGS += -std=c99 -pedantic -Wall -Wno-format-zero-length -Wextra $(INCS)
|
||||||
|
|
|
@ -545,17 +545,17 @@ zathura_dbus_goto_page_and_highlight(const char* filename, unsigned int page,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
zathura_dbus_synctex_position(const char* filename, const char* position,
|
zathura_dbus_synctex_position(const char* filename, const char* input_file,
|
||||||
pid_t hint)
|
int line, int column, pid_t hint)
|
||||||
{
|
{
|
||||||
if (filename == NULL || position == NULL) {
|
if (filename == NULL || input_file == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int page = 0;
|
unsigned int page = 0;
|
||||||
girara_list_t* secondary_rects = NULL;
|
girara_list_t* secondary_rects = NULL;
|
||||||
girara_list_t* rectangles = synctex_rectangles_from_position(
|
girara_list_t* rectangles = synctex_rectangles_from_position(
|
||||||
filename, position, &page, &secondary_rects);
|
filename, input_file, line, column, &page, &secondary_rects);
|
||||||
if (rectangles == NULL) {
|
if (rectangles == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
unsigned int page, girara_list_t* rectangles, girara_list_t* secondary_rects,
|
||||||
pid_t pidhint);
|
pid_t pidhint);
|
||||||
|
|
||||||
bool zathura_dbus_synctex_position(const char* filename, const char* position,
|
bool zathura_dbus_synctex_position(const char* filename, const char* input_file,
|
||||||
pid_t pidhint);
|
int line, int column, pid_t hint);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
18
main.c
18
main.c
|
@ -106,11 +106,25 @@ main(int argc, char* argv[])
|
||||||
return -1;
|
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);
|
free(real_path);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} 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);
|
free(real_path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
300
synctex.c
300
synctex.c
|
@ -8,49 +8,12 @@
|
||||||
#include "page.h"
|
#include "page.h"
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "synctex/synctex_parser.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,
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
void
|
||||||
synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,212 +27,123 @@ synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char** argv = g_try_malloc0(sizeof(char*) * (zathura->synctex.editor != NULL ?
|
synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1);
|
||||||
7 : 5));
|
if (scanner == NULL) {
|
||||||
if (argv == NULL) {
|
girara_debug("Failed to create synctex scanner.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
argv[0] = g_strdup("synctex");
|
synctex_scanner_t temp = synctex_scanner_parse(scanner);
|
||||||
argv[1] = g_strdup("edit");
|
if (temp == NULL) {
|
||||||
argv[2] = g_strdup("-o");
|
girara_debug("Failed to parse synctex file.");
|
||||||
argv[3] = g_strdup_printf("%d:%d:%d:%s", zathura_page_get_index(page) + 1, x,
|
synctex_scanner_free(scanner);
|
||||||
y, filename);
|
return;
|
||||||
if (zathura->synctex.editor != NULL) {
|
|
||||||
argv[4] = g_strdup("-x");
|
|
||||||
argv[5] = g_strdup(zathura->synctex.editor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
|
if (synctex_edit_query(scanner, zathura_page_get_index(page) + 1, x, y) > 0) {
|
||||||
g_strfreev(argv);
|
/* 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
|
char* linestr = g_strdup_printf("%d", line);
|
||||||
scan_float(GScanner* scanner)
|
char* columnstr = g_strdup_printf("%d", column);
|
||||||
{
|
|
||||||
switch (g_scanner_get_next_token(scanner)) {
|
gchar** argv = NULL;
|
||||||
case G_TOKEN_FLOAT:
|
gint argc = 0;
|
||||||
return g_scanner_cur_value(scanner).v_float;
|
if (g_shell_parse_argv(zathura->synctex.editor, &argc, &argv, NULL) == TRUE) {
|
||||||
case G_TOKEN_INT:
|
for (gint i = 0; i != argc; ++i) {
|
||||||
return g_scanner_cur_value(scanner).v_int;
|
char* temp = girara_replace_substring(argv[i], "%{line}", linestr);
|
||||||
default:
|
g_free(argv[i]);
|
||||||
return 0.0;
|
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*
|
girara_list_t*
|
||||||
synctex_rectangles_from_position(const char* filename, const char* position,
|
synctex_rectangles_from_position(const char* filename, const char* input_file,
|
||||||
unsigned int* page,
|
int line, int column, unsigned int* page,
|
||||||
girara_list_t** secondary_rects)
|
girara_list_t** secondary_rects)
|
||||||
{
|
{
|
||||||
if (filename == NULL || position == NULL || page == NULL) {
|
if (filename == NULL || input_file == NULL || page == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char** argv = g_try_malloc0(sizeof(char*) * 7);
|
synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1);
|
||||||
if (argv == NULL) {
|
if (scanner == NULL) {
|
||||||
|
girara_debug("Failed to create synctex scanner.");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
argv[0] = g_strdup("synctex");
|
synctex_scanner_t temp = synctex_scanner_parse(scanner);
|
||||||
argv[1] = g_strdup("view");
|
if (temp == NULL) {
|
||||||
argv[2] = g_strdup("-i");
|
girara_debug("Failed to parse synctex file.");
|
||||||
argv[3] = g_strdup(position);
|
synctex_scanner_free(scanner);
|
||||||
argv[4] = g_strdup("-o");
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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* hitlist = girara_list_new2(g_free);
|
||||||
girara_list_t* other_rects = 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) {
|
if (synctex_display_query(scanner, input_file, line, column) > 0) {
|
||||||
switch (g_scanner_get_next_token(scanner)) {
|
synctex_node_t node = NULL;
|
||||||
case G_TOKEN_EOF:
|
bool got_page = false;
|
||||||
found_end = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case G_TOKEN_SYMBOL:
|
while ((node = synctex_next_result (scanner)) != NULL) {
|
||||||
switch (GPOINTER_TO_INT(g_scanner_cur_value(scanner).v_identifier)) {
|
const unsigned int current_page = synctex_node_page(node) - 1;
|
||||||
case SYNCTEX_RESULT_END:
|
if (got_page == false) {
|
||||||
found_end = true;
|
got_page = true;
|
||||||
break;
|
*page = current_page;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t));
|
zathura_rectangle_t rect = { 0, 0, 0, 0 };
|
||||||
if (page_rect != NULL) {
|
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->page = current_page;
|
||||||
page_rect->rect = rectangle;
|
page_rect->rect = rect;
|
||||||
|
|
||||||
girara_list_append(other_rects, page_rect);
|
girara_list_append(other_rects, page_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_scanner_destroy(scanner);
|
synctex_scanner_free(scanner);
|
||||||
close(output);
|
|
||||||
|
|
||||||
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) {
|
if (secondary_rects != NULL) {
|
||||||
*secondary_rects = other_rects;
|
*secondary_rects = other_rects;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,6 +12,7 @@ typedef struct synctex_page_rect_s {
|
||||||
|
|
||||||
void synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y);
|
void synctex_edit(zathura_t* zathura, zathura_page_t* page, int x, int y);
|
||||||
girara_list_t* synctex_rectangles_from_position(const char* filename,
|
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
|
#endif
|
||||||
|
|
27
synctex/LICENSE
Normal file
27
synctex/LICENSE
Normal file
|
@ -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.
|
7
synctex/Makefile
Normal file
7
synctex/Makefile
Normal file
|
@ -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
|
4429
synctex/synctex_parser.c
Normal file
4429
synctex/synctex_parser.c
Normal file
File diff suppressed because it is too large
Load diff
360
synctex/synctex_parser.h
Normal file
360
synctex/synctex_parser.h
Normal file
|
@ -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
|
502
synctex/synctex_parser_utils.c
Normal file
502
synctex/synctex_parser_utils.c
Normal file
|
@ -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 <synctex_parser_utils.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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 <windows.h>
|
||||||
|
#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)+2<size (see below) */
|
||||||
|
if(strlen(src)<size) {
|
||||||
|
if((dest = (char *)malloc(size+2))) {
|
||||||
|
char * dpc = dest + (lpc-src); /* dpc is the last path component of dest. */
|
||||||
|
if(dest != strncpy(dest,src,size)) {
|
||||||
|
_synctex_error("! _synctex_copy_with_quoting_last_path_component: Copy problem");
|
||||||
|
free(dest);
|
||||||
|
dest = NULL;/* Don't forget to reinitialize. */
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
memmove(dpc+1,dpc,strlen(dpc)+1); /* Also move the null terminating character. */
|
||||||
|
dpc[0]='"';
|
||||||
|
dpc[strlen(dpc)+1]='\0';/* Consistency test */
|
||||||
|
dpc[strlen(dpc)]='"';
|
||||||
|
return 0; /* Success. */
|
||||||
|
}
|
||||||
|
return -1; /* Memory allocation error. */
|
||||||
|
}
|
||||||
|
_synctex_error("! _synctex_copy_with_quoting_last_path_component: Internal inconsistency");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
return 0; /* Success. */
|
||||||
|
}
|
||||||
|
return 0; /* No last path component. */
|
||||||
|
# undef dest
|
||||||
|
}
|
||||||
|
return 1; /* Bad parameter, this value is subject to changes. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The client is responsible of the management of the returned string, if any. */
|
||||||
|
char * _synctex_merge_strings(const char * first,...);
|
||||||
|
|
||||||
|
char * _synctex_merge_strings(const char * first,...) {
|
||||||
|
va_list arg;
|
||||||
|
size_t size = 0;
|
||||||
|
const char * temp;
|
||||||
|
/* First retrieve the size necessary to store the merged string */
|
||||||
|
va_start (arg, first);
|
||||||
|
temp = first;
|
||||||
|
do {
|
||||||
|
size_t len = strlen(temp);
|
||||||
|
if(UINT_MAX-len<size) {
|
||||||
|
_synctex_error("! _synctex_merge_strings: Capacity exceeded.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size+=len;
|
||||||
|
} while( (temp = va_arg(arg, const char *)) != NULL);
|
||||||
|
va_end(arg);
|
||||||
|
if(size>0) {
|
||||||
|
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];
|
||||||
|
}
|
152
synctex/synctex_parser_utils.h
Normal file
152
synctex/synctex_parser_utils.h
Normal file
|
@ -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 <stdlib.h>
|
||||||
|
|
||||||
|
#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
|
1
synctex/synctex_parser_version.txt
Normal file
1
synctex/synctex_parser_version.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
1.17
|
Loading…
Reference in a new issue