diff --git a/export_tex.sh b/export_tex.sh new file mode 100755 index 0000000..13a2d4d --- /dev/null +++ b/export_tex.sh @@ -0,0 +1,173 @@ +#!/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/"