174 lines
6.6 KiB
Bash
Executable File
174 lines
6.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# export_tex.sh — collect all files needed to compile a .tex file
|
|
# Usage: ./export_tex.sh [manuscript.tex] [output_dir]
|
|
# Defaults: first .tex in current dir; output_dir → ./export
|
|
|
|
set -euo pipefail
|
|
|
|
# ── arguments ────────────────────────────────────────────────────────────────
|
|
TEX_FILE="${1:-}"
|
|
if [[ -z "$TEX_FILE" ]]; then
|
|
TEX_FILE=$(ls *.tex 2>/dev/null | head -1)
|
|
[[ -z "$TEX_FILE" ]] && { echo "No .tex file found."; exit 1; }
|
|
fi
|
|
[[ -f "$TEX_FILE" ]] || { echo "File not found: $TEX_FILE"; exit 1; }
|
|
|
|
EXPORT_DIR="${2:-export}"
|
|
SRC_DIR="$(dirname "$(realpath "$TEX_FILE")")"
|
|
TEX_BASE="$(basename "$TEX_FILE")"
|
|
|
|
mkdir -p "$EXPORT_DIR"
|
|
|
|
IMAGE_EXTS=(".pdf" ".png" ".jpg" ".jpeg" ".eps" ".svg" ".PNG" ".JPG" ".JPEG")
|
|
|
|
# Track already-processed tex files to avoid infinite loops
|
|
declare -A VISITED_TEX
|
|
|
|
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
copy_rel() {
|
|
# Copy a file to EXPORT_DIR preserving its path relative to SRC_DIR.
|
|
local src="$1"
|
|
[[ -f "$src" ]] || { echo " [missing] $src"; return; }
|
|
local rel="${src#$SRC_DIR/}"
|
|
local dest="$EXPORT_DIR/$rel"
|
|
mkdir -p "$(dirname "$dest")"
|
|
cp -v "$src" "$dest"
|
|
}
|
|
|
|
find_recursive() {
|
|
# Find file by basename+ext under SRC_DIR; prefer shallower paths.
|
|
local base="$1"; shift
|
|
local found=""
|
|
for ext in "$@"; do
|
|
# Sort by path depth (fewer slashes = shallower) then alphabetically
|
|
found=$(find "$SRC_DIR" -name "${base}${ext}" \
|
|
| awk '{ print gsub(/\//,"/")" "$0 }' | sort -n | head -1 \
|
|
| cut -d' ' -f2-)
|
|
[[ -n "$found" ]] && break
|
|
done
|
|
echo "$found"
|
|
}
|
|
|
|
copy_image() {
|
|
local img="$1"
|
|
# Normalise: strip leading ./ if present
|
|
img="${img#./}"
|
|
|
|
# 1. Try path as written in .tex (relative to SRC_DIR), with/without ext
|
|
if [[ "$img" == *.* ]]; then
|
|
local f="$SRC_DIR/$img"
|
|
if [[ -f "$f" ]]; then copy_rel "$f"; return; fi
|
|
else
|
|
for ext in "${IMAGE_EXTS[@]}"; do
|
|
local f="$SRC_DIR/${img}${ext}"
|
|
if [[ -f "$f" ]]; then copy_rel "$f"; return; fi
|
|
done
|
|
fi
|
|
|
|
# 2. Fallback: recursive search by basename only
|
|
local base="${img##*/}"
|
|
local f
|
|
f=$(find_recursive "$base" "${IMAGE_EXTS[@]}")
|
|
if [[ -n "$f" ]]; then
|
|
copy_rel "$f"
|
|
else
|
|
echo " [missing] $img"
|
|
fi
|
|
}
|
|
|
|
# ── recursive tex processor ───────────────────────────────────────────────────
|
|
process_tex() {
|
|
local tex_path
|
|
tex_path="$(realpath "$1")"
|
|
[[ -n "${VISITED_TEX[$tex_path]:-}" ]] && return
|
|
VISITED_TEX["$tex_path"]=1
|
|
|
|
[[ -f "$tex_path" ]] || { echo " [missing tex] $tex_path"; return; }
|
|
copy_rel "$tex_path"
|
|
|
|
# \input{file} or \include{file}
|
|
while IFS= read -r inc; do
|
|
# Try with and without .tex extension
|
|
local candidate=""
|
|
for try in "$SRC_DIR/${inc}.tex" "$SRC_DIR/${inc}"; do
|
|
[[ -f "$try" ]] && { candidate="$try"; break; }
|
|
done
|
|
if [[ -n "$candidate" ]]; then
|
|
process_tex "$candidate"
|
|
else
|
|
echo " [missing tex] $inc"
|
|
fi
|
|
done < <(grep -oP '\\(input|include)\{[^}]+\}' "$tex_path" \
|
|
| grep -oP '(?<=\{)[^}]+' | tr -d '\r')
|
|
|
|
# \includegraphics
|
|
while IFS= read -r img; do
|
|
copy_image "$img"
|
|
done < <(grep -oP '\\includegraphics(\[[^\]]*\])?\{[^}]+\}' "$tex_path" \
|
|
| grep -oP '(?<=\{)[^}]+' | tr -d '\r')
|
|
}
|
|
|
|
# ── 1. recurse through all .tex files ────────────────────────────────────────
|
|
process_tex "$TEX_FILE"
|
|
|
|
# ── 2. document class (.cls) ──────────────────────────────────────────────────
|
|
CLS=$(grep -oP '\\documentclass\[?[^\]]*\]?\{[^}]+\}' "$TEX_FILE" \
|
|
| grep -oP '(?<=\{)[^}]+' | tr -d '\r' | head -1)
|
|
if [[ -n "$CLS" ]]; then
|
|
f=$(find_recursive "$CLS" ".cls")
|
|
if [[ -n "$f" ]]; then
|
|
copy_rel "$f"
|
|
else
|
|
echo " [system] ${CLS}.cls (not local, assumed installed)"
|
|
fi
|
|
fi
|
|
|
|
# Collect all files to search for bibliography settings: .tex + any local .cls/.sty
|
|
mapfile -t SEARCH_FILES < <(
|
|
printf '%s\n' "${!VISITED_TEX[@]}"
|
|
find "$SRC_DIR" -maxdepth 1 \( -name "*.cls" -o -name "*.sty" \) 2>/dev/null
|
|
)
|
|
|
|
# ── 3. bibliography style (.bst / .bbx) ──────────────────────────────────────
|
|
BST=$(grep -rhP '\\bibliographystyle\{[^}]+\}' "${SEARCH_FILES[@]}" 2>/dev/null \
|
|
| grep -oP '(?<=\{)[^}]+' | tr -d '\r' | head -1 || true)
|
|
if [[ -n "$BST" ]]; then
|
|
f=$(find_recursive "$BST" ".bst" ".bbx")
|
|
if [[ -n "$f" ]]; then
|
|
copy_rel "$f"
|
|
else
|
|
echo " [system] ${BST}.bst (not local, assumed installed)"
|
|
fi
|
|
fi
|
|
|
|
# also copy any local .bbx / .cbx referenced by biblatex style option
|
|
# matches both style= and bibstyle=
|
|
grep -rhP '\\usepackage.*biblatex' "${SEARCH_FILES[@]}" 2>/dev/null \
|
|
| grep -oP '(?:bib)?style\s*=\s*\{?[^},\]]+' | grep -oP '[^={} \r]+$' \
|
|
| tr -d '\r' | sort -u \
|
|
| while IFS= read -r sty; do
|
|
for ext in ".bbx" ".cbx"; do
|
|
f=$(find_recursive "$sty" "$ext")
|
|
[[ -n "$f" ]] && copy_rel "$f"
|
|
done
|
|
done || true
|
|
|
|
# ── 4. bibliography database (.bib) ──────────────────────────────────────────
|
|
# handles both \bibliography{} (BibTeX) and \addbibresource{} (biblatex)
|
|
while IFS= read -r bib; do
|
|
bib="${bib%.bib}" # strip .bib suffix if already present
|
|
f=$(find_recursive "$bib" ".bib")
|
|
[[ -n "$f" ]] && copy_rel "$f" || echo " [missing] ${bib}.bib"
|
|
done < <({ grep -rhP '\\bibliography\{[^}]+\}' "${SEARCH_FILES[@]}" 2>/dev/null \
|
|
| grep -oP '(?<=\{)[^}]+' | tr ',' '\n' | tr -d ' \r'
|
|
grep -rhP '\\addbibresource\{[^}]+\}' "${SEARCH_FILES[@]}" 2>/dev/null \
|
|
| grep -oP '(?<=\{)[^}]+' | tr -d '\r'; } \
|
|
| sort -u || true)
|
|
|
|
# ── 5. .bbl (pre-built bibliography, if present) ─────────────────────────────
|
|
BBL="$SRC_DIR/${TEX_BASE%.tex}.bbl"
|
|
[[ -f "$BBL" ]] && copy_rel "$BBL"
|
|
|
|
echo ""
|
|
echo "Done. Files exported to: $EXPORT_DIR/"
|