Merge branch 'develop' of pwmt.org:zathura into develop

This commit is contained in:
Sebastian Ramacher 2014-06-24 12:08:32 +02:00
commit 2f53f09e7d
16 changed files with 5649 additions and 270 deletions

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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

18
main.c
View file

@ -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;
}

300
synctex.c
View file

@ -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 {

View file

@ -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

27
synctex/LICENSE Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

360
synctex/synctex_parser.h Normal file
View 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

View 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];
}

View 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

View file

@ -0,0 +1 @@
1.17