mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #4893 from overleaf/em-synctex
Use the synctex distributed with TeX Live GitOrigin-RevId: 5a133f21f48fd1e217ab463e8cb2a5cdec8be1af
This commit is contained in:
parent
fa2567aa61
commit
9ee92daea3
27 changed files with 380 additions and 5667 deletions
|
@ -81,7 +81,6 @@ services:
|
||||||
|
|
||||||
# SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'
|
# SANDBOXED_COMPILES_SIBLING_CONTAINERS: 'true'
|
||||||
# SANDBOXED_COMPILES_HOST_DIR: '/var/sharelatex_data/data/compiles'
|
# SANDBOXED_COMPILES_HOST_DIR: '/var/sharelatex_data/data/compiles'
|
||||||
# SYNCTEX_BIN_HOST_PATH: '/var/sharelatex_data/bin/synctex'
|
|
||||||
|
|
||||||
# DOCKER_RUNNER: 'false'
|
# DOCKER_RUNNER: 'false'
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,6 @@ RUN node genScript install | bash
|
||||||
# --------------------
|
# --------------------
|
||||||
RUN node genScript compile | bash
|
RUN node genScript compile | bash
|
||||||
|
|
||||||
# Links CLSI synctex to its default location
|
|
||||||
# ------------------------------------------
|
|
||||||
RUN ln -s /var/www/sharelatex/clsi/bin/synctex /opt/synctex
|
|
||||||
|
|
||||||
|
|
||||||
# Copy runit service startup scripts to its location
|
# Copy runit service startup scripts to its location
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
ADD server-ce/runit /etc/service
|
ADD server-ce/runit /etc/service
|
||||||
|
|
|
@ -61,6 +61,7 @@ RUN mkdir /install-tl-unx \
|
||||||
&& tlmgr install --repository ${TEXLIVE_MIRROR} \
|
&& tlmgr install --repository ${TEXLIVE_MIRROR} \
|
||||||
latexmk \
|
latexmk \
|
||||||
texcount \
|
texcount \
|
||||||
|
synctex \
|
||||||
\
|
\
|
||||||
&& rm -rf /install-tl-unx
|
&& rm -rf /install-tl-unx
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,4 @@ if [ -e '/var/run/docker.sock' ]; then
|
||||||
usermod -aG dockeronhost www-data
|
usermod -aG dockeronhost www-data
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copies over CSLI synctex to the host mounted volume, so it
|
|
||||||
# can be subsequently mounted in TexLive containers on Sandbox Compilation
|
|
||||||
SYNCTEX=/var/lib/sharelatex/bin/synctex
|
|
||||||
if [ ! -f "$SYNCTEX" ]; then
|
|
||||||
if [ "$DISABLE_SYNCTEX_BINARY_COPY" == "true" ]; then
|
|
||||||
echo ">> Copy of synctex executable disabled by DISABLE_SYNCTEX_BINARY_COPY flag, feature may not work"
|
|
||||||
else
|
|
||||||
echo ">> Copying synctex executable to the host"
|
|
||||||
mkdir -p $(dirname $SYNCTEX )
|
|
||||||
cp /var/www/sharelatex/clsi/bin/synctex $SYNCTEX
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec /sbin/setuser www-data /usr/bin/node $NODE_PARAMS /var/www/sharelatex/clsi/app.js >> /var/log/sharelatex/clsi.log 2>&1
|
exec /sbin/setuser www-data /usr/bin/node $NODE_PARAMS /var/www/sharelatex/clsi/app.js >> /var/log/sharelatex/clsi.log 2>&1
|
||||||
|
|
|
@ -31,7 +31,6 @@ The CLSI can be configured through the following environment variables:
|
||||||
* `SENTRY_DSN` - Sentry [Data Source Name](https://docs.sentry.io/product/sentry-basics/dsn-explainer/)
|
* `SENTRY_DSN` - Sentry [Data Source Name](https://docs.sentry.io/product/sentry-basics/dsn-explainer/)
|
||||||
* `SMOKE_TEST` - Whether to run smoke tests
|
* `SMOKE_TEST` - Whether to run smoke tests
|
||||||
* `SQLITE_PATH` - Path to SQLite database
|
* `SQLITE_PATH` - Path to SQLite database
|
||||||
* `SYNCTEX_BIN_HOST_PATH` - Path to SyncTeX binary
|
|
||||||
* `TEXLIVE_IMAGE` - The TeX Live Docker image to use for sibling containers, e.g. `gcr.io/overleaf-ops/texlive-full:2017.1`
|
* `TEXLIVE_IMAGE` - The TeX Live Docker image to use for sibling containers, e.g. `gcr.io/overleaf-ops/texlive-full:2017.1`
|
||||||
* `TEX_LIVE_IMAGE_NAME_OVERRIDE` - The name of the registry for the Docker image e.g. `gcr.io/overleaf-ops`
|
* `TEX_LIVE_IMAGE_NAME_OVERRIDE` - The name of the registry for the Docker image e.g. `gcr.io/overleaf-ops`
|
||||||
* `TEXLIVE_IMAGE_USER` - When using sibling containers, the user to run as in the TeX Live image. Defaults to `tex`
|
* `TEXLIVE_IMAGE_USER` - When using sibling containers, the user to run as in the TeX Live image. Defaults to `tex`
|
||||||
|
|
|
@ -17,6 +17,7 @@ const async = require('async')
|
||||||
const Errors = require('./Errors')
|
const Errors = require('./Errors')
|
||||||
const CommandRunner = require('./CommandRunner')
|
const CommandRunner = require('./CommandRunner')
|
||||||
const { emitPdfStats } = require('./ContentCacheMetrics')
|
const { emitPdfStats } = require('./ContentCacheMetrics')
|
||||||
|
const SynctexOutputParser = require('./SynctexOutputParser')
|
||||||
|
|
||||||
function getCompileName(projectId, userId) {
|
function getCompileName(projectId, userId) {
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
|
@ -428,32 +429,44 @@ function syncFromCode(
|
||||||
// wherever it is on the host.
|
// wherever it is on the host.
|
||||||
const compileName = getCompileName(projectId, userId)
|
const compileName = getCompileName(projectId, userId)
|
||||||
const baseDir = Settings.path.synctexBaseDir(compileName)
|
const baseDir = Settings.path.synctexBaseDir(compileName)
|
||||||
const filePath = baseDir + '/' + filename
|
const inputFilePath = Path.join(baseDir, filename)
|
||||||
const synctexPath = `${baseDir}/output.pdf`
|
const outputFilePath = Path.join(baseDir, 'output.pdf')
|
||||||
const command = ['code', synctexPath, filePath, line, column]
|
const command = [
|
||||||
|
'synctex',
|
||||||
|
'view',
|
||||||
|
'-i',
|
||||||
|
`${line}:${column}:${inputFilePath}`,
|
||||||
|
'-o',
|
||||||
|
outputFilePath,
|
||||||
|
]
|
||||||
_runSynctex(projectId, userId, command, imageName, (error, stdout) => {
|
_runSynctex(projectId, userId, command, imageName, (error, stdout) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
logger.log(
|
logger.debug(
|
||||||
{ projectId, userId, filename, line, column, command, stdout },
|
{ projectId, userId, filename, line, column, command, stdout },
|
||||||
'synctex code output'
|
'synctex code output'
|
||||||
)
|
)
|
||||||
callback(null, _parseSynctexFromCodeOutput(stdout))
|
callback(null, SynctexOutputParser.parseViewOutput(stdout))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncFromPdf(projectId, userId, page, h, v, imageName, callback) {
|
function syncFromPdf(projectId, userId, page, h, v, imageName, callback) {
|
||||||
const compileName = getCompileName(projectId, userId)
|
const compileName = getCompileName(projectId, userId)
|
||||||
const baseDir = Settings.path.synctexBaseDir(compileName)
|
const baseDir = Settings.path.synctexBaseDir(compileName)
|
||||||
const synctexPath = `${baseDir}/output.pdf`
|
const outputFilePath = `${baseDir}/output.pdf`
|
||||||
const command = ['pdf', synctexPath, page, h, v]
|
const command = [
|
||||||
|
'synctex',
|
||||||
|
'edit',
|
||||||
|
'-o',
|
||||||
|
`${page}:${h}:${v}:${outputFilePath}`,
|
||||||
|
]
|
||||||
_runSynctex(projectId, userId, command, imageName, (error, stdout) => {
|
_runSynctex(projectId, userId, command, imageName, (error, stdout) => {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
}
|
}
|
||||||
logger.log({ projectId, userId, page, h, v, stdout }, 'synctex pdf output')
|
logger.log({ projectId, userId, page, h, v, stdout }, 'synctex pdf output')
|
||||||
callback(null, _parseSynctexFromPdfOutput(stdout, baseDir))
|
callback(null, SynctexOutputParser.parseEditOutput(stdout, baseDir))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,12 +495,12 @@ function _checkFileExists(dir, filename, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _runSynctex(projectId, userId, command, imageName, callback) {
|
function _runSynctex(projectId, userId, command, imageName, callback) {
|
||||||
command.unshift('/opt/synctex')
|
|
||||||
|
|
||||||
const directory = getCompileDir(projectId, userId)
|
const directory = getCompileDir(projectId, userId)
|
||||||
const timeout = 60 * 1000 // increased to allow for large projects
|
const timeout = 60 * 1000 // increased to allow for large projects
|
||||||
const compileName = getCompileName(projectId, userId)
|
const compileName = getCompileName(projectId, userId)
|
||||||
const compileGroup = 'synctex'
|
const compileGroup = 'synctex'
|
||||||
|
const defaultImageName =
|
||||||
|
Settings.clsi && Settings.clsi.docker && Settings.clsi.docker.image
|
||||||
_checkFileExists(directory, 'output.synctex.gz', error => {
|
_checkFileExists(directory, 'output.synctex.gz', error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return callback(error)
|
return callback(error)
|
||||||
|
@ -496,10 +509,7 @@ function _runSynctex(projectId, userId, command, imageName, callback) {
|
||||||
compileName,
|
compileName,
|
||||||
command,
|
command,
|
||||||
directory,
|
directory,
|
||||||
imageName ||
|
imageName || defaultImageName,
|
||||||
(Settings.clsi && Settings.clsi.docker
|
|
||||||
? Settings.clsi.docker.image
|
|
||||||
: undefined),
|
|
||||||
timeout,
|
timeout,
|
||||||
{},
|
{},
|
||||||
compileGroup,
|
compileGroup,
|
||||||
|
@ -517,39 +527,6 @@ function _runSynctex(projectId, userId, command, imageName, callback) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseSynctexFromCodeOutput(output) {
|
|
||||||
const results = []
|
|
||||||
for (const line of output.split('\n')) {
|
|
||||||
const [node, page, h, v, width, height] = line.split('\t')
|
|
||||||
if (node === 'NODE') {
|
|
||||||
results.push({
|
|
||||||
page: parseInt(page, 10),
|
|
||||||
h: parseFloat(h),
|
|
||||||
v: parseFloat(v),
|
|
||||||
height: parseFloat(height),
|
|
||||||
width: parseFloat(width),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
function _parseSynctexFromPdfOutput(output, baseDir) {
|
|
||||||
const results = []
|
|
||||||
for (const line of output.split('\n')) {
|
|
||||||
const [node, filePath, lineNum, column] = line.split('\t')
|
|
||||||
if (node === 'NODE') {
|
|
||||||
const file = filePath.slice(baseDir.length + 1)
|
|
||||||
results.push({
|
|
||||||
file,
|
|
||||||
line: parseInt(lineNum, 10),
|
|
||||||
column: parseInt(column, 10),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
function wordcount(projectId, userId, filename, image, callback) {
|
function wordcount(projectId, userId, filename, image, callback) {
|
||||||
logger.log({ projectId, userId, filename, image }, 'running wordcount')
|
logger.log({ projectId, userId, filename, image }, 'running wordcount')
|
||||||
const filePath = `$COMPILE_DIR/${filename}`
|
const filePath = `$COMPILE_DIR/${filename}`
|
||||||
|
|
|
@ -258,12 +258,6 @@ const DockerRunner = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.path != null && Settings.path.synctexBinHostPath != null) {
|
|
||||||
options.HostConfig.Binds.push(
|
|
||||||
`${Settings.path.synctexBinHostPath}:/opt/synctex:ro`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.clsi.docker.seccomp_profile != null) {
|
if (Settings.clsi.docker.seccomp_profile != null) {
|
||||||
options.HostConfig.SecurityOpt.push(
|
options.HostConfig.SecurityOpt.push(
|
||||||
`seccomp=${Settings.clsi.docker.seccomp_profile}`
|
`seccomp=${Settings.clsi.docker.seccomp_profile}`
|
||||||
|
|
113
services/clsi/app/js/SynctexOutputParser.js
Normal file
113
services/clsi/app/js/SynctexOutputParser.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
const Path = require('path')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse output from the `synctex view` command
|
||||||
|
*/
|
||||||
|
function parseViewOutput(output) {
|
||||||
|
return _parseOutput(output, (record, label, value) => {
|
||||||
|
switch (label) {
|
||||||
|
case 'Page':
|
||||||
|
_setIntProp(record, 'page', value)
|
||||||
|
break
|
||||||
|
case 'h':
|
||||||
|
_setFloatProp(record, 'h', value)
|
||||||
|
break
|
||||||
|
case 'v':
|
||||||
|
_setFloatProp(record, 'v', value)
|
||||||
|
break
|
||||||
|
case 'W':
|
||||||
|
_setFloatProp(record, 'width', value)
|
||||||
|
break
|
||||||
|
case 'H':
|
||||||
|
_setFloatProp(record, 'height', value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse output from the `synctex edit` command
|
||||||
|
*/
|
||||||
|
function parseEditOutput(output, baseDir) {
|
||||||
|
return _parseOutput(output, (record, label, value) => {
|
||||||
|
switch (label) {
|
||||||
|
case 'Input':
|
||||||
|
if (Path.isAbsolute(value)) {
|
||||||
|
record.file = Path.relative(baseDir, value)
|
||||||
|
} else {
|
||||||
|
record.file = value
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'Line':
|
||||||
|
_setIntProp(record, 'line', value)
|
||||||
|
break
|
||||||
|
case 'Column':
|
||||||
|
_setIntProp(record, 'column', value)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic parser for synctex output
|
||||||
|
*
|
||||||
|
* Parses the output into records. Each line is split into a label and a value,
|
||||||
|
* which are then sent to `processLine` for further processing.
|
||||||
|
*/
|
||||||
|
function _parseOutput(output, processLine) {
|
||||||
|
const lines = output.split('\n')
|
||||||
|
let currentRecord = null
|
||||||
|
const records = []
|
||||||
|
for (const line of lines) {
|
||||||
|
const [label, value] = _splitLine(line)
|
||||||
|
|
||||||
|
// A line that starts with 'Output:' indicates a new record
|
||||||
|
if (label === 'Output') {
|
||||||
|
// Start new record
|
||||||
|
currentRecord = {}
|
||||||
|
records.push(currentRecord)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the line if we're not in a record yet
|
||||||
|
if (currentRecord == null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the line
|
||||||
|
processLine(currentRecord, label, value)
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a line in label and value components.
|
||||||
|
*
|
||||||
|
* The components are separated by a colon. Note that this is slightly
|
||||||
|
* different from `line.split(':', 2)`. This version puts the entirety of the
|
||||||
|
* line after the colon in the value component, even if there are more colons
|
||||||
|
* on the line.
|
||||||
|
*/
|
||||||
|
function _splitLine(line) {
|
||||||
|
const splitIndex = line.indexOf(':')
|
||||||
|
if (splitIndex === -1) {
|
||||||
|
return ['', line]
|
||||||
|
}
|
||||||
|
return [line.slice(0, splitIndex).trim(), line.slice(splitIndex + 1).trim()]
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setIntProp(record, prop, value) {
|
||||||
|
const intValue = parseInt(value, 10)
|
||||||
|
if (!isNaN(intValue)) {
|
||||||
|
record[prop] = intValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setFloatProp(record, prop, value) {
|
||||||
|
const floatValue = parseFloat(value)
|
||||||
|
if (!isNaN(floatValue)) {
|
||||||
|
record[prop] = floatValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { parseViewOutput, parseEditOutput }
|
Binary file not shown.
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
echo "hello world"
|
|
||||||
sleep 3
|
|
||||||
echo "awake"
|
|
||||||
/opt/synctex pdf /compile/output.pdf 1 100 200
|
|
|
@ -9,12 +9,10 @@ services:
|
||||||
SHARELATEX_CONFIG: /app/config/settings.defaults.js
|
SHARELATEX_CONFIG: /app/config/settings.defaults.js
|
||||||
DOCKER_RUNNER: "true"
|
DOCKER_RUNNER: "true"
|
||||||
COMPILES_HOST_DIR: $PWD/compiles
|
COMPILES_HOST_DIR: $PWD/compiles
|
||||||
SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- ./compiles:/app/compiles
|
- ./compiles:/app/compiles
|
||||||
- ./cache:/app/cache
|
- ./cache:/app/cache
|
||||||
- ./bin/synctex:/app/bin/synctex
|
|
||||||
|
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
|
@ -25,10 +23,8 @@ services:
|
||||||
SHARELATEX_CONFIG: /app/config/settings.defaults.js
|
SHARELATEX_CONFIG: /app/config/settings.defaults.js
|
||||||
DOCKER_RUNNER: "true"
|
DOCKER_RUNNER: "true"
|
||||||
COMPILES_HOST_DIR: $PWD/compiles
|
COMPILES_HOST_DIR: $PWD/compiles
|
||||||
SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex
|
|
||||||
SQLITE_PATH: /app/compiles/db.sqlite
|
SQLITE_PATH: /app/compiles/db.sqlite
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:rw
|
- /var/run/docker.sock:/var/run/docker.sock:rw
|
||||||
- ./compiles:/app/compiles
|
- ./compiles:/app/compiles
|
||||||
- ./cache:/app/cache
|
- ./cache:/app/cache
|
||||||
- ./bin/synctex:/app/bin/synctex
|
|
||||||
|
|
|
@ -13,7 +13,4 @@ mkdir -p /app/compiles && chown node:node /app/compiles
|
||||||
mkdir -p /app/db && chown node:node /app/db
|
mkdir -p /app/db && chown node:node /app/db
|
||||||
mkdir -p /app/output && chown node:node /app/output
|
mkdir -p /app/output && chown node:node /app/output
|
||||||
|
|
||||||
# make synctex available for remount in compiles
|
|
||||||
cp /app/bin/synctex /app/bin/synctex-mount/synctex
|
|
||||||
|
|
||||||
exec runuser -u node -- "$@"
|
exec runuser -u node -- "$@"
|
||||||
|
|
6
services/clsi/package-lock.json
generated
6
services/clsi/package-lock.json
generated
|
@ -6016,6 +6016,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sinon-chai": {
|
||||||
|
"version": "3.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz",
|
||||||
|
"integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"slice-ansi": {
|
"slice-ansi": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"sandboxed-module": "^2.0.3",
|
"sandboxed-module": "^2.0.3",
|
||||||
"sinon": "~9.0.1",
|
"sinon": "~9.0.1",
|
||||||
|
"sinon-chai": "^3.7.0",
|
||||||
"timekeeper": "2.2.0"
|
"timekeeper": "2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "synctex/synctex_parser.h"
|
|
||||||
|
|
||||||
|
|
||||||
void print_usage() {
|
|
||||||
fprintf (stderr, "Usage: synctex code <synctex_file> <filename> <line> <column>\n");
|
|
||||||
fprintf (stderr, " synctex pdf <synctex_file> <page> <h> <v>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[], char *envp[]) {
|
|
||||||
synctex_scanner_t scanner;
|
|
||||||
|
|
||||||
if (argc < 6 || (strcmp(argv[1], "code") != 0 && strcmp(argv[1], "pdf") != 0)) {
|
|
||||||
print_usage();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* direction = argv[1];
|
|
||||||
const char* synctex_file = argv[2];
|
|
||||||
|
|
||||||
scanner = synctex_scanner_new_with_output_file(synctex_file, NULL, 1);
|
|
||||||
|
|
||||||
if(!(scanner = synctex_scanner_parse(scanner))) {
|
|
||||||
fprintf (stderr, "Could not parse output file\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(direction, "code") == 0) {
|
|
||||||
const char* name = argv[3];
|
|
||||||
int line = atoi(argv[4]);
|
|
||||||
int column = atoi(argv[5]);
|
|
||||||
|
|
||||||
if(synctex_display_query(scanner, name, line, column) > 0) {
|
|
||||||
synctex_node_t node;
|
|
||||||
while((node = synctex_next_result(scanner))) {
|
|
||||||
int page = synctex_node_page(node);
|
|
||||||
float h = synctex_node_box_visible_h(node);
|
|
||||||
float v = synctex_node_box_visible_v(node);
|
|
||||||
float width = synctex_node_box_visible_width(node);
|
|
||||||
float height = synctex_node_box_visible_height(node);
|
|
||||||
printf ("NODE\t%d\t%.2f\t%.2f\t%.2f\t%.2f\n", page, h, v, width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (strcmp(direction, "pdf") == 0) {
|
|
||||||
int page = atoi(argv[3]);
|
|
||||||
float h = atof(argv[4]);
|
|
||||||
float v = atof(argv[5]);
|
|
||||||
|
|
||||||
if(synctex_edit_query(scanner, page, h, v) > 0) {
|
|
||||||
synctex_node_t node;
|
|
||||||
while((node = synctex_next_result(scanner))) {
|
|
||||||
int tag = synctex_node_tag(node);
|
|
||||||
const char* name = synctex_scanner_get_name(scanner, tag);
|
|
||||||
int line = synctex_node_line(node);
|
|
||||||
int column = synctex_node_column(node);
|
|
||||||
printf ("NODE\t%s\t%d\t%d\n", name, line, column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,346 +0,0 @@
|
||||||
/*
|
|
||||||
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.16
|
|
||||||
|
|
||||||
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_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 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);
|
|
||||||
|
|
||||||
/* 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
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
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.16
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This local header file is for TEXLIVE, use your own header to fit your system */
|
|
||||||
# include <w2c/c-auto.h> /* for inline && HAVE_xxx */
|
|
||||||
/* No inlining for synctex tool in texlive. */
|
|
||||||
# define SYNCTEX_INLINE
|
|
|
@ -1,141 +0,0 @@
|
||||||
This file is part of the SyncTeX package.
|
|
||||||
|
|
||||||
The Synchronization TeXnology named SyncTeX is a new feature
|
|
||||||
of recent TeX engines designed by Jerome Laurens.
|
|
||||||
It allows to synchronize between input and output, which means to
|
|
||||||
navigate from the source document to the typeset material and vice versa.
|
|
||||||
More informations on http://itexmac2.sourceforge.net/SyncTeX.html
|
|
||||||
|
|
||||||
This package is mainly for developers, it mainly contains the following files:
|
|
||||||
|
|
||||||
synctex_parser_readme.txt
|
|
||||||
synctex_parser_version.txt
|
|
||||||
synctex_parser_utils.c
|
|
||||||
synctex_parser_utils.h
|
|
||||||
synctex_parser_local.h
|
|
||||||
synctex_parser.h
|
|
||||||
synctex_parser.c
|
|
||||||
|
|
||||||
The file you are reading contains more informations about the SyncTeX parser history.
|
|
||||||
|
|
||||||
In order to support SyncTeX in a viewer, it is sufficient to include
|
|
||||||
in the source the files synctex_parser.h and synctex_parser.c.
|
|
||||||
The synctex parser usage is described in synctex_parser.h header file.
|
|
||||||
|
|
||||||
The other files are used by tex engines or by the synctex command line utility:
|
|
||||||
|
|
||||||
ChangeLog
|
|
||||||
README.txt
|
|
||||||
am
|
|
||||||
man1
|
|
||||||
man5
|
|
||||||
synctex-common.h
|
|
||||||
synctex-convert.sh
|
|
||||||
synctex-e-mem.ch0
|
|
||||||
synctex-e-mem.ch1
|
|
||||||
synctex-e-rec.ch0
|
|
||||||
synctex-e-rec.ch1
|
|
||||||
synctex-etex.h
|
|
||||||
synctex-mem.ch0
|
|
||||||
synctex-mem.ch1
|
|
||||||
synctex-mem.ch2
|
|
||||||
synctex-pdf-rec.ch2
|
|
||||||
synctex-pdftex.h
|
|
||||||
synctex-rec.ch0
|
|
||||||
synctex-rec.ch1
|
|
||||||
synctex-rec.ch2
|
|
||||||
synctex-tex.h
|
|
||||||
synctex-xe-mem.ch2
|
|
||||||
synctex-xe-rec.ch2
|
|
||||||
synctex-xe-rec.ch3
|
|
||||||
synctex-xetex.h
|
|
||||||
synctex.c
|
|
||||||
synctex.defines
|
|
||||||
synctex.h
|
|
||||||
synctex_main.c
|
|
||||||
tests
|
|
||||||
|
|
||||||
|
|
||||||
Version:
|
|
||||||
--------
|
|
||||||
This is version 1, which refers to the synctex output file format.
|
|
||||||
The files are identified by a build number.
|
|
||||||
In order to help developers to automatically manage the version and build numbers
|
|
||||||
and download the parser only when necessary, the synctex_parser.version
|
|
||||||
is an ASCII text file just containing the current version and build numbers.
|
|
||||||
|
|
||||||
History:
|
|
||||||
--------
|
|
||||||
1.1: Thu Jul 17 09:28:13 UTC 2008
|
|
||||||
- First official version available in TeXLive 2008 DVD.
|
|
||||||
Unfortunately, the backwards synchronization is not working properly mainly for ConTeXt users, see below.
|
|
||||||
1.2: Tue Sep 2 10:28:32 UTC 2008
|
|
||||||
- Correction for ConTeXt support in the edit query.
|
|
||||||
The previous method was assuming that TeX boxes do not overlap,
|
|
||||||
which is reasonable for LaTeX but not for ConTeXt.
|
|
||||||
This assumption is no longer considered.
|
|
||||||
1.3: Fri Sep 5 09:39:57 UTC 2008
|
|
||||||
- Local variable "read" renamed to "already_read" to avoid conflicts.
|
|
||||||
- "inline" compiler directive renamed to "SYNCTEX_INLINE" for code support and maintenance
|
|
||||||
- _synctex_error cannot be inlined due to variable arguments (thanks Christiaan Hofman)
|
|
||||||
- Correction in the display query, extra boundary nodes are used for a more precise forwards synchronization
|
|
||||||
1.4: Fri Sep 12 08:12:34 UTC 2008
|
|
||||||
- For an unknown reason, the previous version was not the real 1.3 (as used in iTeXMac2 build 747).
|
|
||||||
As a consequence, a crash was observed.
|
|
||||||
- Some typos are fixed.
|
|
||||||
1.6: Mon Nov 3 20:20:02 UTC 2008
|
|
||||||
- The bug that prevented synchronization with compressed files on windows has been fixed.
|
|
||||||
- New interface to allow system specific customization.
|
|
||||||
- Note that some APIs have changed.
|
|
||||||
1.8: Mer 8 jul 2009 11:32:38 UTC
|
|
||||||
Note that version 1.7 was delivered privately.
|
|
||||||
- bug fix: synctex was causing a memory leak in pdftex and xetex, thus some processing speed degradation
|
|
||||||
- bug fix: the synctex command line tool was broken when updating a .synctex file
|
|
||||||
- enhancement: better accuracy of the synchronization process
|
|
||||||
- enhancement: the pdf output file and the associated .synctex file no longer need to live in the same directory.
|
|
||||||
The new -d option of the synctex command line tool manages this situation.
|
|
||||||
This is handy when using something like tex -output-directory=DIR ...
|
|
||||||
1.9: Wed Nov 4 11:52:35 UTC 2009
|
|
||||||
- Various typo fixed
|
|
||||||
- OutputDebugString replaced by OutputDebugStringA to deliberately disable unicode preprocessing
|
|
||||||
- New conditional created because OutputDebugStringA is only available since Windows 2K professional
|
|
||||||
1.10: Sun Jan 10 10:12:32 UTC 2010
|
|
||||||
- Bug fix in synctex_parser.c to solve a synchronization problem with amsmath's gather environment.
|
|
||||||
Concerns the synctex tool.
|
|
||||||
1.11: Sun Jan 17 09:12:31 UTC 2010
|
|
||||||
- Bug fix in synctex_parser.c, function synctex_node_box_visible_v: 'x' replaced by 'y'.
|
|
||||||
Only 3rd party tools are concerned.
|
|
||||||
1.12: Mon Jul 19 21:52:10 UTC 2010
|
|
||||||
- Bug fix in synctex_parser.c, function __synctex_open: the io_mode was modified even in case of a non zero return,
|
|
||||||
causing a void .synctex.gz file to be created even if it was not expected. Reported by Marek Kasik concerning a bug on evince.
|
|
||||||
1.13: Fri Mar 11 07:39:12 UTC 2011
|
|
||||||
- Bug fix in synctex_parser.c, better synchronization as suggested by Jan Sundermeyer (near line 3388).
|
|
||||||
- Stronger code design in synctex_parser_utils.c, function _synctex_get_name (really neutral behavior).
|
|
||||||
Only 3rd party tools are concerned.
|
|
||||||
1.14: Fri Apr 15 19:10:57 UTC 2011
|
|
||||||
- taking output_directory into account
|
|
||||||
- Replaced FOPEN_WBIN_MODE by FOPEN_W_MODE when opening the text version of the .synctex file.
|
|
||||||
- Merging with LuaTeX's version of synctex.c
|
|
||||||
1.15: Fri Jun 10 14:10:17 UTC 2011
|
|
||||||
This concerns the synctex command line tool and 3rd party developers.
|
|
||||||
TeX and friends are not concerned by these changes.
|
|
||||||
- Bug fixed in _synctex_get_io_mode_name, sometimes the wrong mode was returned
|
|
||||||
- Support for LuaTeX convention of './' file prefixing
|
|
||||||
1.16: Tue Jun 14 08:23:30 UTC 2011
|
|
||||||
This concerns the synctex command line tool and 3rd party developers.
|
|
||||||
TeX and friends are not concerned by these changes.
|
|
||||||
- Better forward search (thanks Jose Alliste)
|
|
||||||
- Support for LuaTeX convention of './' file prefixing now for everyone, not only for Windows
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Copyright (c) 2008-2011 jerome DOT laurens AT u-bourgogne DOT fr
|
|
||||||
|
|
|
@ -1,479 +0,0 @@
|
||||||
/*
|
|
||||||
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.16
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
|
|
||||||
#define SYNCTEX_WINDOWS 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# ifdef SYNCTEX_WINDOWS
|
|
||||||
/* 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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * synctex_ignore_leading_dot_slash(const char * name)
|
|
||||||
{
|
|
||||||
while(SYNCTEX_IS_DOT(*name) && SYNCTEX_IS_PATH_SEPARATOR(name[1])) {
|
|
||||||
name += 2;
|
|
||||||
while (SYNCTEX_IS_PATH_SEPARATOR(*name)) {
|
|
||||||
++name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
lhs = synctex_ignore_leading_dot_slash(lhs);
|
|
||||||
rhs = synctex_ignore_leading_dot_slash(rhs);
|
|
||||||
# if SYNCTEX_WINDOWS
|
|
||||||
/* On Windows, filename should be compared case insensitive.
|
|
||||||
* The characters '/' and '\' are both valid path separators.
|
|
||||||
* There will be a very serious problem concerning UTF8 because
|
|
||||||
* not all the characters must be toupper...
|
|
||||||
* I would like to have URL's instead of filenames. */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else if(SYNCTEX_IS_PATH_SEPARATOR(*rhs)) {/* rhs points to a path separator but not lhs */
|
|
||||||
return synctex_NO;
|
|
||||||
} else if(toupper(*lhs) != toupper(*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;
|
|
||||||
# else
|
|
||||||
return 0 == strcmp(lhs,rhs)?synctex_YES:synctex_NO;
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
|
|
||||||
synctex_bool_t _synctex_path_is_absolute(const char * name) {
|
|
||||||
if(!strlen(name)) {
|
|
||||||
return synctex_NO;
|
|
||||||
}
|
|
||||||
# if SYNCTEX_WINDOWS
|
|
||||||
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];
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
/*
|
|
||||||
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.16
|
|
||||||
|
|
||||||
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 _WIN32
|
|
||||||
# define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c || '\\' == c)
|
|
||||||
# else
|
|
||||||
# define SYNCTEX_IS_PATH_SEPARATOR(c) ('/' == c)
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if _WIN32
|
|
||||||
# define SYNCTEX_IS_DOT(c) ('.' == c)
|
|
||||||
# else
|
|
||||||
# define SYNCTEX_IS_DOT(c) ('.' == c)
|
|
||||||
# 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);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
const char * synctex_ignore_leading_dot_slash(const char * name);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1 +0,0 @@
|
||||||
1.16
|
|
|
@ -101,7 +101,13 @@ Hello world
|
||||||
expect(error).to.not.exist
|
expect(error).to.not.exist
|
||||||
expect(result).to.deep.equal({
|
expect(result).to.deep.equal({
|
||||||
pdf: [
|
pdf: [
|
||||||
{ page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 },
|
{
|
||||||
|
page: 1,
|
||||||
|
h: 133.768356,
|
||||||
|
v: 134.764618,
|
||||||
|
height: 6.918498,
|
||||||
|
width: 343.71106,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
done()
|
done()
|
||||||
|
|
|
@ -59,7 +59,13 @@ Hello world
|
||||||
}
|
}
|
||||||
expect(pdfPositions).to.deep.equal({
|
expect(pdfPositions).to.deep.equal({
|
||||||
pdf: [
|
pdf: [
|
||||||
{ page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 },
|
{
|
||||||
|
page: 1,
|
||||||
|
h: 133.768356,
|
||||||
|
v: 134.764618,
|
||||||
|
height: 6.918498,
|
||||||
|
width: 343.71106,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return done()
|
return done()
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
|
const sinonChai = require('sinon-chai')
|
||||||
const SandboxedModule = require('sandboxed-module')
|
const SandboxedModule = require('sandboxed-module')
|
||||||
|
|
||||||
// Setup should interface
|
// Setup chai
|
||||||
chai.should()
|
chai.should()
|
||||||
|
chai.use(sinonChai)
|
||||||
|
|
||||||
// Global SandboxedModule settings
|
// Global SandboxedModule settings
|
||||||
SandboxedModule.configure({
|
SandboxedModule.configure({
|
||||||
requires: {
|
requires: {
|
||||||
'logger-sharelatex': {
|
'logger-sharelatex': {
|
||||||
|
debug() {},
|
||||||
log() {},
|
log() {},
|
||||||
info() {},
|
info() {},
|
||||||
warn() {},
|
warn() {},
|
||||||
|
|
|
@ -34,6 +34,12 @@ describe('CompileManager', function () {
|
||||||
build: 1234,
|
build: 1234,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
this.commandOutput = 'Dummy output'
|
||||||
|
this.compileBaseDir = '/compile/dir'
|
||||||
|
this.outputBaseDir = '/output/dir'
|
||||||
|
this.compileDir = `${this.compileBaseDir}/${this.projectId}-${this.userId}`
|
||||||
|
this.outputDir = `${this.outputBaseDir}/${this.projectId}-${this.userId}`
|
||||||
|
|
||||||
this.proc = new EventEmitter()
|
this.proc = new EventEmitter()
|
||||||
this.proc.stdout = new EventEmitter()
|
this.proc.stdout = new EventEmitter()
|
||||||
this.proc.stderr = new EventEmitter()
|
this.proc.stderr = new EventEmitter()
|
||||||
|
@ -53,11 +59,9 @@ describe('CompileManager', function () {
|
||||||
}
|
}
|
||||||
this.Settings = {
|
this.Settings = {
|
||||||
path: {
|
path: {
|
||||||
compilesDir: '/compiles/dir',
|
compilesDir: this.compileBaseDir,
|
||||||
outputDir: '/output/dir',
|
outputDir: this.outputBaseDir,
|
||||||
},
|
synctexBaseDir: sinon.stub(),
|
||||||
synctexBaseDir() {
|
|
||||||
return '/compile'
|
|
||||||
},
|
},
|
||||||
clsi: {
|
clsi: {
|
||||||
docker: {
|
docker: {
|
||||||
|
@ -65,12 +69,15 @@ describe('CompileManager', function () {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
this.Settings.path.synctexBaseDir
|
||||||
|
.withArgs(`${this.projectId}-${this.userId}`)
|
||||||
|
.returns(this.compileDir)
|
||||||
this.child_process = {
|
this.child_process = {
|
||||||
exec: sinon.stub(),
|
exec: sinon.stub(),
|
||||||
spawn: sinon.stub().returns(this.proc),
|
spawn: sinon.stub().returns(this.proc),
|
||||||
}
|
}
|
||||||
this.CommandRunner = {
|
this.CommandRunner = {
|
||||||
run: sinon.stub().yields(),
|
run: sinon.stub().yields(null, { stdout: this.commandOutput }),
|
||||||
}
|
}
|
||||||
this.DraftModeManager = {
|
this.DraftModeManager = {
|
||||||
injectDraftMode: sinon.stub().yields(),
|
injectDraftMode: sinon.stub().yields(),
|
||||||
|
@ -83,6 +90,11 @@ describe('CompileManager', function () {
|
||||||
runner((err, ...result) => callback(err, ...result))
|
runner((err, ...result) => callback(err, ...result))
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
this.SynctexOutputParser = {
|
||||||
|
parseViewOutput: sinon.stub(),
|
||||||
|
parseEditOutput: sinon.stub(),
|
||||||
|
}
|
||||||
|
|
||||||
this.fs = {
|
this.fs = {
|
||||||
lstat: sinon.stub(),
|
lstat: sinon.stub(),
|
||||||
stat: sinon.stub(),
|
stat: sinon.stub(),
|
||||||
|
@ -104,6 +116,7 @@ describe('CompileManager', function () {
|
||||||
'./DraftModeManager': this.DraftModeManager,
|
'./DraftModeManager': this.DraftModeManager,
|
||||||
'./TikzManager': this.TikzManager,
|
'./TikzManager': this.TikzManager,
|
||||||
'./LockManager': this.LockManager,
|
'./LockManager': this.LockManager,
|
||||||
|
'./SynctexOutputParser': this.SynctexOutputParser,
|
||||||
fs: this.fs,
|
fs: this.fs,
|
||||||
'fs-extra': this.fse,
|
'fs-extra': this.fse,
|
||||||
},
|
},
|
||||||
|
@ -124,9 +137,6 @@ describe('CompileManager', function () {
|
||||||
compileGroup: (this.compileGroup = 'compile-group'),
|
compileGroup: (this.compileGroup = 'compile-group'),
|
||||||
}
|
}
|
||||||
this.env = {}
|
this.env = {}
|
||||||
this.Settings.compileDir = 'compiles'
|
|
||||||
this.compileDir = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`
|
|
||||||
this.outputDir = `${this.Settings.path.outputDir}/${this.projectId}-${this.userId}`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when the project is locked', function () {
|
describe('when the project is locked', function () {
|
||||||
|
@ -278,13 +288,7 @@ describe('CompileManager', function () {
|
||||||
|
|
||||||
it('should remove the project directory', function () {
|
it('should remove the project directory', function () {
|
||||||
this.child_process.spawn
|
this.child_process.spawn
|
||||||
.calledWith('rm', [
|
.calledWith('rm', ['-r', '-f', '--', this.compileDir, this.outputDir])
|
||||||
'-r',
|
|
||||||
'-f',
|
|
||||||
'--',
|
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`,
|
|
||||||
`${this.Settings.path.outputDir}/${this.projectId}-${this.userId}`,
|
|
||||||
])
|
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -312,13 +316,7 @@ describe('CompileManager', function () {
|
||||||
|
|
||||||
it('should remove the project directory', function () {
|
it('should remove the project directory', function () {
|
||||||
this.child_process.spawn
|
this.child_process.spawn
|
||||||
.calledWith('rm', [
|
.calledWith('rm', ['-r', '-f', '--', this.compileDir, this.outputDir])
|
||||||
'-r',
|
|
||||||
'-f',
|
|
||||||
'--',
|
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`,
|
|
||||||
`${this.Settings.path.outputDir}/${this.projectId}-${this.userId}`,
|
|
||||||
])
|
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -326,7 +324,7 @@ describe('CompileManager', function () {
|
||||||
this.callback.calledWithExactly(sinon.match(Error)).should.equal(true)
|
this.callback.calledWithExactly(sinon.match(Error)).should.equal(true)
|
||||||
|
|
||||||
this.callback.args[0][0].message.should.equal(
|
this.callback.args[0][0].message.should.equal(
|
||||||
`rm -r ${this.Settings.path.compilesDir}/${this.projectId}-${this.userId} ${this.Settings.path.outputDir}/${this.projectId}-${this.userId} failed: ${this.error}`
|
`rm -r ${this.compileDir} ${this.outputDir} failed: ${this.error}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -341,9 +339,7 @@ describe('CompileManager', function () {
|
||||||
this.height = 234.56
|
this.height = 234.56
|
||||||
this.line = 5
|
this.line = 5
|
||||||
this.column = 3
|
this.column = 3
|
||||||
this.file_name = 'main.tex'
|
this.filename = 'main.tex'
|
||||||
this.Settings.path.synctexBaseDir = projectId =>
|
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('syncFromCode', function () {
|
describe('syncFromCode', function () {
|
||||||
|
@ -353,12 +349,14 @@ describe('CompileManager', function () {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.stdout = `NODE\t${this.page}\t${this.h}\t${this.v}\t${this.width}\t${this.height}\n`
|
this.records = [{ page: 1, h: 2, v: 3, width: 4, height: 5 }]
|
||||||
this.CommandRunner.run.yields(null, { stdout: this.stdout })
|
this.SynctexOutputParser.parseViewOutput
|
||||||
|
.withArgs(this.commandOutput)
|
||||||
|
.returns(this.records)
|
||||||
this.CompileManager.syncFromCode(
|
this.CompileManager.syncFromCode(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
this.userId,
|
this.userId,
|
||||||
this.file_name,
|
this.filename,
|
||||||
this.line,
|
this.line,
|
||||||
this.column,
|
this.column,
|
||||||
'',
|
'',
|
||||||
|
@ -367,39 +365,30 @@ describe('CompileManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should execute the synctex binary', function () {
|
it('should execute the synctex binary', function () {
|
||||||
const synctexPath = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/output.pdf`
|
const outputFilePath = `${this.compileDir}/output.pdf`
|
||||||
const filePath = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/${this.file_name}`
|
const inputFilePath = `${this.compileDir}/${this.filename}`
|
||||||
this.CommandRunner.run
|
this.CommandRunner.run.should.have.been.calledWith(
|
||||||
.calledWith(
|
|
||||||
`${this.projectId}-${this.userId}`,
|
`${this.projectId}-${this.userId}`,
|
||||||
[
|
[
|
||||||
'/opt/synctex',
|
'synctex',
|
||||||
'code',
|
'view',
|
||||||
synctexPath,
|
'-i',
|
||||||
filePath,
|
`${this.line}:${this.column}:${inputFilePath}`,
|
||||||
this.line,
|
'-o',
|
||||||
this.column,
|
outputFilePath,
|
||||||
],
|
],
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`,
|
this.compileDir,
|
||||||
this.Settings.clsi.docker.image,
|
this.Settings.clsi.docker.image,
|
||||||
60000,
|
60000,
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
.should.equal(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the callback with the parsed output', function () {
|
it('should call the callback with the parsed output', function () {
|
||||||
this.callback
|
this.callback.should.have.been.calledWith(
|
||||||
.calledWith(null, [
|
null,
|
||||||
{
|
sinon.match.array.deepEquals(this.records)
|
||||||
page: this.page,
|
)
|
||||||
h: this.h,
|
|
||||||
v: this.v,
|
|
||||||
height: this.height,
|
|
||||||
width: this.width,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.should.equal(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with a custom imageName', function () {
|
describe('with a custom imageName', function () {
|
||||||
|
@ -409,7 +398,7 @@ describe('CompileManager', function () {
|
||||||
this.CompileManager.syncFromCode(
|
this.CompileManager.syncFromCode(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
this.userId,
|
this.userId,
|
||||||
this.file_name,
|
this.filename,
|
||||||
this.line,
|
this.line,
|
||||||
this.column,
|
this.column,
|
||||||
customImageName,
|
customImageName,
|
||||||
|
@ -418,25 +407,23 @@ describe('CompileManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should execute the synctex binary in a custom docker image', function () {
|
it('should execute the synctex binary in a custom docker image', function () {
|
||||||
const synctexPath = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/output.pdf`
|
const outputFilePath = `${this.compileDir}/output.pdf`
|
||||||
const filePath = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/${this.file_name}`
|
const inputFilePath = `${this.compileDir}/${this.filename}`
|
||||||
this.CommandRunner.run
|
this.CommandRunner.run.should.have.been.calledWith(
|
||||||
.calledWith(
|
|
||||||
`${this.projectId}-${this.userId}`,
|
`${this.projectId}-${this.userId}`,
|
||||||
[
|
[
|
||||||
'/opt/synctex',
|
'synctex',
|
||||||
'code',
|
'view',
|
||||||
synctexPath,
|
'-i',
|
||||||
filePath,
|
`${this.line}:${this.column}:${inputFilePath}`,
|
||||||
this.line,
|
'-o',
|
||||||
this.column,
|
outputFilePath,
|
||||||
],
|
],
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`,
|
this.compileDir,
|
||||||
customImageName,
|
customImageName,
|
||||||
60000,
|
60000,
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
.should.equal(true)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -448,8 +435,10 @@ describe('CompileManager', function () {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.stdout = `NODE\t${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/${this.file_name}\t${this.line}\t${this.column}\n`
|
this.records = [{ file: 'main.tex', line: 1, column: 1 }]
|
||||||
this.CommandRunner.run.yields(null, { stdout: this.stdout })
|
this.SynctexOutputParser.parseEditOutput
|
||||||
|
.withArgs(this.commandOutput, this.compileDir)
|
||||||
|
.returns(this.records)
|
||||||
this.CompileManager.syncFromPdf(
|
this.CompileManager.syncFromPdf(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
this.userId,
|
this.userId,
|
||||||
|
@ -462,29 +451,27 @@ describe('CompileManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should execute the synctex binary', function () {
|
it('should execute the synctex binary', function () {
|
||||||
const synctexPath = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/output.pdf`
|
const outputFilePath = `${this.compileDir}/output.pdf`
|
||||||
this.CommandRunner.run
|
this.CommandRunner.run.should.have.been.calledWith(
|
||||||
.calledWith(
|
|
||||||
`${this.projectId}-${this.userId}`,
|
`${this.projectId}-${this.userId}`,
|
||||||
['/opt/synctex', 'pdf', synctexPath, this.page, this.h, this.v],
|
[
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`,
|
'synctex',
|
||||||
|
'edit',
|
||||||
|
'-o',
|
||||||
|
`${this.page}:${this.h}:${this.v}:${outputFilePath}`,
|
||||||
|
],
|
||||||
|
this.compileDir,
|
||||||
this.Settings.clsi.docker.image,
|
this.Settings.clsi.docker.image,
|
||||||
60000,
|
60000,
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
.should.equal(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the callback with the parsed output', function () {
|
it('should call the callback with the parsed output', function () {
|
||||||
this.callback
|
this.callback.should.have.been.calledWith(
|
||||||
.calledWith(null, [
|
null,
|
||||||
{
|
sinon.match.array.deepEquals(this.records)
|
||||||
file: this.file_name,
|
)
|
||||||
line: this.line,
|
|
||||||
column: this.column,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.should.equal(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with a custom imageName', function () {
|
describe('with a custom imageName', function () {
|
||||||
|
@ -503,12 +490,17 @@ describe('CompileManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should execute the synctex binary in a custom docker image', function () {
|
it('should execute the synctex binary in a custom docker image', function () {
|
||||||
const synctexPath = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}/output.pdf`
|
const outputFilePath = `${this.compileDir}/output.pdf`
|
||||||
this.CommandRunner.run
|
this.CommandRunner.run
|
||||||
.calledWith(
|
.calledWith(
|
||||||
`${this.projectId}-${this.userId}`,
|
`${this.projectId}-${this.userId}`,
|
||||||
['/opt/synctex', 'pdf', synctexPath, this.page, this.h, this.v],
|
[
|
||||||
`${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`,
|
'synctex',
|
||||||
|
'edit',
|
||||||
|
'-o',
|
||||||
|
`${this.page}:${this.h}:${this.v}:${outputFilePath}`,
|
||||||
|
],
|
||||||
|
this.compileDir,
|
||||||
customImageName,
|
customImageName,
|
||||||
60000,
|
60000,
|
||||||
{}
|
{}
|
||||||
|
@ -525,22 +517,20 @@ describe('CompileManager', function () {
|
||||||
this.fs.readFile.yields(null, this.stdout)
|
this.fs.readFile.yields(null, this.stdout)
|
||||||
|
|
||||||
this.timeout = 60 * 1000
|
this.timeout = 60 * 1000
|
||||||
this.file_name = 'main.tex'
|
this.filename = 'main.tex'
|
||||||
this.Settings.path.compilesDir = '/local/compile/directory'
|
|
||||||
this.image = 'example.com/image'
|
this.image = 'example.com/image'
|
||||||
|
|
||||||
this.CompileManager.wordcount(
|
this.CompileManager.wordcount(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
this.userId,
|
this.userId,
|
||||||
this.file_name,
|
this.filename,
|
||||||
this.image,
|
this.image,
|
||||||
this.callback
|
this.callback
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should run the texcount command', function () {
|
it('should run the texcount command', function () {
|
||||||
this.directory = `${this.Settings.path.compilesDir}/${this.projectId}-${this.userId}`
|
this.filePath = `$COMPILE_DIR/${this.filename}`
|
||||||
this.filePath = `$COMPILE_DIR/${this.file_name}`
|
|
||||||
this.command = [
|
this.command = [
|
||||||
'texcount',
|
'texcount',
|
||||||
'-nocol',
|
'-nocol',
|
||||||
|
@ -553,7 +543,7 @@ describe('CompileManager', function () {
|
||||||
.calledWith(
|
.calledWith(
|
||||||
`${this.projectId}-${this.userId}`,
|
`${this.projectId}-${this.userId}`,
|
||||||
this.command,
|
this.command,
|
||||||
this.directory,
|
this.compileDir,
|
||||||
this.image,
|
this.image,
|
||||||
this.timeout,
|
this.timeout,
|
||||||
{}
|
{}
|
||||||
|
|
116
services/clsi/test/unit/js/SynctexOutputParserTests.js
Normal file
116
services/clsi/test/unit/js/SynctexOutputParserTests.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
const Path = require('path')
|
||||||
|
const SandboxedModule = require('sandboxed-module')
|
||||||
|
const { expect } = require('chai')
|
||||||
|
|
||||||
|
const MODULE_PATH = Path.join(__dirname, '../../../app/js/SynctexOutputParser')
|
||||||
|
|
||||||
|
describe('SynctexOutputParser', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.SynctexOutputParser = SandboxedModule.require(MODULE_PATH)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('parseViewOutput', function () {
|
||||||
|
it('parses valid output', function () {
|
||||||
|
const output = `This is SyncTeX command line utility, version 1.5
|
||||||
|
SyncTeX result begin
|
||||||
|
Output:/compile/output.pdf
|
||||||
|
Page:1
|
||||||
|
x:136.537964
|
||||||
|
y:661.437561
|
||||||
|
h:133.768356
|
||||||
|
v:663.928223
|
||||||
|
W:343.711060
|
||||||
|
H:9.962640
|
||||||
|
before:
|
||||||
|
offset:-1
|
||||||
|
middle:
|
||||||
|
after:
|
||||||
|
Output:/compile/output.pdf
|
||||||
|
Page:2
|
||||||
|
x:178.769592
|
||||||
|
y:649.482361
|
||||||
|
h:134.768356
|
||||||
|
v:651.973022
|
||||||
|
W:342.711060
|
||||||
|
H:19.962640
|
||||||
|
before:
|
||||||
|
offset:-1
|
||||||
|
middle:
|
||||||
|
after:
|
||||||
|
SyncTeX result end
|
||||||
|
`
|
||||||
|
const records = this.SynctexOutputParser.parseViewOutput(output)
|
||||||
|
expect(records).to.deep.equal([
|
||||||
|
{
|
||||||
|
page: 1,
|
||||||
|
h: 133.768356,
|
||||||
|
v: 663.928223,
|
||||||
|
width: 343.71106,
|
||||||
|
height: 9.96264,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
page: 2,
|
||||||
|
h: 134.768356,
|
||||||
|
v: 651.973022,
|
||||||
|
width: 342.71106,
|
||||||
|
height: 19.96264,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles garbage', function () {
|
||||||
|
const output = 'This computer is on strike!'
|
||||||
|
const records = this.SynctexOutputParser.parseViewOutput(output)
|
||||||
|
expect(records).to.deep.equal([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('parseEditOutput', function () {
|
||||||
|
it('parses valid output', function () {
|
||||||
|
const output = `This is SyncTeX command line utility, version 1.5
|
||||||
|
SyncTeX result begin
|
||||||
|
Output:/compile/output.pdf
|
||||||
|
Input:/compile/main.tex
|
||||||
|
Line:17
|
||||||
|
Column:-1
|
||||||
|
Offset:0
|
||||||
|
Context:
|
||||||
|
SyncTeX result end
|
||||||
|
`
|
||||||
|
const records = this.SynctexOutputParser.parseEditOutput(
|
||||||
|
output,
|
||||||
|
'/compile'
|
||||||
|
)
|
||||||
|
expect(records).to.deep.equal([
|
||||||
|
{ file: 'main.tex', line: 17, column: -1 },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles values that contain colons', function () {
|
||||||
|
const output = `This is SyncTeX command line utility, version 1.5
|
||||||
|
SyncTeX result begin
|
||||||
|
Output:/compile/output.pdf
|
||||||
|
Input:/compile/this-file:has-a-weird-name.tex
|
||||||
|
Line:17
|
||||||
|
Column:-1
|
||||||
|
Offset:0
|
||||||
|
Context:
|
||||||
|
SyncTeX result end
|
||||||
|
`
|
||||||
|
|
||||||
|
const records = this.SynctexOutputParser.parseEditOutput(
|
||||||
|
output,
|
||||||
|
'/compile'
|
||||||
|
)
|
||||||
|
expect(records).to.deep.equal([
|
||||||
|
{ file: 'this-file:has-a-weird-name.tex', line: 17, column: -1 },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles garbage', function () {
|
||||||
|
const output = '2 + 2 = 4'
|
||||||
|
const records = this.SynctexOutputParser.parseEditOutput(output)
|
||||||
|
expect(records).to.deep.equal([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue