summaryrefslogtreecommitdiff
path: root/support/xml2pmx/scripts/musixtex.lua
diff options
context:
space:
mode:
Diffstat (limited to 'support/xml2pmx/scripts/musixtex.lua')
-rwxr-xr-xsupport/xml2pmx/scripts/musixtex.lua631
1 files changed, 631 insertions, 0 deletions
diff --git a/support/xml2pmx/scripts/musixtex.lua b/support/xml2pmx/scripts/musixtex.lua
new file mode 100755
index 0000000000..a4175ad17b
--- /dev/null
+++ b/support/xml2pmx/scripts/musixtex.lua
@@ -0,0 +1,631 @@
+#!/usr/bin/env texlua
+
+VERSION = "0.23"
+
+--[[
+ musixtex.lua: processes MusiXTeX files using xml2pmx and/or prepmx and/or pmxab
+ and/or autosp as pre-processors (and deletes intermediate files)
+
+ (c) Copyright 2011-2020 Bob Tennent rdt@cs.queensu.ca
+ and Dirk Laurie dirk.laurie@gmail.com
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+--]]
+
+--[[
+
+ ChangeLog:
+
+ version 0.23 2020-05-21 RDT
+ added support for xml2pmx pre-preprocessing
+
+ version 0.22 2020-03-20 RDT
+ add -X option
+ add -version, --version, -help, --help options
+
+ version 0.21 2018-07-27 RDT
+ add -P option.
+
+ version 0.20 2018-06-11 RDT
+ remove .mx1 file before tex processing
+
+ version 0.19 2017-12-10 RDT
+ Allow non-standard extensions.
+ Add -M and -A options.
+
+ version 0.18 2017-06-13 RDT
+ Allow autosp to generate .ltx files
+
+ version 0.17a 2017-01-08 RDT
+ Added -D option.
+ Avoid writing or concatenating a nil value.
+
+ version 0.16e 2016-03-02 DL
+ missing version information (caused by batchmode in 0.16c) fixed
+
+ version 0.16d 2016-03-02 RDT
+ filename argument in autosp failure message fixed
+
+ version 0.16c 2016-02-24 RDT
+ -interaction batchmode for -q
+ report_error reports only the first error
+
+ version 0.16b 2016-02-20 DL
+ Improved help message as suggested by Bob Tennent.
+
+ version 0.16a 2015-12-30 DL
+ Corrects bug in -g option reported by Christian Mondrup.
+
+ version 0.16 2015-12-24 DL
+ Versions read from tempfile and written to musixtex.log.
+
+ version 0.15 2015-12-03 DL
+ Option -q added, which redirects most screen output into
+ a temporary file. If an error occurs at the TeX stage, processing
+ halts immediately and the tail of the log file is sent to stderr.
+
+ version 0.14 2015-12-01 DL
+ Writes `musixtex.log` and deletes other logfiles (except with -i)
+ Hierarchy of extensions emphasized in usage() and consistently
+ used elsewhere.
+ Unknown option-like arguments reported and ignored, not treated
+ as filenames
+ Warnings and error messages generated by musixtex.lua start with
+ respctively `!` and `!!`.
+
+ version 0.13 2015-11-30 RDT
+ Process .mtx and .pmx files (suggested by Dirk Laurie)
+ exit_code is now an error count
+ Use pmxab exit code to distinguish between errors and warnings
+
+ version 0.12 2015-11-28 RDT
+ Process .ltx files, with -l implied
+
+ version 0.11 2015-07-16 RDT
+ Automatic autosp preprocessing.
+
+ version 0.10 2015-04-23 RDT
+ Add -a option to preprocess using autosp
+
+ version 0.9 2015-02-13 RDT
+ Add an additional latex pass to resolve cross-references.
+ Add -e0 option to dvips as given in musixdoc.tex
+ Add -x option to call makeindex
+
+ version 0.8 2014-05-18 RDT
+ Add -g option
+
+ version 0.7 2013-12-11 RDT
+ Add -F fmt option
+
+ version 0.6 2012-09-14 RDT
+ Add -1 (one-pass [pdf][la]tex processing) option.
+
+ version 0.5 2011-11-28 RDT
+ Add -i (retain intermediate files) option.
+
+ version 0.4 2011-04-30 RDT
+ Allow multiple filenames (and options).
+ Add -f (default) and -l (latex) options.
+
+ version 0.3 2011-04-25 RDT
+ Add -d (dvipdfm) and -s (stop at dvi) options.
+
+ version 0.2 2011-04-21 RDT
+ Allow basename.tex as filename.
+ Add -p option for pdfetex processing.
+ Add standard -v -h options.
+--]]
+local orig_print = print
+
+function usage()
+ orig_print
+[[
+Usage: [texlua] musixtex.lua { option | basename[ .xml | .mtx | .pmx | .aspc | .tex | .ltx] } ...
+ When no extension is given, extensions are tried in the above order
+ until a source file is found. Preprocessing goes xml-pmx-tex or mtx-pmx-tex or
+ aspc-tex, with the entry point determined by the extension.
+ The normal route after preprocessing goes tex-dvi-ps-pdf, but shorter
+ routes are also available, see the options. The default 3-pass processing route
+ for .tex files is etex-musixflx-etex.
+Options: -v, --version version
+ -h, --help help
+ -l latex source
+ -p direct tex-pdf (pdftex etc)
+ -F fmt use fmt as the TeX processor
+ -d tex-dvi-pdf (using dvipdfm if -D not used)
+ -D dvixx use dvixx as the dvi processor
+ -P ps2pdfxx use ps2pdfxx as the Postscript processor
+ -c preprocess pmx file using pmxchords
+ -m stop at pmx
+ -M prepmxx use prepmxx as the mtx preprocessor
+ -A autospx use autospx as the aspc preprocessor
+ -X pmxabx use pmxabx as the pmx preprocessor
+ -L xmlx use xmlx as the xml preprocessor
+ -t stop at tex/mid
+ -s stop at dvi
+ -g stop at ps
+ -i retain intermediate and log files
+ -q quiet mode (redirect screen logs, write summary on musixtex.log)
+ -1 one-pass [pdf][la]tex processing
+ -x run makeindex
+ -f restore default processing
+Four TeX engines are available via the -l and -p options.
+ etex default
+ latex -l
+ pdfetex -p
+ pdflatex -l -p
+If the -F option is used, options -l and -p need to be set if the engine
+name does not contain "latex" and "pdf" respectively. For example, the
+above four engines can be replaced by:
+ -F "luatex --output-format=dvi"
+ -F "lualatex --output-format=dvi"
+ -F "luatex" -p
+ -F "lualatex" -p
+]]
+end
+
+function whoami ()
+ print("This is musixtex.lua version ".. VERSION .. ".")
+end
+
+function exists (filename, nolog)
+ local f = io.open(filename, "r")
+ if f then
+ f:close()
+ if not nolog then musixlog:write("Processing " .. filename,'\n' ) end
+ return true
+ else
+ return false
+ end
+end
+
+-- System commands for the various programs are mostly
+-- set to nil if the step is to be omitted, which can be
+-- tested by a simple "if" statement.
+-- Exceptions:
+-- 'tex' is the command for processing a TeX file, but it is important
+-- to know whether the user has explicitly specified an option that
+-- affects this choice, in which case the automatic selection of the
+-- command is restricted or disabled.
+-- force_engine "use the command provided"
+-- override records when 'l' and/or 'p' options are in effect
+-- 'dvi' is the command for processing a DVI file, but there are two
+-- reasons for not needing it and it is important for the automatic
+-- selection of a TeX engine to know which is in effect. This is
+-- possible by exploiting the the fact that Lua has two false values.
+-- dvi == nil "do not produce a DVI file" (but maybe PDF)
+-- dvi == false "do not process the DVI file" (but stop after TeX)
+local dvips = "dvips -e0"
+function defaults()
+ xml2pmx = "xml2pmx"
+ prepmx = "prepmx"
+ pmx = "pmxab"
+ autosp = "autosp"
+ tex = "etex"
+ musixflx = "musixflx"
+ dvi = dvips
+ ps2pdf = "ps2pdf"
+ cleanup = true -- clean up intermediate and log files
+ index = false
+ latex = false
+ force_engine = false -- indicates whether -F is specified
+ override = ""
+ passes = 2
+ quiet = "" -- set by "-q"
+end
+
+------------------------------------------------------------------------
+
+if #arg == 0 then
+ whoami()
+ usage()
+ os.exit(0)
+end
+
+-- Logging on `musixtex.log` everything that is printed, all os calls,
+-- and warnings from other log files.
+
+musixlog = io.open("musixtex.log","w")
+if not musixlog then
+ print"!! Can't open files for writing in current directory, aborting"
+ os.exit(1)
+end
+
+------------------------------------------------------------------------
+
+print = function(...)
+ orig_print(...)
+ musixlog:write(...,"\n") -- only the first argument gets written!
+end
+
+local orig_remove = os.remove
+remove = function(filename)
+ if exists(filename,"nolog") then
+ musixlog:write(" removing ",filename,"\n")
+ return orig_remove(filename)
+ end
+end
+
+local orig_execute = os.execute
+execute = function(command)
+ musixlog:write(" ",command,"\n")
+ return orig_execute(command .. quiet)
+end
+
+function report_warnings(filename,pattern)
+ local log = io.open(filename)
+ if not log then
+ print("! No file "..filename)
+ return
+ end
+ for line in log:lines() do
+ if line:match(pattern) then
+ musixlog:write("! "..line,"\n")
+ end
+ end
+end
+
+function report_error(filename)
+ local log = io.open(filename)
+ if not log then
+ print("! No file "..filename)
+ return
+ end
+ local trigger = false
+ for line in log:lines() do
+ if trigger and line:match"^!" then
+ -- report just the first error
+ break
+ end
+ trigger = trigger or line:match"^!"
+ if trigger then
+ io.stderr:write("! "..line,"\n")
+ end
+ end
+end
+
+function process_option(this_arg)
+ if this_arg == "-v" or this_arg == "-version" or this_arg == "--version" then
+ os.exit(0)
+ elseif this_arg == "-h" or this_arg == "-help" or this_arg == "--help" then
+ usage()
+ os.exit(0)
+ elseif this_arg == "-l" then
+ latex = true
+ override = override .. 'l'
+ elseif this_arg == "-p" then
+ dvi = nil
+ override = override .. 'p'
+ elseif this_arg == "-d" then
+ dvi = "dvipdfm"; ps2pdf = nil
+ elseif this_arg == "-D" then
+ narg = narg+1
+ dvi = arg[narg]
+ elseif this_arg == "-c" then
+ pmx = "pmxchords"
+ elseif this_arg == "-F" then
+ narg = narg+1
+ tex = arg[narg]
+ force_engine = true
+ elseif this_arg == "-s" then
+ dvi = false; ps2pdf = nil; protect.dvi = true
+ elseif this_arg == "-g" then
+ dvi = dvips; ps2pdf = nil; protect.ps = true
+ elseif this_arg == "-i" then
+ cleanup = false
+ elseif this_arg == "-x" then
+ index = true
+ elseif this_arg == "-1" then
+ passes = 1
+ elseif this_arg == "-f" then
+ defaults()
+ elseif this_arg == "-t" then
+ tex, dvi, ps2pdf = nil,nil,nil
+ protect.tex = true
+ elseif this_arg == "-m" then
+ pmx, tex, dvi, ps2pdf = nil,nil,nil,nil
+ protect.pmx = true
+ elseif this_arg == "-M" then
+ narg = narg+1
+ prepmx = arg[narg]
+ elseif this_arg == "-A" then
+ narg = narg+1
+ autosp = arg[narg]
+ elseif this_arg == "-L" then
+ narg = narg+1
+ xml2pmx = arg[narg]
+ elseif this_arg == "-q" then
+ if not tempname then
+ tempname = tempname or os.tmpname()
+ print("Redirecting screen output to "..tempname)
+ end
+ if dvi == dvips then dvi = dvi .. " -q" end
+ quiet = " >> " .. tempname
+ elseif this_arg == "-P" then
+ narg = narg+1
+ ps2pdf = arg[narg]
+ elseif this_arg == "-X" then
+ narg = narg+1
+ pmx = arg[narg]
+ else
+ print("! Unknown option "..this_arg.." ignored")
+ end
+end
+
+function find_file(this_arg)
+ basename, extension = this_arg:match"(.*)%.(.*)"
+ extensions = {["xml"] = true, ["mtx"] = true, ["pmx"] = true, ["aspc"] = true, ["tex"] = true, ["ltx"] = true}
+ if extensions[extension] then
+ return basename, extension
+ end
+ basename, extension = this_arg, null
+ for ext in ("xml,mtx,pmx,aspc,tex,ltx"):gmatch"[^,]+" do
+ if exists (basename .. "." .. ext) then
+ extension = ext
+ break
+ end
+ end
+ if extension == null then
+ print("!! No file " .. basename .. ".[xml|mtx|pmx|aspc|tex|ltx]")
+ exit_code = exit_code+1
+ return
+ end
+ return basename, extension
+end
+
+function preprocess(basename,extension)
+ if not (basename and extension) then return end
+ if extension == "xml" then
+ if execute(xml2pmx .. " " .. basename .. ".xml" .. " " .. basename .. ".pmx" ) == 0 then
+ extension = "pmx"
+ else
+ print ("!! xml2pmx preprocessing of " .. basename .. ".xml fails.")
+ return
+ end
+ elseif extension == "mtx" then
+ if execute(prepmx .. " " .. basename ) == 0 then
+ extension = "pmx"
+ else
+ print ("!! prepmx preprocessing of " .. basename .. ".mtx fails.")
+ return
+ end
+ end
+ if extension == "pmx" then
+ local OK = true
+ if pmx then
+ local code = execute(pmx .. " " .. basename)
+ local pmxaerr = io.open("pmxaerr.dat", "r")
+ if (not pmxaerr) then
+ OK = false
+ print("!! No pmx log file.")
+ else
+ extension = "tex"
+ local linebuf = pmxaerr:read()
+ local err = tonumber(linebuf)
+ if (code == 0) then
+ if err ~=0 then
+ print ("! pmx produced a warning on line "..err..
+ " of "..basename..".pmx")
+ end
+ else
+ OK = false
+ print ("!! pmx processing of " .. basename .. ".pmx fails.")
+ end
+ pmxaerr:close()
+ end
+ end
+ if not OK then
+ exit_code = exit_code+1
+ return
+ end
+ end
+ if extension == "aspc" then
+ if execute (autosp .. " " .. basename .. ".aspc" ) == 0 then
+ if exists ( basename .. ".ltx")
+ then extension = "ltx"
+ else extension = "tex"
+ end
+ else
+ print ("!! autosp preprocessing of " .. basename .. ".aspc fails.")
+ exit_code = exit_code+1
+ return
+ end
+ end
+ return extension
+end
+
+function tex_process(tex,basename,extension)
+ if not (extension == "tex" or extension == "ltx") or not tex then return end
+ remove(basename .. ".mx1")
+ remove(basename .. ".mx2")
+ local filename = basename .. "." ..extension
+-- .ltx extension re-selects engine only for the current file, and only
+-- if default processing is plain TeX
+ local latex = latex
+ if extension == "ltx" then
+ if not force_engine and not latex then
+ if dvi then tex = "latex" else tex = "pdflatex" end
+ end
+ latex = true
+ end
+ if quiet ~= "" then
+ tex = tex .. " -interaction batchmode"
+ end
+ local OK = (execute(tex .. " " .. filename) == 0)
+ if passes ~= 1 then
+ OK = OK and (execute(musixflx .. " " .. basename) == 0)
+ and (execute(tex .. " " .. filename) == 0)
+ if latex and index then
+ OK = OK and (execute("makeindex -q " .. basename) == 0)
+ and (execute(tex .. " " .. filename) == 0)
+ if exists (basename .. ".toc","nolog") then
+ -- an extra line for the index will have been added
+ OK = OK and (execute(tex .. " " .. filename) == 0)
+ -- there is a tiny possibility that the extra line for the index
+ -- has changed the pagination. If you feel this should not be
+ -- ignored, here are the possibilities:
+ -- a. Do an extra TeX pass regardless.
+ -- b. Provide a user option -4 to force the extra pass.
+ -- c. Compare aux files to find out.
+ end
+ end
+ end
+ if (quiet ~= "") and not OK then
+ report_error(basename..".log")
+ end
+ if OK and latex then
+ report_warnings(basename..".log","LaTeX.*Warning")
+ end
+ if dvi then OK = OK and (execute(dvi .. " " .. basename) == 0) end
+ if dvi and ps2pdf then
+ OK = OK and (execute(ps2pdf .. " " .. basename .. ".ps") == 0)
+ if OK then
+ print(basename .. ".pdf generated by " .. ps2pdf .. ".")
+ end
+ end
+ if not OK then
+ print("!! Processing of " .. filename .. " fails.\n")
+ exit_code = exit_code+1
+ end
+end
+
+---- Report version information on musixtex.log
+
+-- File names and message signatures to be looked for in a TeX log file.
+logtargets = {
+mtxtex = {"mtx%.tex","mtxTeX"},
+mtxlatex = {"mtxlatex%.sty","mtxLaTeX"},
+pmxtex = {"pmx%.tex","PMX"},
+musixtex = {"musixtex%.tex","MusiXTeX"},
+musixltx = {"musixltx%.tex","MusiXLaTeX"},
+musixlyr = {"musixlyr%.tex","MusiXLYR"}
+}
+
+-- Signatures of messages displayed on standard output by programs
+capturetargets = {
+MTx = "This is (M%-Tx.->)",
+PMX = "This is (PMX[^\n]+)",
+pdftex = "This is (pdfTeX[^\n]+)",
+musixflx = "Musixflx%S+",
+autosp = "This is autosp.*$",
+index = "autosp,MTx,PMX,musixflx,pdftex"
+}
+
+function report_texfiles(logname)
+ local log = logname and io.open(logname)
+ if not log then return end
+ local lines =
+ {"--- TeX files actually included according to "..logname.." ---"}
+ log = log:read"*a"
+-- The following pattern matches filenames input by TeX, even if
+-- interrupted by a line break. It may include the first word of
+-- an \immediate\write10 message emitted by the file.
+ for pos,filename in log:gmatch"%(()(%.?[/]%a[%-/%.%w\n]+)" do
+ local hit
+ repeat
+ local oldfilename = filename
+ filename = oldfilename:match"[^\n]+" -- up to next line break
+ hit = io.open(filename) -- success if the file exists
+ if hit then break end
+ filename = oldfilename:gsub("\n","",1) -- remove line break
+ until filename==oldfilename
+ if hit then
+ for target,sig in pairs(logtargets) do
+ if filename:match(sig[1]) then
+ local i,j = log:find(sig[2].."[^\n]+",pos)
+ if j then lines[#lines+1] = filename.."\n "..log:sub(i,j) end
+ end
+ end
+ end
+ end
+ return table.concat(lines,'\n').."\n"
+end
+
+function report_versions(tempname)
+ if not tempname then return end -- only available with -q
+ local logs = io.open(tempname)
+ if not logs then
+ musixlog:write ("No version information: could not open "..tempname)
+ return
+ end
+ local versions = {}
+ musixlog:write("--- Programs actually executed according to "..tempname.." ---\n")
+ for line in logs:lines() do
+ for target in capturetargets.index:gmatch"[^,]+" do
+ if not versions[target] then
+ local found = line:match(capturetargets[target])
+ if found then
+ versions[target] = found
+ musixlog:write(found,"\n")
+ end
+ end
+ end
+ end
+ logs:close()
+ return
+end
+
+------------------------------------------------------------------------
+
+whoami()
+
+defaults()
+
+exit_code = 0
+narg = 1
+protect = {} -- extensions not to be deleted, even if cleanup requested
+
+repeat
+ this_arg = arg[narg]
+ if this_arg:match"^%-" then process_option(this_arg)
+ else
+ basename, extension = find_file(this_arg) -- nil,nil if not found
+ if tex then -- tex output enabled, now select engine
+ if tex:match"pdf" then dvi = nil end
+ if not dvi then ps2pdf = nil end
+ -- .ltx extension will be taken into account later, in `process`
+ -- deduce tex/latex from current engine name if -l is not specified
+ if not override:match"l" then latex = tex:match"latex" end
+ if not force_engine then -- select appropriate default engine
+ if latex then
+ if dvi==nil then tex = "pdflatex" else tex = "latex" end
+ else
+ if dvi==nil then tex = "pdfetex" else tex = "etex" end
+ end
+ end
+ end
+ extension = preprocess(basename, extension)
+ tex_process(tex,basename,extension)
+ if basename and io.open(basename..".log") then -- to be printed later
+ versions = report_texfiles(basename..".log")
+ end
+ if basename and cleanup then
+ remove("pmxaerr.dat")
+ for ext in ("mx1,mx2,dvi,ps,idx,log,ilg,pml,pmxprep"):gmatch"[^,]+" do
+ if not protect[ext] then remove(basename.."."..ext)
+ end
+ end
+ end
+ protect = {}
+ end
+ narg = narg+1
+until narg > #arg
+
+if versions then musixlog:write(versions) end
+report_versions(tempname)
+musixlog:close()
+os.exit( exit_code )