summaryrefslogtreecommitdiffstats
path: root/contrib/glilypond
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/glilypond')
-rw-r--r--contrib/glilypond/ChangeLog290
-rw-r--r--contrib/glilypond/ChangeLog.0x104
-rw-r--r--contrib/glilypond/README.txt43
-rw-r--r--contrib/glilypond/examples/example.groff46
-rw-r--r--contrib/glilypond/glilypond.1.man881
-rw-r--r--contrib/glilypond/glilypond.am64
-rwxr-xr-xcontrib/glilypond/glilypond.pl1907
7 files changed, 3335 insertions, 0 deletions
diff --git a/contrib/glilypond/ChangeLog b/contrib/glilypond/ChangeLog
new file mode 100644
index 0000000..c218610
--- /dev/null
+++ b/contrib/glilypond/ChangeLog
@@ -0,0 +1,290 @@
+2022-10-19 G. Branden Robinson <g.branden.robinson@gmail.com>
+
+ * glilypond.pl (version): Report version information in format
+ recommended by GNU coding standards. Bump micro version number
+ to reflect this and the restructuring immediately previous.
+
+2022-10-19 G. Branden Robinson <g.branden.robinson@gmail.com>
+
+ Make glilypond script stand alone.
+
+ * args.pl:
+ * oop_fh.pl
+ * subs.pl: Delete, moving their content into...
+ * glilypond.pl: ...here. Also bump overall license to GPLv3
+ from GPLv2 because all of the deleted files were GPLv3.
+ * glilypond.am (dist_glilypond_DATA): Delete.
+
+2022-05-03 G. Branden Robinson <g.branden.robinson@gmail.com>
+
+ * glilypond.am (glilypond): Spell dependency on
+ `$(SH_DEPS_SED_SCRIPT)` using that macro expansion instead of a
+ literal file name. See groff's doc/automake.mom.
+
+2021-01-06 Colin Watson <cjwatson@debian.org>
+
+ * glilypond.pl: Avoid Perl's unsafe "<>" operator.
+
+ The "<>" operator is implemented using the two-argument form of
+ "open", which interprets magic such as pipe characters, allowing
+ execution of arbitrary commands which is unlikely to be
+ expected. Perl >= 5.22 has a "<<>>" operator which avoids this,
+ but also forbids the use of "-" to mean the standard input,
+ which is a facility that the affected groff programs document.
+
+ ARGV::readonly would probably also fix this, but I fundamentally
+ dislike the approach of escaping data in preparation for a
+ language facility to unescape it, especially when the required
+ escaping is as non-obvious as it is here. (For the same reason,
+ I prefer to use subprocess invocation facilities that allow
+ passing the argument list as a list rather than as a string to
+ be interpreted by the shell.) So I've abandoned this dubious
+ convenience and changed the affected programs to iterate over
+ command-line arguments manually using the three-argument form of
+ open.
+
+ glilypond doesn't need the initial unshift since that's already
+ handled in args.pl.
+
+ Fixes <https://savannah.gnu.org/bugs/?55557>.
+
+2020-04-22 G. Branden Robinson <g.branden.robinson@gmail.com>
+
+ * glilypond.1.man: Delete references to groffer.
+
+2018-02-28 Werner LEMBERG <wl@gnu.org>
+
+ * glilypond.am (glilypond): Use $(AM_V_GEN) to silence file generation.
+
+2017-10-22 G. Branden Robinson <g.branden.robinson@gmail.com>
+
+ * args.pl: Fix grammar in usage message.
+
+ When used attributively, e.g. as an adjectival phrase,
+ "command-line" should be hyphenated.
+
+2015-09-10 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.pl, args.pl, subs.pl: New default `eps_func' as `pdf'.
+
+2015-09-10 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.1.man: New default `pdf2eps'. Several fixes.
+
+ * subs.pl: Replace `.PSPIC' by `$P_PIC'. Set new default sub on
+ top.
+
+2015-08-22 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.1.man: Rename `glilypond.man'.
+
+ * glilypond.am: Include renaming.
+
+2015-08-05 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.am: Add `Last update'. Setup Emacs mode.
+
+2015-04-03 Werner LEMBERG <wl@gnu.org>
+
+ * glilypond.man: Make it work in compatibility mode.
+ (EL): Fix typo.
+
+2015-03-20 Ralph Corderoy <ralph@inputplus.co.uk>
+
+ * glilypond.pl: Minor syntax fixes.
+
+2015-03-20 Werner LEMBERG <wl@gnu.org>
+
+ * glilypond.pl <read files or stdin>: Fix typo.
+
+ Problem reported by Grégoire Babey <gibux@gmx.ch>.
+
+2014-09-03 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.pl: New version 1.3.1
+
+ * all `glilypond' files: Copying and Emacs setting.
+
+2014-07-06 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.pl: New version 1.3
+
+ * glilypond.man: Make man-page compatible with doclifter.
+
+2014-07-04 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.man: Transform to classical man-page style.
+
+2014-07-03 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.man: Improve definitions.
+
+2014-03-30 Steffen Nurpmeso <sdaoden@yandex.com>
+
+ * Makefile.sub: Put straight error-prevention prefixes for `rm'.
+
+2014-03-30 Steffen Nurpmeso <sdaoden@yandex.com>
+
+ * Makefile.sub (uninstall_sub): Typo.
+
+2014-03-11 Ingo Schwarze <schwarze@openbsd.org> (tiny change)
+
+ * Makefile.sub (install_data): POSIX conformance.
+
+ Do not use $< outside inference rules, and even less when there
+ are multiple targets.
+
+2014-02-14 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * examples/example.groff: Add this directory and this file.
+
+2014-01-06 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ Remove archive git@github.com:RUNOFF/groff_lilypond.git
+
+2013-10-30 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.man: Correct writing.
+
+2013-05-10 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.pl: Correct position information. Add debug code.
+
+ * args.pl, oop_fh.pl, subs.pl: Correct position information.
+
+2013-04-25 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * Makefile.sub: minor corrections.
+
+2013-04-24 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ Public `glilypond' version `v1.1'.
+
+ * args.pl, sub.pl, glilypond.man: Change option `-v' to mean
+ `--verbose' instead of former `--version' such as many GNU
+ programs do. Correct sub `&usage()' and man-page.
+
+ * args.pl, glilypond.pl, oop_fh.pl, subs.pl: Remove spaces in
+ ` -> ', some `( ... )', and some `{ ... }' places for better
+ readability of the Perl source code.
+
+2013-04-24 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * args.pl, oop_fh.pl: Remove 1st line calling `perl'.
+
+ * subs.pl: Remove 1st line calling `perl'. Remove sub
+ `&perl_version()'. Adjust sub `&usage()'.
+
+ * glilypond.pl: Keep 1st line, which will be reset by running
+ `make'. Remove all parts of Perl testing.
+
+ * perl_test.pl: Remove this file.
+
+ * README.txt: Add information about needed Perl version.
+
+ * Makefile.sub: Corrections for removing Perl test. Use `$<'.
+
+2013-04-24 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * Makefile.sub: Remove Perl test.
+
+2013-04-12 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * glilypond.pl: Fix END for early exit of `--version'.
+
+2013-04-12 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * subs.pl: Replace `state' by global variable. So the Perl
+ version can be older.
+
+ * perl_test.pl: Replace the Perl version by `v5.6', analogously to
+ `groffer'.
+
+2013-04-11 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * Makefile.sub: Corrections for Emacs.
+
+2013-04-11 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * old groff_lilypond: There is now a free `git' package containing
+ all old versions of the former name `groff_lilypond v0.*'. They
+ work with `lilypond' parts in `roff' files, but were not
+ installed. This package can be got at:
+
+ $ git clone git@github.com:RUNOFF/groff_lilypond.git
+
+ The new versions `glilypond 1.*' are not included there.
+
+2013-03-29 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ Published version is `v1.0'.
+
+ Run `autoconf' again.
+
+2013-03-29 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * <groff_src_dir>/m4/groff.m4, <groff_src_dir>/configure.ac: Add
+ libdir information for `glilypond'.
+
+ * <groff_src_dir>/Makefile.in: Add
+ `<groff_src_dir>/contrib/glilypond'.
+
+ Run `autoconf'.
+
+ `glilypond' can now be installed to the system.
+
+2013-03-29 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ Rename `groff_lilypond' to `glilypond'. So remove the former
+ source directory `<groff_src_dir>/contrib/lilypond' and newly
+ install `<groff_src_dir>/contrib/glilypond', which now has many
+ files. The new version starts at `v1.0'.
+
+ Version will now be v1.*. All former files of versions v0.*
+ vanished or were renamed. This is not yet an information about
+ publishing.
+
+ * ChangeLog.0x: old `ChangeLog' file for the old `groff_lilypond'
+ versions v0.*. In the future, this file won't be changed any
+ more.
+
+ * ChangeLog: New file. It is this file. It displays the history
+ of `glilypond' versions v1.* or later.
+
+ * glilypond.pl: New main Perl file written from
+ `groff_lilypond.pl' in a totally different way. It is split now
+ into 4 Perl files.
+
+ * args.pl: New Perl file. It handles the command line options for
+ a run of `glilypond.pl'.
+
+ * oop_fh.pl: New Perl file. OOP handling of file handles.
+
+ * perl_test.pl: Test whether the actual Perl program has a
+ suitable versions. For `Makefile.sub' and `glilypond.pl'.
+
+ * subs.pl: New Perl file. Defines the global subs for
+ `glilypond.pl'.
+
+ * Makefile.sub: Newly written `Makefile' for this subdirectory of
+ `groff'. `glilypond' should be able to be installed by `make'
+ with this file.
+
+ * glilypond.man: Newly written man-page for `glilypond'.
+
+ * README.txt: New file about the installation.
+
+########################################################################
+
+Copyright 2013-2020 Free Software Foundation, Inc.
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.
+
+Local Variables:
+fill-column: 72
+mode: change-log
+version-control: never
+End:
+vim:set autoindent textwidth=72:
diff --git a/contrib/glilypond/ChangeLog.0x b/contrib/glilypond/ChangeLog.0x
new file mode 100644
index 0000000..b1dc833
--- /dev/null
+++ b/contrib/glilypond/ChangeLog.0x
@@ -0,0 +1,104 @@
+2013-04-11 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * old groff_lilypond: There is now a free `git' package containing
+ all old versions of the former name `groff_lilypond v0.*'. They
+ work with `lilypond' parts in `roff' files, but were not
+ installed. This package can be got at:
+
+ $ git clone git@github.com:RUNOFF/groff_lilypond.git
+
+ The new versions `glilypond 1.*' are not included there.
+
+2013-03-28 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ Rename `groff_lilypond' to `glilypond' for *.pl and *.man files.
+ Split the single Perl script into several minor *.pl files.
+ Construct `Makefile.sub' in order to have installed `glilypond'.
+ Public versions will be v1.x.
+ Rename `ChangeLog' to `ChangeLog.0x' for the old versions of
+ `groff_lilypond' v0.*. New `ChangeLog' for the new versions
+ `glilypond' 1.*.
+
+ * groff_lilypond.pl: Vanished. Renamed to `glilypond.pl'.
+ * groff_lilypond.man: Vanished. Renamed to `glilypond.man'.
+ * ChangeLog.0x: Moved from `ChangeLog'. It is this file. It
+ describes only the old `groff_lilypond' versions v0.*. See file
+ `ChangeLog' for the files of the new `glilypond' v1.*.
+
+2013-03-11 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * groff_lilypond.pl: Publishing groff_lilypond version v0.6.
+ New options: -e|--eps_dir, -l|--license, -k|--keep_files,
+ -p|--prefix=..., -t|--temp_dir=...
+ Install --eps_dir as directory for the useful EPS files.
+ * groff_lilypond.man: Include the new options. Add section
+ SEE ALSO.
+
+2013-03-03 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * groff_lilypond.pl: New code with Perl references.
+ Publishing groff_lilypond version v0.5.
+ New options: --usage, -V|--Verbose|--verbose, --file_prefix=...,
+ -o|--output=..., --temp_dir=...
+ Perl >=5.10.0 needed.
+ * groff_lilypond.man: Include the new options.
+
+2013-02-23 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * groff_lilypond_pl: Remove `.lilypond include' for lilypond
+ regions. Within `groff' mode. it is still allowed.
+ * groff_lilypond.man: Update `.lilypond include'.
+
+2013-02-23 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ New version v0.4 of groff_lilypond.
+ * groff_lilypond_pl: Major rewrite.
+ New options: --file_prefix, --temp_dir, and --license.
+ * groff_lilypond.man: documents the new features.
+
+2013-02-16 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * groff_lilypond.man: Minor corrections.
+
+2013-02-12 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * groff_lilypond.pl: Add deletion of unused temporary files.
+
+2013-02-12 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * contrib/lilypond: Version v0.3 for groff_lilypond
+ * groff_lilypond.pl: A new request was added for importing
+ lilypond files:
+ .lilypond include ...
+ The argument handling was improved.
+
+2013-02-11 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * contrib/lilypond: Version v0.2 for groff_lilypond
+ * groff_lilypond.pl: Now there are 2 modes for generationg the EPS
+ files: --ly2eps (the default) and --pdf2eps (that's the old mode
+ from version v0.1 with pdf to ps to eps). With ly2eps mode,
+ lilypond generates the EPS files itself, for each page one EPS
+ file.
+ * groff_lilypond.man: Corresponding updated man-page
+
+2013-02-10 Bernd Warken <groff-bernd.warken-72@web.de>
+
+ * contrib/lilypond: New files for adding lilypond parts into groff
+ files. These files will not yet be installed.
+ * groff_lilypond.pl: Program written in Perl, version v0.1
+ * groff_lilypond.man: Corresponding man-page
+
+Copyright 2013-2020 Free Software Foundation, Inc.
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.
+
+Local Variables:
+coding: utf-8
+fill-column: 72
+mode: change-log
+version-control: never
+End:
+vim:set autoindent textwidth=72:
diff --git a/contrib/glilypond/README.txt b/contrib/glilypond/README.txt
new file mode 100644
index 0000000..f47e824
--- /dev/null
+++ b/contrib/glilypond/README.txt
@@ -0,0 +1,43 @@
+ Copyright (C) 2013-2020 Free Software Foundation, Inc.
+
+ Written by Bernd Warken <groff-bernd.warken-72@web.de>
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved.
+
+ This file is part of 'glilypond', which is part of 'groff'.
+
+
+########################################################################
+
+In order to run 'glilypond', your system must have installed Perl of at
+least version 'v5.6'.
+
+
+########################################################################
+
+In order to have this program installed by 'make', the creation of a
+libdir (library directory) must be programmed in some system files.
+The following actions must be taken:
+
+1) <groff_src_dir>/m4/groff.m4:
+Add 'AC_DEFUN([GROFF_GROFFERDIR_DEFAULT])'.
+
+2) <groff_src_dir>/configure.ac:
+Add 'GROFF_GROFFERDIR_DEFAULT'.
+
+3) <groff_src_dir>/Makefile.in:
+Add several information of 'glilypond_dir'
+
+With that, the program 'autoconf' can be run in order to update the
+configure files and Makefile's.
+
+Now '$glilypond_dir' can be used as libdir.
+
+##### Editor settings
+Local Variables:
+fill-column: 72
+mode: text
+End:
+vim: set textwidth=72:
diff --git a/contrib/glilypond/examples/example.groff b/contrib/glilypond/examples/example.groff
new file mode 100644
index 0000000..856474e
--- /dev/null
+++ b/contrib/glilypond/examples/example.groff
@@ -0,0 +1,46 @@
+.\" --------------------------------------------------------------------
+.\" Legalese
+.\" --------------------------------------------------------------------
+.
+.ig
+glilypond - integrate 'lilypond' parts into 'groff' files
+
+This file was written by Bernd Warken <groff\-bernd.warken\-72@web.de>.
+.
+Copyright (C) 2013-2020 Free Software Foundation, Inc.
+
+This file is part of glilypond, which is part of GNU groff, a free
+software project.
+
+You can redistribute it and/or modify it under the terms of the GNU
+General Public License version 2 as published by the Free Software
+Foundation.
+
+The license text is available in the internet at
+.UR http://\%www.gnu.org/\%licenses/\%gpl-2.0.html
+.UE .
+..
+.
+.\" --------------------------------------------------------------------
+.\" Groff Part
+.\" --------------------------------------------------------------------
+.
+before
+.lilypond start
+\version "2.14.2"
+\relative c' {
+ \key c \minor
+ c d e f
+ g(
+ <ees c'>)
+ <d f gis b>-.
+ <ees g bes>-.
+}
+\paper {
+ oddHeaderMarkup = #f
+ evenHeaderMarkup = #f
+ oddFooterMarkup = #f
+ evenFooterMarkup = #f
+}
+.lilypond end
+after
diff --git a/contrib/glilypond/glilypond.1.man b/contrib/glilypond/glilypond.1.man
new file mode 100644
index 0000000..a81174e
--- /dev/null
+++ b/contrib/glilypond/glilypond.1.man
@@ -0,0 +1,881 @@
+.TH glilypond @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+glilypond \- embed LilyPond musical notation in
+.I groff
+documents
+.
+.
+.\" TODO: This page needs a thorough edit by a fluent English speaker.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 2013-2020 Free Software Foundation, Inc.
+.\"
+.\" This file is part of glilypond, which is part of GNU groff, a free
+.\" software project.
+.\"
+.\" You can redistribute it and/or modify it under the terms of the GNU
+.\" General Public License version 2 (GPL2) as published by the Free
+.\" Software Foundation.
+.\"
+.\" The license text is available in the internet at
+.\" <http://www.gnu.org/licenses/gpl-2.0.html>.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_glilypond_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY glilypond
+.RB [ \-k ]
+.RB [{ \-\-ly2eps | \-\-pdf2eps }]
+.RB [ \-e
+.IR directory ]
+.RB [ \-o
+.IR output-file ]
+.RB [ \-p
+.IR filename-prefix ]
+.RB [ \-t
+.IR tdir ]
+.RB [{ \-v | \-V }]
+.RB [ \-\- ]
+.RI [ file\~ .\|.\|.]
+.
+.
+.SY glilypond
+.RB [{ \-\-ly2eps | \-\-pdf2eps }]
+.RB [ \-\-eps_dir
+.IR directory ]
+.RB [ \-\-keep_all ]
+.RB [ \-\-output
+.IR output-file ]
+.RB [ \-\-prefix
+.IR filename-prefix ]
+.RB [ \-\-temp_dir
+.IR tdir ]
+.RB [ \-\-verbose ]
+.RB [ \-\- ]
+.RI [ file\~ .\|.\|.]
+.YS
+.
+.
+.SY glilypond
+.B \-?
+.SY glilypond
+.B \-h
+.SY glilypond
+.B \-\-help
+.SY glilypond
+.B \-\-usage
+.YS
+.
+.
+.SY glilypond
+.B \-l
+.SY glilypond
+.B \-\-license
+.YS
+.
+.
+.SY glilypond
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I glilypond
+is a
+.MR groff @MAN7EXT@
+preprocessor that enables the embedding of LilyPond music scores in
+.I groff
+documents.
+.\".
+.\".B .PDFPIC
+.\"is available, but does not yet work with lilypond.
+.
+If no operands are given,
+or if
+.I file
+is
+.RB \[lq] \- \[rq],
+.I glilypond
+reads the standard input stream.
+.
+A double-dash argument
+.RB (\[lq] \-\- \[rq])
+causes all subsequent arguments to be interpreted as
+.I file
+operands,
+even if their names start with a dash.
+.
+.
+.\" ====================================================================
+.SH Usage
+.\" ====================================================================
+.
+At present,
+.I glilypond
+works with the
+.I groff
+.BR ps ,
+.BR dvi ,
+.BR html ,
+and
+.B xhtml
+devices.
+.
+The
+.B lbp
+and
+.B lj4
+devices are untested.
+.
+Unfortunately,
+the
+.B pdf
+device does not yet work.
+.
+.
+.\" ====================================================================
+.SH "Option overview"
+.\" ====================================================================
+.
+.
+.TP
+.BR \-? | \-h | \-\-help | \-\-usage
+Display usage information and exit.
+.
+.TP
+.B \-\-version
+Display version information and exit.
+.
+.TP
+.BR \-l | \-\-license
+Display copyright license information and exit.
+.
+.
+.\" ====================================================================
+.SS "Options for building EPS files"
+.\" ====================================================================
+.
+.TP
+.B \-\-ly2eps
+Direct
+.MR lilypond 1
+to create Encapsulated PostScript (EPS) files.
+.
+This is the default.
+.
+.
+.TP
+.B \-\-pdf2eps
+The program
+.I glilypond
+generates a PDF file using
+.IR lilypond .
+.
+Then the EPS file is generated by
+.I pdf2ps
+and
+.IR ps2eps .
+.
+.
+.\" ====================================================================
+.SS "Directories and files"
+.\" ====================================================================
+.
+.TP
+.BR \-e | \-\-eps_dir "\fI directory_name\fP"
+Normally all
+.I EPS
+files are sent to the temporary directory.
+.
+With this option,
+you can generate your own directory,
+in which all useful
+.I EPS
+files are send.
+.
+So at last, the temporary directory can be removed.
+.
+.
+.TP
+.BR \-p | \-\-prefix "\fI begin_of_name\fP"
+Normally all temporary files get names that start with the
+.BI ly .\|.\|.\&
+prefix.
+.
+With this option, you can freely change this prefix.
+.
+.
+.TP
+.BR \-k | \-\-keep_all
+Normally all temporary files without the
+.I eps
+files are deleted.
+.
+With this option, all generated files either by the
+.I lilypond
+program or other format transposers are kept.
+.
+.
+.TP
+.BR \-t | \-\-temp_dir "\fI dir\fP"
+With this option, you call a directory that is the base for the
+temporary directory.
+.
+This directory name is used as is without any extensions.
+.
+If this directory does not exist it is be created.
+.
+The temporary directory is created by Perl's security operations
+directly under this directory.
+.
+In this temporary directory, the temporary files are stored.
+.
+.
+.\" ====================================================================
+.SS Output
+.\" ====================================================================
+.
+.TP
+.BR \-o | \-\-output "\fI file_name\fP"
+Normally all
+.I groff
+output of this program is sent to
+.BR STDOUT .
+.
+With this option, that can be changed, such that the output is stored
+into a file named in the option argument
+.IR file_name .
+.
+.
+.TP
+.BR \-v | \-V | \-\-verbose
+A lot more of information is sent to STDERR.
+.
+.
+.\" ====================================================================
+.SS "Short option collections"
+.\" ====================================================================
+.
+The argument handling of options
+.
+.
+.P
+.I "Short options"
+are arguments that start with a single dash
+.BR \- .
+.
+Such an argument can consist of arbitrary many options without option
+argument, composed as a collection of option characters following the
+single dash.
+.
+.
+.P
+Such a collection can be terminated by an option character that
+expects an option argument.
+.
+If this option character is not the last character of the argument,
+the following final part of the argument is the option argument.
+.
+If it is the last character of the argument, the next argument is
+taken as the option argument.
+.
+.
+.P
+This is the standard for
+.I POSIX
+and
+.I GNU
+option management.
+.
+.
+.P
+For example,
+.
+.TP
+.BI \-kVe " some_dir"
+is a collection of the short options
+.B \-k
+and
+.B \-V
+without option argument, followed by the short option
+.B \-e
+with option argument that is the following part of the argument
+.IR some_dir .
+.
+So this argument could also be written as several arguments
+.B \-k \-V \-e
+.IR some_dir .
+.
+.
+.\" ====================================================================
+.SS "Handling of long options"
+.\" ====================================================================
+.
+Arguments that start with a double dash
+.B \-\-
+are so-called
+.I "long options" R .
+.
+Each double dash argument can only have a single long option.
+.
+.
+.P
+.I "Long options"
+have or have not an option argument.
+.
+An option argument can be the next argument or can be appended with an
+equal sign
+.B =
+to the same argument as the long option.
+.
+.
+.TP
+.B \-\-help
+is a long option without an option argument.
+.
+.TP
+.BI \-\-eps_dir " some_dir"
+.TQ
+.BI \-\-eps_dir= some_dir
+is the long option
+.B \-\-eps_dir
+with the option argument
+.IR some_dir .
+.
+.
+.P
+Moreover the program allows abbreviations of long options, as much as
+possible.
+.
+.
+.P
+The
+.I "long option"
+.B \-\-keep_all
+can be abbreviated from
+.B \-\-keep_al
+up to
+.B \-\-k
+because the program does not have another
+.I "long option"
+whose name starts with the character
+.BR k .
+.
+.
+.P
+On the other hand, the option
+.B \-\-version
+cannot be abbreviated further than
+.B \-\-vers
+because there is also the
+.I long option
+.B \-\-verbose
+that can be abbreviated up to
+.BR \-\-verb .
+.
+.
+.P
+An option argument can also be appended to an abbreviation.
+.
+So is
+.BI \-\-e= some_dir
+the same as
+.B \-\-eps_dir
+.IR some_dir .
+.
+.
+.P
+Moreover the program allows an arbitrary usage of upper and lower case
+in the option name.
+.
+This is
+.I Perl
+style.
+.
+.
+.P
+For example, the
+.I "long option"
+.B \-\-keep_all
+can as well be written as
+.B \-\-Keep_All
+or even as an abbreviation like
+.BR \-\-KeE .
+.
+.
+.br
+.ne 6v
+.\" ====================================================================
+.SH "LilyPond regions in \f[I]roff\f[] input"
+.\" ====================================================================
+.
+.\" ====================================================================
+.SS "Integrated LilyPond code"
+.\" ====================================================================
+.
+A
+.I lilypond
+part within a structure written in the
+.I groff
+language is the whole part between the marks
+.RS
+.EX
+.B ".lilypond start"
+.EE
+.RE
+and
+.RS
+.EX
+.B ".lilypond end"
+.EE
+.RE
+.
+A
+.I groff
+input can have several of these
+.I lilypond
+parts.
+.
+.
+.P
+When processing such a
+.I lilypond
+part between
+.B ".lilypond start"
+and
+.B ".lilypond end"
+we say that the
+.B glilypond
+program is in
+.IR "lilypond mode" .
+.
+.
+.P
+These
+.I lilypond
+parts are sent into temporary
+.I lilypond
+files with the file name extension
+.BR .ly .
+.
+These files are transformed later on into
+.I EPS
+files.
+.
+.
+.\" ====================================================================
+.SS "Inclusion of \f[I].ly\f[] files"
+.\" ====================================================================
+.
+An additional command line for file inclusion of
+.I lilypond
+files is given by
+.EX
+.BI ".lilypond include" " file_name"
+.EE
+in
+.I groff
+input.
+.
+For each such
+.I include
+command, one file of
+.I lilypond
+code can be included into the
+.I groff
+code.
+.
+Arbitrarily many of these commands can be included in the
+.I groff
+input.
+.
+.
+.P
+These include commands can only be used outside the
+.I lilypond
+parts.
+.
+Within the
+.IR "lilypond mode" ,
+this inclusion is not possible.
+.
+So
+.B ".lilypond include"
+may not be used in
+.IR "lilypond mode" ,
+i.e.\& between
+.B ".lilypond start"
+and
+.BR ".lilypond end" .
+.
+.
+These included
+.IR ly -files
+are also transformed into
+.I EPS
+files.
+.
+.
+.\" ====================================================================
+.SH "Generated files"
+.\" ====================================================================
+.
+By the transformation process of
+.I lilypond
+parts into
+.I EPS
+files, there are many files generated.
+.
+By default, these files are regarded as temporary files and as such
+stored in a temporary directory.
+.
+.
+.P
+This process can be changed by command-line options.
+.
+.
+.\" ====================================================================
+.SS "Command-line options for directories"
+.\" ====================================================================
+.
+The temporary directory for this program is either created
+automatically or can be named by the option
+.BR \-t | \-\-temp_dir
+.IR dir .
+.
+.
+.P
+Moreover, the
+.I EPS
+files that are later on referred by
+.B .PSPIC
+command in the final
+.I groff
+output can be stored in a different directory that can be set by the
+command-line option
+.BR \-e | \-\-eps_dir
+.IR directory_name .
+.
+With this option, the temporary directory can be removed completely at
+the end of the program.
+.
+.
+.P
+The beginning of the names of the temporary files can be set by the
+command-line options
+.B \-p
+or
+.BR \-\-prefix .
+.
+.
+.P
+All of the temporary files except the
+.I EPS
+files are deleted finally.
+.
+This can be changed by setting the command-line options
+.B \-k
+or
+.BR \-\-keep_files .
+.
+With this, all temporary files and directories are kept, not deleted.
+.
+.
+.P
+These
+.I EPS
+files are stored in a temporary or
+.I EPS
+directory.
+.
+But they cannot be deleted by the transformation process because they
+are needed for the display which can take a long time.
+.
+.
+.\" ====================================================================
+.SH "Transformation processes for generating EPS files"
+.\" ====================================================================
+.
+.\" ====================================================================
+.SS "Mode pdf2eps"
+.\" ====================================================================
+.
+This mode is the actual default and can also be chosen by the option
+.BR \-\-pdf2eps .
+.
+.
+.P
+In this mode, the
+.B .ly
+files are transformed by the
+.MR lilypond 1
+program into
+.I PDF
+files, using
+.RS
+.EX
+.BI "lilypond \-\-pdf \-\-output=" file-name
+.EE
+.RE
+for each
+.B .ly
+file.
+.
+The
+.I file-name
+must be provided without the extension
+.BR .pdf .
+.
+By this process, a file
+.IB file-name .pdf
+is generated.
+.
+.
+.P
+The next step is to transform these
+.I PDF
+files into a
+.I PS
+file.
+.
+This is done by the
+.MR pdf2ps 1
+program using
+.RS
+.EX
+$\~\c
+.B pdf2ps\~\c
+.IB file-name .pdf\~\c
+.IB file-name .pds
+.EE
+.RE
+.
+.
+The next step creates an
+.I EPS
+file from the
+.I PS
+file.
+.
+This is done by the
+.MR ps2eps 1
+program using
+.RS
+.EX
+.RB "$ " "ps2eps " \fIfile-name\fP ".ps"
+.EE
+.RE
+.
+.
+.P
+By that, a file
+.IB file-name .eps
+is created for each
+.I lilypond
+part in the
+.I groff
+file or standard input.
+.
+.
+.P
+The last step to be done is replacing all
+.I lilypond
+parts by the
+.I groff
+command
+.RS
+.EX
+.BI ".PSPIC " file-name .eps
+.EE
+.RE
+.
+.
+.\" ====================================================================
+.SS "Mode ly2eps"
+.\" ====================================================================
+.
+In earlier time, this mode was the default.
+.
+But now it does not work any more, so accept the new default
+.IR pdf2eps .
+.
+For testing, this mode can also be chosen by the
+.I glilypond
+option
+.BR \-\-ly2eps .
+.
+.
+.P
+In this mode, the
+.B .ly
+files are transformed by the
+.I lilypond
+program into many files of different formats, including
+.I eps
+files, using
+.RS
+.EX
+$\~\c
+.B lilypond \-\-ps \-dbackend=eps \-dgs\-load\-fonts \-\-output=\c
+.I file-name
+.EE
+.RE
+for each
+.B .ly
+file.
+.
+The output
+.I file\-name
+must be provided without an extension, its directory is temporary.
+.
+.
+.P
+There are many
+.I EPS
+files created.
+.
+One having the complete transformed
+.B ly
+file, named
+.IB file\-name .eps \fR.\fP
+.
+.
+.P
+Moreover there are
+.I EPS
+files for each page, named
+.IB file\-name \- digit .eps \fR.\fP
+.
+.
+.P
+The last step to be done is replacing all
+.I lilypond
+parts by the collection of the corresponding
+.I EPS
+page files.
+.
+This is done by
+.I groff
+commands
+.EX
+.BI ".PSPIC " file-name \- digit .eps
+.EE
+.
+.
+.\" ====================================================================
+.SH "Generated \f[I]groff\f[] output"
+.\" ====================================================================
+.
+The new
+.MR groff @MAN7EXT@
+structure generated by
+.I glilypond
+is either
+.
+.TP
+1)
+sent to standard output and can there be saved into a file or piped into
+.MR groff @MAN1EXT@
+or
+.
+.TP
+2)
+stored into a file by given the option
+.BR \-o\ \~| \~\-\-output
+.I file_name
+.
+.
+.\" ====================================================================
+.SH Authors
+.\" ====================================================================
+.
+.I glilypond
+was written by
+.MT groff\-bernd\:.warken\-72@\:web\:.de
+Bernd Warken
+.ME .
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.TP
+.MR groff @MAN1EXT@
+describes the usage of the
+.I groff
+command and contains pointers to further documentation of the
+.I groff
+system.
+.
+.
+.TP
+.MR groff_tmac @MAN5EXT@
+describes the
+.B .PSPIC
+request.
+.
+.
+.TP
+.MR lilypond 1
+briefly describes the
+.I lilypond
+command and contains pointers to further documentation.
+.
+.
+.TP
+.MR pdf2ps 1
+transforms a
+.I PDF
+file into a
+.I PostScript
+format.
+.
+.
+.TP
+.MR ps2eps 1
+transforms a
+.I PS
+file into an
+.I EPS
+format.
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_glilypond_1_man_C]
+.do rr *groff_glilypond_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/contrib/glilypond/glilypond.am b/contrib/glilypond/glilypond.am
new file mode 100644
index 0000000..d18049f
--- /dev/null
+++ b/contrib/glilypond/glilypond.am
@@ -0,0 +1,64 @@
+# Automake rules for 'glilypond'
+
+# Copyright (C) 2013-2020 Free Software Foundation, Inc.
+# Written by Werner Lemberg <wl@gnu.org> and
+# Bernd Warken <groff-bernd.warken-72@web.de>.
+# Automake migration by Bertrand Garrigues
+
+# This file is part of 'glilypond' which is part of 'groff'.
+
+# 'groff' 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 3 of the License, or
+# (at your option) any later version.
+
+# 'groff' 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, see <http://www.gnu.org/licenses/>.
+
+########################################################################
+glilypond_srcdir = $(top_srcdir)/contrib/glilypond
+bin_SCRIPTS += glilypond
+man1_MANS += contrib/glilypond/glilypond.1
+
+# files going to lib directory '$(glilypond_dir)'
+# TODO glilypond_dir is subsitued by configure.ac, check if this could be removed
+glilyponddir = $(glilypond_dir)
+
+EXTRA_DIST += \
+ contrib/glilypond/ChangeLog \
+ contrib/glilypond/ChangeLog.0x \
+ contrib/glilypond/glilypond.1.man \
+ contrib/glilypond/glilypond.pl \
+ contrib/glilypond/README.txt \
+ contrib/glilypond/examples/example.groff
+
+
+# create perl executable 'glilypond', being stored into 'bindir'
+glilypond: $(glilypond_srcdir)/glilypond.pl $(SH_DEPS_SED_SCRIPT)
+ $(AM_V_GEN)$(RM) $@ \
+ && sed -f "$(SH_DEPS_SED_SCRIPT)" \
+ -e "s|[@]g[@]|$(g)|g" \
+ -e "s|[@]BINDIR[@]|$(DESTDIR)$(bindir)|g" \
+ -e "s|[@]glilypond_dir[@]|$(DESTDIR)$(glilypond_dir)|g" \
+ -e "s|[@]VERSION[@]|$(VERSION)|g" \
+ $(glilypond_srcdir)/glilypond.pl \
+ >$@ \
+ && chmod +x $@
+
+uninstall_groffdirs: uninstall-glilypond-hook
+uninstall-glilypond-hook:
+ if test -d $(DESTDIR)$(glilyponddir); then \
+ rmdir $(DESTDIR)$(glilyponddir); \
+ fi
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/contrib/glilypond/glilypond.pl b/contrib/glilypond/glilypond.pl
new file mode 100755
index 0000000..e97ed24
--- /dev/null
+++ b/contrib/glilypond/glilypond.pl
@@ -0,0 +1,1907 @@
+#! /usr/bin/env perl
+
+package main;
+
+########################################################################
+# debugging
+########################################################################
+
+# See 'Mastering Perl', chapter 4.
+
+# use strict;
+# use warnings;
+# use diagnostics;
+
+use Carp;
+$SIG{__DIE__} = sub { &Carp::croak; };
+
+use Data::Dumper;
+
+########################################################################
+# Legalese
+########################################################################
+
+our $Legalese;
+
+{
+ use constant VERSION => '1.3.2'; # version of glilypond
+
+### This constant 'LICENSE' is the license for this file 'GPL' >= 3
+ use constant LICENSE => q*
+glilypond - integrate 'lilypond' into 'groff' files
+
+Copyright (C) 2013-2020 Free Software Foundation, Inc.
+ Written by Bernd Warken <groff-bernd.warken-72@web.de>
+
+This file is part of 'GNU groff'.
+
+ 'GNU groff' 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 3 of the License, or (at your
+option) any later version.
+
+ 'GNU groff' 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 'groff', see the files 'COPYING' and 'LICENSE' in the top
+directory of the 'groff' source package. If not, see
+<http://www.gnu.org/licenses/>.
+*;
+
+
+ $Legalese =
+ {
+ 'version' => VERSION,
+ 'license' => LICENSE,
+ }
+
+}
+
+##### end legalese
+
+
+########################################################################
+# global variables and BEGIN
+########################################################################
+
+use integer;
+use utf8;
+
+use Cwd qw[];
+use File::Basename qw[];
+use File::Copy qw[];
+use File::HomeDir qw[];
+use File::Spec qw[];
+use File::Path qw[];
+use File::Temp qw[];
+use FindBin qw[];
+use POSIX qw[];
+
+
+BEGIN {
+
+ use constant FALSE => 0;
+ use constant TRUE => 1;
+ use constant EMPTYSTRING => '';
+ use constant EMPTYARRAY => ();
+ use constant EMPTYHASH => ();
+
+ our $Globals =
+ {
+ 'before_make' => FALSE,
+ 'groff_version' => EMPTYSTRING,
+ 'prog' => EMPTYSTRING,
+ };
+
+ {
+ ( my $volume, my $directory, $Globals->{'prog'} ) =
+ File::Spec->splitpath($0);
+ # $Globals->{'prog'} is 'glilypond' when installed,
+ # 'glilypond.pl' when not
+ }
+
+
+ $\ = "\n"; # adds newline at each print
+ $/ = "\n"; # newline separates input
+ $| = 1; # flush after each print or write command
+
+
+ {
+ {
+ # script before run of 'make'
+ my $at = '@';
+ $Globals->{'before_make'} = TRUE if '@VERSION@' eq "${at}VERSION${at}";
+ }
+
+ my $file_test_pl;
+ my $glilypond_libdir;
+
+ if ( $Globals->{'before_make'} ) { # in source, not yet installed
+ my $glilypond_dir = $FindBin::Bin;
+ $glilypond_dir = Cwd::realpath($glilypond_dir);
+ $glilypond_libdir = $glilypond_dir;
+
+ } else { # already installed
+ $Globals->{'groff_version'} = '@VERSION@';
+ $glilypond_libdir = '@glilypond_dir@';
+ }
+
+ unshift(@INC, $glilypond_libdir);
+
+ umask 0077; # octal output: 'printf "%03o", umask;'
+ }
+
+ use integer;
+ use utf8;
+ use feature 'state';
+
+ my $P_PIC;
+ # $P_PIC = '.PDFPIC';
+ $P_PIC = '.PSPIC';
+
+ ######################################################################
+ # subs for using several times
+ ######################################################################
+
+ sub create_ly2eps { # '--ly2eps' default
+ our ( $out, $Read, $Temp );
+
+ my $prefix = $Read->{'file_numbered'}; # w/ dir change to temp dir
+
+ # '$ lilypond --ps -dbackend=eps -dgs-load-fonts \
+ # output=file_without_extension file.ly'
+ # extensions are added automatically
+ my $opts = '--ps -dbackend=eps -dinclude-eps-fonts -dgs-load-fonts'
+ . " --output=$prefix $prefix";
+ &run_lilypond("$opts");
+
+ Cwd::chdir $Temp->{'cwd'} or
+ die "Could not change to former directory '" .
+ $Temp->{'cwd'} . "': $!";
+
+ my $eps_dir = $Temp->{'eps_dir'};
+ my $dir = $Temp->{'temp_dir'};
+ opendir( my $dh, $dir ) or
+ die "could not open temporary directory '$dir': $!";
+
+ my $re = qr<
+ ^
+ $prefix
+ -
+ .*
+ \.eps
+ $
+ >x;
+ my $file;
+ while ( readdir( $dh ) ) {
+ chomp;
+ $file = $_;
+ if ( /$re/ ) {
+ my $file_path = File::Spec->catfile($dir, $file);
+ if ( $eps_dir ) {
+ my $could_copy = FALSE;
+ File::Copy::copy($file_path, $eps_dir)
+ and $could_copy = TRUE;
+ if ( $could_copy ) {
+ unlink $file_path;
+ $file_path = File::Spec->catfile($eps_dir, $_);
+ }
+ }
+ $out->print( $P_PIC . ' ' . $file_path );
+ }
+ } # end while readdir
+ closedir( $dh );
+ } # end sub create_ly2eps()
+
+
+ sub create_pdf2eps { # '--pdf2eps'
+ our ( $v, $stdout, $stderr, $out, $Read, $Temp );
+
+ my $prefix = $Read->{'file_numbered'}; # w/ dir change to temp dir
+
+ &run_lilypond("--pdf --output=$prefix $prefix");
+
+ my $file_pdf = $prefix . '.pdf';
+ my $file_ps = $prefix . '.ps';
+
+ # pdf2ps in temp dir
+ my $temp_file = &next_temp_file;
+ $v->print( "\n##### run of 'pdf2ps'" );
+ # '$ pdf2ps file.pdf file.ps'
+ my $output = `pdf2ps $file_pdf $file_ps 2> $temp_file`;
+ die 'Program pdf2ps does not work.' if ( $? );
+ &shell_handling($output, $temp_file);
+ $v->print( "##### end run of 'pdf2ps'\n" );
+
+ # ps2eps in temp dir
+ $temp_file = &next_temp_file;
+ $v->print( "\n##### run of 'ps2eps'" );
+ # '$ ps2eps file.ps'
+ $output = `ps2eps $file_ps 2> $temp_file`;
+ die 'Program ps2eps does not work.' if ( $? );
+ &shell_handling($output, $temp_file);
+ $v->print( "##### end run of 'ps2eps'\n" );
+
+ # change back to former dir
+ Cwd::chdir $Temp->{'cwd'} or
+ die "Could not change to former directory '" .
+ $Temp->{'cwd'} . "': $!";
+
+ # handling of .eps file
+ my $file_eps = $prefix . '.eps';
+ my $eps_path = File::Spec->catfile($Temp->{'temp_dir'}, $file_eps);
+ if ( $Temp->{'eps_dir'} ) {
+ my $has_copied = FALSE;
+ File::Copy::copy( $eps_path, $Temp->{'eps_dir'} )
+ and $has_copied = TRUE;
+ if ( $has_copied ) {
+ unlink $eps_path;
+ $eps_path = File::Spec->catfile( $Temp->{'eps_dir'}, $file_eps );
+ } else {
+ $stderr->print( "Could not use EPS-directory." );
+ } # end Temp->{'eps_dir'}
+ }
+ # print into groff output
+ $out->print( $P_PIC . ' ' . $eps_path );
+ } # end sub create_pdf2eps()
+
+
+ sub is_subdir { # arg1 is subdir of arg2 (is longer)
+ my ( $dir1, $dir2 ) = @_;
+ $dir1 = &path2abs( $dir1 );;
+ $dir2 = &path2abs( $dir2 );;
+ my @split1 = File::Spec->splitdir($dir1);
+ my @split2 = File::Spec->splitdir($dir2);
+ for ( @split2 ) {
+ next if ( $_ eq shift @split1 );
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+
+ sub license {
+ our ( $Legalese, $stdout );
+ &version;
+ $stdout->print( $Legalese->{'license'} );
+ } # end sub license()
+
+
+ sub make_dir { # make directory or check if it exists
+ our ( $v, $Args );
+
+ my $dir_arg = shift;
+ chomp $dir_arg;
+ $dir_arg =~ s/^\s*(.*)\s*$/$1/;
+
+ unless ( $dir_arg ) {
+ $v->print( "make_dir(): empty argument" );
+ return FALSE;
+ }
+
+ unless ( File::Spec->file_name_is_absolute($dir_arg) ) {
+ my $res = Cwd::realpath($dir_arg);
+ $res = File::Spec->canonpath($dir_arg) unless ( $res );
+ $dir_arg = $res if ( $res );
+ }
+
+ return $dir_arg if ( -d $dir_arg && -w $dir_arg );
+
+
+ # search thru the dir parts
+ my @dir_parts = File::Spec->splitdir($dir_arg);
+ my @dir_grow;
+ my $dir_grow;
+ my $can_create = FALSE; # dir could be created if TRUE
+
+ DIRPARTS: for ( @dir_parts ) {
+ push @dir_grow, $_;
+ next DIRPARTS unless ( $_ ); # empty string for root directory
+
+ # from array to path dir string
+ $dir_grow = File::Spec->catdir(@dir_grow);
+
+ next DIRPARTS if ( -d $dir_grow );
+
+ if ( -e $dir_grow ) { # exists, but not a dir, so must be removed
+ die "Couldn't create dir '$dir_arg', it is blocked by "
+ . "'$dir_grow'." unless ( -w $dir_grow );
+
+ # now it's writable, but not a dir, so it can be removed
+ unlink ( $dir_grow ) or
+ die "Couldn't remove '$dir_grow', " .
+ "so I cannot create dir '$dir_arg': $!";
+ }
+
+ # $dir_grow no longer exists, so the former dir must be writable
+ # in order to create the directory
+ pop @dir_grow;
+ $dir_grow = File::Spec->catdir(@dir_grow);
+
+ die "'$dir_grow' is not writable, " .
+ "so directory '$dir_arg' can't be created."
+ unless ( -w $dir_grow );
+
+ # former directory is writable, so '$dir_arg' can be created
+
+ File::Path::make_path( $dir_arg,
+ {
+ mask => oct('0700'),
+ verbose => $Args->{'verbose'},
+ }
+ ) # 'mkdir -P'
+ or die "Could not create directory '$dir_arg': $!";
+
+ last DIRPARTS;
+ }
+
+ die "'$dir_arg' is not a writable directory"
+ unless ( -d $dir_arg && -w $dir_arg );
+
+ return $dir_arg;
+
+ } # end sub make_dir()
+
+
+ my $number = 0;
+ sub next_temp_file {
+ our ( $Temp, $v, $Args );
+ ++$number;
+ my $temp_basename = $Args->{'prefix'} . '_temp_' . $number;
+ my $temp_file = File::Spec->catfile( $Temp->{'temp_dir'} ,
+ $temp_basename );
+ $v->print( "next temporary file: '$temp_file'" );
+ return $temp_file;
+ } # end sub next_temp_file()
+
+
+ sub path2abs {
+ our ( $Temp, $Args );
+
+ my $path = shift;
+ $path =~ s/
+ ^
+ \s*
+ (
+ .*
+ )
+ \s*
+ $
+ /$1/x;
+
+ die "path2abs(): argument is empty." unless ( $path );
+
+ # Perl does not support shell '~' for home dir
+ if ( $path =~ /
+ ^
+ ~
+ /x ) {
+ if ( $path eq '~' ) { # only own home
+ $path = File::HomeDir->my_home;
+ } elsif ( $path =~ m<
+ ^
+ ~ /
+ (
+ .*
+ )
+ $
+ >x ) { # subdir of own home
+ $path = File::Spec->catdir( $Temp->{'cwd'}, $1 );
+ } elsif ( $path =~ m<
+ ^
+ ~
+ (
+ [^/]+
+ )
+ $
+ >x ) { # home of other user
+ $path = File::HomeDir->users_home($1);
+ } elsif ( $path =~ m<
+ ^
+ ~
+ (
+ [^/]+
+ )
+ /+
+ (
+ .*
+ )
+ $
+ >x ) { # subdir of other home
+ $path = File::Spec->
+ catdir( File::HomeDir->users_home($1), $2 );
+ }
+ }
+
+ $path = File::Spec->rel2abs($path);
+
+ # now $path is absolute
+ return $path;
+ } # end sub path2abs()
+
+
+ sub run_lilypond {
+ # arg is the options collection for 'lilypond' to run
+ # either from ly or pdf
+
+ our ( $Temp, $v );
+
+ my $opts = shift;
+ chomp $opts;
+
+ my $temp_file = &next_temp_file;
+ my $output = EMPTYSTRING;
+
+ # change to temp dir
+ Cwd::chdir $Temp->{'temp_dir'} or
+ die "Could not change to temporary directory '" .
+ $Temp->{'temp_dir'} . "': $!";
+
+ $v->print( "\n##### run of 'lilypond " . $opts . "'" );
+ $output = `lilypond $opts 2>$temp_file`;
+ die "Program lilypond does not work, see '$temp_file': $?"
+ if ( $? );
+ chomp $output;
+ &shell_handling($output, $temp_file);
+ $v->print( "##### end run of 'lilypond'\n" );
+
+ # stay in temp dir
+ } # end sub run_lilypond()
+
+
+ sub shell_handling {
+ # Handle ``-shell-command output in a string (arg1).
+ # stderr goes to temporary file $TempFile.
+
+ our ( $out, $v, $Args );
+
+ my $out_string = shift;
+ my $temp_file = shift;
+
+ my $a = &string2array($out_string); # array ref
+ for ( @$a ) {
+ $out->print( $_ );
+ }
+
+ $temp_file && -f $temp_file && -r $temp_file ||
+ die "shell_handling(): $temp_file is not a readable file.";
+ my $temp = new FH_READ_FILE($temp_file);
+ my $res = $temp->read_all();
+ for ( @$res ) {
+ chomp;
+ $v->print($_);
+ }
+
+ unlink $temp_file unless ( $Args->{'keep_all'} );
+ } # end sub shell_handling()
+
+
+ sub string2array {
+ my $s = shift;
+ my @a = ();
+ for ( split "\n", $s ) {
+ chomp;
+ push @a, $_;
+ }
+ return \@a;
+ } # end string2array()
+
+
+ sub usage { # for '--help'
+ our ( $Globals, $Args );
+
+ my $p = $Globals->{'prog'};
+ my $usage = EMPTYSTRING;
+ $usage = '###### usage:' . "\n" if ( $Args->{'verbose'} );
+ $usage .= qq*Options for $p:
+Read a 'roff' file or standard input and transform 'lilypond' parts
+(everything between '.lilypond start' and '.lilypond end') into
+'EPS'-files that can be read by groff using '.PSPIC'.
+
+There is also a command '.lilypond include <file_name>' that can
+include a complete 'lilypond' file into the 'groff' document.
+
+
+# Breaking options:
+$p -?|-h|--help|--usage # usage
+$p --version # version information
+$p --license # the license is GPL >= 3
+
+
+# Normal options:
+$p [options] [--] [filename ...]
+
+There are 2 options for influencing the way how the 'EPS' files for the
+'roff' display are generated:
+--ly2eps 'lilypond' generates 'EPS' files directly (default)
+--pdf2eps 'lilypond' generates a 'PDF' file that is transformed
+
+-k|--keep_all do not delete any temporary files
+-v|--verbose print much information to STDERR
+
+Options with an argument:
+-e|--eps_dir=... use a directory for the EPS files
+-o|--output=... sent output in the groff language into file ...
+-p|--prefix=... start for the names of temporary files
+-t|--temp_dir=... provide the directory for temporary files.
+
+The directories set are created when they do not exist.
+*;
+
+ # old options:
+ # --keep_files -k: do not delete any temporary files
+ # --file_prefix=... -p: start for the names of temporary files
+
+ $main::stdout->print( $usage );
+ } # end sub usage()
+
+
+ sub version { # for '--version'
+ our ( $Globals, $Legalese, $stdout, $Args );
+ my $groff_version = '';
+ if ( $Globals->{'groff_version'} ) {
+ $groff_version = "(groff $Globals->{'groff_version'}) ";
+ }
+
+ my $output = EMPTYSTRING;
+ $output = "$Globals->{'prog'} ${groff_version}version "
+ . $Legalese->{'version'};
+
+ $stdout->print($output);
+ } # end sub version()
+}
+
+#die "test: ";
+########################################################################
+# OOP declarations for some file handles
+########################################################################
+
+use integer;
+
+########################################################################
+# OOP for writing file handles that are open by default, like STD*
+########################################################################
+
+# -------------------------- _FH_WRITE_OPENED --------------------------
+
+{ # FH_OPENED: base class for all opened file handles, like $TD*
+
+ package _FH_WRITE_OPENED;
+ use strict;
+
+ sub new {
+ my ( $pkg, $std ) = @_;
+ bless {
+ 'fh' => $std,
+ }
+ }
+
+ sub open {
+ }
+
+ sub close {
+ }
+
+ sub print {
+ my $self = shift;
+ for ( @_ ) {
+ print { $self->{'fh'} } $_;
+ }
+ }
+
+}
+
+
+# ------------------------------ FH_STDOUT ----------------------------
+
+{ # FH_STDOUT: print to normal output STDOUT
+
+ package FH_STDOUT;
+ use strict;
+ @FH_STDOUT::ISA = qw( _FH_WRITE_OPENED );
+
+ sub new {
+ &_FH_WRITE_OPENED::new( '_FH_WRITE_OPENED', *STDOUT );
+ }
+
+} # end FH_STDOUT
+
+
+# ------------------------------ FH_STDERR -----------------------------
+
+{ # FH_STDERR: print to STDERR
+
+ package FH_STDERR;
+ use strict;
+ @FH_STDERR::ISA = qw( _FH_WRITE_OPENED );
+
+ sub new {
+ &_FH_WRITE_OPENED::new( 'FH_OPENED', *STDERR );
+ }
+
+} # end FH_STDERR
+
+
+########################################################################
+# OOP for file handles that write into a file or string
+########################################################################
+
+# ------------------------------- FH_FILE ------------------------------
+
+{ # FH_FILE: base class for writing into a file or string
+
+ package FH_FILE;
+ use strict;
+
+ sub new {
+ my ( $pkg, $file ) = @_;
+ bless {
+ 'fh' => undef,
+ 'file' => $file,
+ 'opened' => main::FALSE,
+ }
+ }
+
+ sub DESTROY {
+ my $self = shift;
+ $self->close();
+ }
+
+ sub open {
+ my $self = shift;
+ my $file = $self->{'file'};
+ if ( $file && -e $file ) {
+ die "file $file is not writable" unless ( -w $file );
+ die "$file is a directory" if ( -d $file );
+ }
+ open $self->{'fh'}, ">", $self->{'file'}
+ or die "could not open file '$file' for writing: $!";
+ $self->{'opened'} = main::TRUE;
+ }
+
+ sub close {
+ my $self = shift;
+ close $self->{'fh'} if ( $self->{'opened'} );
+ $self->{'opened'} = main::FALSE;
+ }
+
+ sub print {
+ my $self = shift;
+ $self->open() unless ( $self->{'opened'} );
+ for ( @_ ) {
+ print { $self->{'fh'} } $_;
+ }
+ }
+
+} # end FH_FILE
+
+
+# ------------------------------ FH_STRING -----------------------------
+
+{ # FH_STRING: write into a string
+
+ package FH_STRING; # write to \string
+ use strict;
+ @FH_STRING::ISA = qw( FH_FILE );
+
+ sub new {
+ my $pkg = shift; # string is a reference to scalar
+ bless
+ {
+ 'fh' => undef,
+ 'string' => '',
+ 'opened' => main::FALSE,
+ }
+ }
+
+ sub open {
+ my $self = shift;
+ open $self->{'fh'}, ">", \ $self->{'string'}
+ or die "could not open string for writing: $!";
+ $self->{'opened'} = main::TRUE;
+ }
+
+ sub get { # get string, move to array ref, close, and return array ref
+ my $self = shift;
+ return '' unless ( $self->{'opened'} );
+ my $a = &string2array( $self->{'string'} );
+ $self->close();
+ return $a;
+ }
+
+} # end FH_STRING
+
+
+# -------------------------------- FH_NULL -----------------------------
+
+{ # FH_NULL: write to null device
+
+ package FH_NULL;
+ use strict;
+ @FH_NULL::ISA = qw( FH_FILE FH_STRING );
+
+ use File::Spec;
+
+ my $devnull = File::Spec->devnull();
+ $devnull = '' unless ( -e $devnull && -w $devnull );
+
+ sub new {
+ my $pkg = shift;
+ if ( $devnull ) {
+ &FH_FILE::new( $pkg, $devnull );
+ } else {
+ &FH_STRING::new( $pkg );
+ }
+ } # end new()
+
+} # end FH_NULL
+
+
+########################################################################
+# OOP for reading file handles
+########################################################################
+
+# ---------------------------- FH_READ_FILE ----------------------------
+
+{ # FH_READ_FILE: read a file
+
+ package FH_READ_FILE;
+ use strict;
+
+ sub new {
+ my ( $pkg, $file ) = @_;
+ die "File '$file' cannot be read." unless ( -f $file && -r $file );
+ bless {
+ 'fh' => undef,
+ 'file' => $file,
+ 'opened' => main::FALSE,
+ }
+ }
+
+ sub DESTROY {
+ my $self = shift;
+ $self->close();
+ }
+
+ sub open {
+ my $self = shift;
+ my $file = $self->{'file'};
+ if ( $file && -e $file ) {
+ die "file $file is not writable" unless ( -r $file );
+ die "$file is a directory" if ( -d $file );
+ }
+ open $self->{'fh'}, "<", $self->{'file'}
+ or die "could not read file '$file': $!";
+ $self->{'opened'} = main::TRUE;
+ }
+
+ sub close {
+ my $self = shift;
+ close $self->{'fh'} if ( $self->{'opened'} );
+ $self->{'opened'} = main::FALSE;
+ }
+
+ sub read_line {
+ # Read 1 line of the file into a chomped string.
+ # Do not close the read handle at the end.
+ my $self = shift;
+ $self->open() unless ( $self->{'opened'} );
+
+ my $res;
+ if ( defined($res = CORE::readline($self->{'fh'}) ) ) {
+ chomp $res;
+ return $res;
+ } else {
+ $self->close();
+ return undef;
+ }
+ }
+
+ sub read_all {
+ # Read the complete file into an array reference.
+ # Close the read handle at the end.
+ # Return array reference.
+ my $self = shift;
+ $self->open() unless ( $self->{'opened'} );
+
+ my $res = [];
+ my $line;
+ while ( defined ( $line = CORE::readline $self->{'fh'} ) ) {
+ chomp $line;
+ push @$res, $line;
+ }
+ $self->close();
+ $self->{'opened'} = main::FALSE;
+ return $res;
+ }
+
+}
+
+# end of OOP definitions
+
+
+our $stdout = new FH_STDOUT();
+our $stderr = new FH_STDERR();
+
+# verbose printing, not clear whether this will be set by '--verbose',
+# so store this now into a string, which can be gotten later on, when
+# it will become either STDERR or /dev/null
+our $v = new FH_STRING();
+
+# for standard output, either STDOUT or output file
+our $out;
+
+# end of FH
+
+
+########################################################################
+# Args: command-line arguments
+########################################################################
+
+# command-line arguments are handled in 2 runs:
+# 1) split short option collections, '=' optargs, and transfer abbrevs
+# 2) handle the transferred options with subs
+
+our $Args =
+ {
+ 'eps_dir' => EMPTYSTRING, # can be overwritten by '--eps_dir'
+
+ # 'eps-func' has 2 possible values:
+ # 1) 'pdf' '--pdf2eps' (default)
+ # 2) 'ly' from '--ly2eps'
+ 'eps_func' => 'pdf',
+
+ # files names of temporary files start with this string,
+ # can be overwritten by '--prefix'
+ 'prefix' => 'ly',
+
+ # delete or do not delete temporary files
+ 'keep_all' => FALSE,
+
+ # the roff output goes normally to STDOUT, can be a file with '--output'
+ 'output' => EMPTYSTRING,
+
+ # temporary directory, can be overwritten by '--temp_dir',
+ # empty for default of the program
+ 'temp_dir' => EMPTYSTRING,
+
+ # regulates verbose output (on STDERR), overwritten by '--verbose'
+ 'verbose' => FALSE,
+ };
+
+{ # 'Args'
+ use integer;
+
+ our ( $Globals, $Args, $stderr, $v, $out );
+
+ # ----------
+ # subs for second run, for remaining long options after splitting and
+ # transfer
+ # ----------
+
+ my %opts_with_arg =
+ (
+
+ '--eps_dir' => sub {
+ $Args->{'eps_dir'} = shift;
+ },
+
+ '--output' => sub {
+ $Args->{'output'} = shift;
+ },
+
+ '--prefix' => sub {
+ $Args->{'prefix'} = shift;
+ },
+
+ '--temp_dir' => sub {
+ $Args->{'temp_dir'} = shift;
+ },
+
+ ); # end of %opts_with_arg
+
+
+ my %opts_noarg =
+ (
+
+ '--help' => sub {
+ &usage;
+ exit;
+ },
+
+ '--keep_all' => sub {
+ $Args->{'keep_all'} = TRUE;
+ },
+
+ '--license' => sub {
+ &license;
+ exit;
+ },
+
+ '--ly2eps' => sub {
+ $Args->{'eps_func'} = 'ly';
+ },
+
+ '--pdf2eps' => sub {
+ $Args->{'eps_func'} = 'pdf';
+ },
+
+ '--verbose' => sub {
+ $Args->{'verbose'} = TRUE;
+ },
+
+ '--version' => sub {
+ &version;
+ exit;
+ },
+
+ ); # end of %opts_noarg
+
+
+ # used variables in both runs
+
+ my @files = EMPTYARRAY;
+
+
+ #----------
+ # first run for command-line arguments
+ #----------
+
+ # global variables for first run
+
+ my @splitted_args;
+ my $double_minus = FALSE;
+ my $arg = EMPTYSTRING;
+ my $has_arg = FALSE;
+
+
+ # Split short option collections and transfer these to suitable long
+ # options from above. Note that '-v' now means '--verbose' in version
+ # 'v1.1', earlier versions had '--version' for '-v'.
+
+ my %short_opts =
+ (
+ '?' => '--help',
+ 'e' => '--eps_dir',
+ 'h' => '--help',
+ 'l' => '--license',
+ 'k' => '--keep_all',
+ 'o' => '--output',
+ 'p' => '--prefix',
+ 't' => '--temp_dir',
+ 'v' => '--verbose',
+ 'V' => '--verbose',
+ );
+
+
+ # transfer long option abbreviations to the long options from above
+
+ my @long_opts;
+
+ $long_opts[3] =
+ { # option abbreviations of 3 characters
+ '--e' => '--eps_dir',
+ '--f' => '--prefix', # --f for --file_prefix
+ '--h' => '--help',
+ '--k' => '--keep_all', # and --keep_files
+ '--o' => '--output',
+ '--p' => '--prefix', # and --file_prefix
+ '--t' => '--temp_dir',
+ '--u' => '--help', # '--usage' is mapped to '--help'
+ };
+
+ $long_opts[4] =
+ { # option abbreviations of 4 characters
+ '--li' => '--license',
+ '--ly' => '--ly2eps',
+ '--pd' => '--pdf2eps',
+ '--pr' => '--prefix',
+ };
+
+ $long_opts[6] =
+ { # option abbreviations of 6 characters
+ '--verb' => '--verbose',
+ '--vers' => '--version',
+ };
+
+
+ # subs for short splitting and replacing long abbreviations
+
+ my $split_short = sub {
+
+ my @chars = split //, $1; # omit leading dash
+
+ # if result is TRUE: run 'next SPLIT' afterwards
+
+ CHARS: while ( @chars ) {
+ my $c = shift @chars;
+
+ unless ( exists $short_opts{$c} ) {
+ $stderr->print( "Unknown short option '-$c'." );
+ next CHARS;
+ }
+
+ # short option exists
+
+ # map or transfer to special long option from above
+ my $transopt = $short_opts{$c};
+
+ if ( exists $opts_noarg{$transopt} ) {
+ push @splitted_args, $transopt;
+ $Args->{'verbose'} = TRUE if ( $transopt eq '--verbose' );
+ next CHARS;
+ }
+
+ if ( exists $opts_with_arg{$transopt} ) {
+ push @splitted_args, $transopt;
+
+ if ( @chars ) {
+ # if @chars is not empty, option $transopt has argument
+ # in this arg, the rest of characters in @chars
+ push @splitted_args, join "", @chars;
+ @chars = EMPTYARRAY;
+ return TRUE; # use 'next SPLIT' afterwards
+ }
+
+ # optarg is the next argument
+ $has_arg = $transopt;
+ return TRUE; # use 'next SPLIT' afterwards
+ } # end of if %opts_with_arg
+ } # end of while CHARS
+ return FALSE; # do not do anything
+ }; # end of sub for short_opt_collection
+
+
+ my $split_long = sub {
+ my $from_arg = shift;
+ $from_arg =~ /^([^=]+)/;
+ my $opt_part = lc($1);
+ my $optarg = undef;
+ if ( $from_arg =~ /=(.*)$/ ) {
+ $optarg = $1;
+ }
+
+ N: for my $n ( qw/6 4 3/ ) {
+ $opt_part =~ / # match $n characters
+ ^
+ (
+ .{$n}
+ )
+ /x;
+ my $argn = $1; # get the first $n characters
+
+ # no match, so luck for fewer number of chars
+ next N unless ( $argn );
+
+ next N unless ( exists $long_opts[$n]->{$argn} );
+ # not in $n hash, so go on to next loop for $n
+
+ # now $n-hash has arg
+
+ # map or transfer to special long opt from above
+ my $transopt = $long_opts[$n]->{$argn};
+
+ # test on option without arg
+ if ( exists $opts_noarg{$transopt} ) { # opt has no arg
+ $stderr->print( 'Option ' . $transopt . 'has no argument: ' .
+ $from_arg . '.' ) if ( defined($optarg) );
+ push @splitted_args, $transopt;
+ $Args->{'verbose'} = TRUE if ( $transopt eq '--verbose' );
+ return TRUE; # use 'next SPLIT' afterwards
+ } # end of if %opts_noarg
+
+ # test on option with arg
+ if ( exists $opts_with_arg{$transopt} ) { # opt has arg
+ push @splitted_args, $transopt;
+
+ # test on optarg in arg
+ if ( defined($optarg) ) {
+ push @splitted_args, $1;
+ return TRUE; # use 'next SPLIT' afterwards
+ } # end of if optarg in arg
+
+ # has optarg in next arg
+ $has_arg = $transopt;
+ return TRUE; # use 'next SPLIT' afterwards
+ } # end of if %opts_with_arg
+
+ # not with and without option, so is not permitted
+ $stderr->print( "'" . $transopt .
+ "' is unknown long option from '" . $from_arg . "'" );
+ return TRUE; # use 'next SPLIT' afterwards
+ } # end of for N
+ return FALSE; # do nothing
+ }; # end of split_long()
+
+
+ #----------
+ # do split and transfer arguments
+ #----------
+ sub run_first {
+
+ SPLIT: foreach (@ARGV) {
+ # Transform long and short options into some given long options.
+ # Split long opts with arg into 2 args (no '=').
+ # Transform short option collections into given long options.
+ chomp;
+
+ if ( $has_arg ) {
+ push @splitted_args, $_;
+ $has_arg = EMPTYSTRING;
+ next SPLIT;
+ }
+
+ if ( $double_minus ) {
+ push @files, $_;
+ next SPLIT;
+ }
+
+ if ( $_ eq '-' ) { # file arg '-'
+ push @files, $_;
+ next SPLIT;
+ }
+
+ if ( $_ eq '--' ) { # POSIX arg '--'
+ push @splitted_args, $_;
+ $double_minus = TRUE;
+ next SPLIT;
+ }
+
+ if ( / # short option or collection of short options
+ ^
+ -
+ (
+ [^-]
+ .*
+ )
+ $
+ /x ) {
+ $split_short->($1);
+ next SPLIT;
+ } # end of short option
+
+ if ( /^--/ ) { # starts with 2 dashes, a long option
+ $split_long->($_);
+ next SPLIT;
+ } # end of long option
+
+ # unknown option without leading dash is a file name
+ push @files, $_;
+ next SPLIT;
+ } # end of foreach SPLIT
+
+ # all args are considered
+ $stderr->print( "Option '$has_arg' needs an argument." )
+ if ( $has_arg );
+
+
+ push @files, '-' unless ( @files );
+ @ARGV = @splitted_args;
+
+ }; # end of first run, splitting with map or transfer
+
+
+ #----------
+ # open or ignore verbose output
+ #----------
+ sub install_verbose {
+ if ( $Args->{'verbose'} ) { # '--verbose' was used
+ # make verbose output into $v
+ # get content of string so far as array ref, close
+ my $s = $v->get();
+
+ $v = new FH_STDERR(); # make verbose output into STDERR
+ if ( $s ) {
+ for ( @$s ) {
+ # print the file content into new verbose output
+ $v->print($_);
+ }
+ }
+ # verbose output is now active (into STDERR)
+ $v->print( "Option '-v' means '--verbose'." );
+ $v->print( "Version information is printed by option"
+ . " '--version'."
+ );
+ $v->print( "#" x 72 );
+
+ } else { # '--verbose' was not used
+ # do not be verbose, make verbose invisible
+
+ $v->close(); # close and ignore the string content
+
+ $v = new FH_NULL();
+ # this is either into /dev/null or in an ignored string
+
+ } # end if-else about verbose
+ # '$v->print' works now in any case
+
+ $v->print( "Verbose output was chosen." );
+
+ my $s = $Globals->{'prog_is_installed'} ? '' : ' not';
+ $v->print( $Globals->{'prog'} . " is" . $s .
+ " installed." );
+
+ $v->print( 'The command-line options are:' );
+
+ $s = " options:";
+ $s .= " '" . $_ . "'" for ( @ARGV );
+ $v->print( $s );
+
+ $s = " file names:";
+ $s .= " '" . $_ . "'\n" for ( @files );
+ $v->print( $s );
+ } # end install_verbose()
+
+
+ #----------
+ # second run of command-line arguments
+ #----------
+ sub run_second {
+ # Second run of args with new @ARGV from the former splitting.
+ # Arguments are now split and transformed into special long
+ # options.
+
+ my $double_minus = FALSE;
+ my $has_arg = FALSE;
+
+ ARGS: for my $arg ( @ARGV ) {
+
+ # ignore '--', file names are handled later on
+ last ARGS if ( $arg eq '--' );
+
+ if ( $has_arg ) {
+ unless ( exists $opts_with_arg{$has_arg} ) {
+ $stderr->print( "'\%opts_with_args' does not have key '" .
+ $has_arg . "'." );
+ next ARGS;
+ }
+
+ $opts_with_arg{$has_arg}->($arg);
+ $has_arg = FALSE;
+ next ARGS;
+ } # end of $has_arg
+
+ if ( exists $opts_with_arg{$arg} ) {
+ $has_arg = $arg;
+ next ARGS;
+ }
+
+ if ( exists $opts_noarg{$arg} ) {
+ $opts_noarg{$arg}->();
+ next ARGS;
+ }
+
+ # not a suitable option
+ $stderr->print( "Wrong option '" . $arg . "'." );
+ next ARGS;
+
+ } # end of for ARGS:
+
+
+ if ( $has_arg ) { # after last argument
+ die "Option '$has_arg' needs an argument.";
+ }
+
+ }; # end of second run
+
+
+ sub handle_args {
+ # handling the output of args
+
+ if ( $Args->{'output'} ) { # '--output' was set in the arguments
+ my $out_path = &path2abs($Args->{'output'});
+ die "Output file name $Args->{'output'} cannot be used."
+ unless ( $out_path );
+
+ my ( $file, $dir );
+ ( $file, $dir ) = File::Basename::fileparse($out_path)
+ or die "Could not handle output file path '" . $out_path
+ . "': directory name '" . $dir . "' and file name '" . $file
+ . "'.";
+
+ die "Could not find output directory for '" . $Args->{'output'}
+ . "'" unless ( $dir );
+ die "Could not find output file: '" . $Args->{'output'} .
+ "'" unless ( $file );
+
+ if ( -d $dir ) {
+ die "Could not write to output directory '" . $dir . "'."
+ unless ( -w $dir );
+ } else {
+ $dir = &make_dir($dir);
+ die "Could not create output directory in: '" . $out_path . "'."
+ unless ( $dir );
+ }
+
+ # now $dir is a writable directory
+
+ if ( -e $out_path ) {
+ die "Could not write to output file '" . $out_path . "'."
+ unless ( -w $out_path );
+ }
+
+ $out = new FH_FILE( $out_path );
+ $v->print( "Output goes to file '" . $out_path . "'." );
+ } else { # '--output' was not set
+ $out = new FH_STDOUT();
+ }
+ # no $out is the right behavior for standard output
+
+ # $Args->{'prefix'} .= '_' . $Args->{'eps_func'} . '2eps';
+
+ @ARGV = @files;
+ }
+
+ &run_first();
+ &install_verbose();
+ &run_second();
+ &handle_args();
+}
+
+# end 'Args'
+
+
+########################################################################
+# temporary directory .../tmp/groff/USER/lilypond/TIME
+########################################################################
+
+our $Temp =
+ {
+ # store the current directory
+ 'cwd' => Cwd::getcwd(),
+
+ # directory for EPS files
+ 'eps_dir' => EMPTYSTRING,
+
+ # temporary directory
+ 'temp_dir' => EMPTYSTRING,
+ };
+
+{ # 'Temp'
+
+ if ( $Args->{'temp_dir'} ) {
+
+ #----------
+ # temporary directory was set by '--temp_dir'
+ #----------
+
+ my $dir = $Args->{'temp_dir'};
+
+ $dir = &path2abs($dir);
+ $dir = &make_dir($dir) or
+ die "The directory '$dir' cannot be used temporarily: $!";
+
+
+ # now '$dir' is a writable directory
+
+ opendir( my $dh, $dir ) or
+ die "Could not open temporary directory '$dir': $!";
+ my $file_name;
+ my $found = FALSE;
+ my $prefix = $Args->{'prefix'};
+ my $re = qr<
+ ^
+ $prefix
+ _
+ >x;
+
+ READDIR: while ( defined($file_name = readdir($dh)) ) {
+ chomp $file_name;
+ if ( $file_name =~ /$re/ ) { # file name starts with $prefix_
+ $found = TRUE;
+ last READDIR;
+ }
+ next;
+ }
+
+ $Temp->{'temp_dir'} = $dir;
+ my $n = 0;
+ while ( $found ) {
+ $dir = File::Spec->catdir( $Temp->{'temp_dir'}, ++$n );
+ next if ( -e $dir );
+
+ $dir = &make_dir($dir) or next;
+
+ $found = FALSE;
+ last;
+ }
+
+ $Temp->{'temp_dir'} = $dir;
+
+
+ } else { # $Args->{'temp_dir'} not given by '--temp_dir'
+
+ #----------
+ # temporary directory was not set
+ #----------
+
+ { # search for or create a temporary directory
+
+ my @tempdirs = EMPTYARRAY;
+ {
+ my $tmpdir = File::Spec->tmpdir();
+ push @tempdirs, $tmpdir
+ if ( $tmpdir && -d $tmpdir && -w $tmpdir );
+
+ my $root_dir = File::Spec->rootdir(); # '/' in Unix
+ my $root_tmp = File::Spec->catdir($root_dir, 'tmp');
+ push @tempdirs, $root_tmp
+ if ( $root_tmp ne $tmpdir && -d $root_tmp && -w $root_tmp );
+
+ # home directory of the actual user
+ my $home = File::HomeDir->my_home;
+ my $home_tmp = File::Spec->catdir($home, 'tmp');
+ push @tempdirs, $home_tmp if ( -d $home_tmp && -w $home_tmp );
+
+ # '/var/tmp' in Unix
+ my $var_tmp = File::Spec->catdir('', 'var', 'tmp');
+ push @tempdirs, $var_tmp if ( -d $var_tmp && -w $var_tmp );
+ }
+
+
+ my @path_extension = qw( groff ); # TEMPDIR/groff/USER/lilypond/N
+ {
+ # '$<' is UID of actual user,
+ # 'getpwuid' gets user name in scalar context
+ my $user = getpwuid($<);
+ push @path_extension, $user if ( $user );
+
+ push @path_extension, qw( lilypond );
+ }
+
+
+ TEMPS: foreach ( @tempdirs ) {
+
+ my $dir; # final directory name in 'while' loop
+ $dir = &path2abs($_);
+ next TEMPS unless ( $dir );
+
+ # beginning of directory name
+ my @dir_begin =
+ ( File::Spec->splitdir($dir), @path_extension );
+
+
+ my $n = 0;
+ my $dir_blocked = TRUE;
+ BLOCK: while ( $dir_blocked ) {
+ # should become the final dir name
+ $dir = File::Spec->catdir(@dir_begin, ++$n);
+ next BLOCK if ( -d $dir );
+
+ # dir name is now free, create it, and end the blocking
+ my $res = &make_dir( $dir );
+ die "Could not create directory: $dir" unless ( $res );
+
+ $dir = $res;
+ $dir_blocked = FALSE;
+ }
+
+ next TEMPS unless ( -d $dir && -w $dir );
+
+ # $dir is now a writable directory
+ $Temp->{'temp_dir'} = $dir; # tmp/groff/USER/lilypond/TIME
+ last TEMPS;
+ } # end foreach tmp directories
+ } # end to create a temporary directory
+
+ die "Could not find a temporary directory" unless
+ ( $Temp->{'temp_dir'} && -d $Temp->{'temp_dir'} &&
+ -w $Temp->{'temp_dir'} );
+
+ } # end temporary directory
+
+ $v->print( "Temporary directory: '" . $Temp->{'temp_dir'} . "'\n" );
+ $v->print( "file_prefix: '" . $Args->{'prefix'} . "'" );
+
+
+ #----------
+ # EPS directory
+ #----------
+
+ my $make_dir = FALSE;
+ if ( $Args->{'eps_dir'} ) { # set by '--eps_dir'
+ my $dir = $Args->{'eps_dir'};
+
+ $dir = &path2abs($dir);
+
+ if ( -e $dir ) {
+ goto EMPTY unless ( -w $dir );
+
+ # '$dir' is writable
+ if ( -d $dir ) {
+ my $upper_dir = $dir;
+
+ my $found = FALSE;
+ opendir( my $dh, $upper_dir ) or $found = TRUE;
+ my $prefix = $Args->{'prefix'};
+ my $re = qr<
+ ^
+ $prefix
+ _
+ >x;
+ while ( not $found ) {
+ my $file_name = readdir($dh);
+ if ( $file_name =~ /$re/ ) { # file name starts with $prefix_
+ $found = TRUE;
+ last;
+ }
+ next;
+ }
+
+ my $n = 0;
+ while ( $found ) {
+ $dir = File::Spec->catdir($upper_dir, ++$n);
+ next if ( -d $dir );
+ $found = FALSE;
+ }
+ $make_dir = TRUE;
+ $Temp->{'eps_dir'} = $dir;
+ } else { # '$dir' is not a dir, so unlink it to create it as dir
+ if ( unlink $dir ) { # could remove '$dir'
+ $Temp->{'eps_dir'} = $dir;
+ $make_dir = TRUE;
+ } else { # could not remove
+ $stderr->print( "Could not use EPS dir '" . $dir .
+ "', use temp dir." );
+ } # end of unlink
+ } # end test of -d $dir
+ } else {
+ $make_dir = TRUE;
+ } # end of if -e $dir
+
+
+ if ( $make_dir ) { # make directory '$dir'
+ my $made = FALSE;
+ $dir = &make_dir($dir) and $made = TRUE;
+
+ if ( $made ) {
+ $Temp->{'eps_dir'} = $dir;
+ $v->print( "Directory for useful EPS files is '" . $dir . "'." );
+ } else {
+ $v->print( "The EPS directory '" . $dir . "' cannot be used: $!" );
+ }
+ } else { # '--eps_dir' was not set, so take the temporary directory
+ $Temp->{'eps_dir'} = $Args->{'temp_dir'};
+ } # end of make dir
+ }
+
+ EMPTY: unless ( $Temp->{'eps_dir'} ) {
+ # EPS-dir not set or available, use temp dir,
+ # but leave $Temp->{'}eps_dir'} empty
+ $v->print( "Directory for useful EPS files is the " .
+ "temporary directory '" . $Temp->{'temp_dir'} . "'." );
+ }
+
+} # end 'Temp'
+
+
+########################################################################
+# Read: read files or stdin
+########################################################################
+
+our $Read =
+ {
+ 'file_numbered' => EMPTYSTRING,
+ 'file_ly' => EMPTYSTRING, # '$file_numbered.ly'
+ };
+
+{ # read files or stdin
+
+ my $ly_number = 0; # number of lilypond file
+
+ # '$Args->{'prefix'}_[0-9]'
+
+ my $lilypond_mode = FALSE;
+
+ my $arg1; # first argument for '.lilypond'
+ my $arg2; # argument for '.lilypond include'
+
+ my $path_ly; # path of ly-file
+
+
+ my $check_file = sub { # for argument of '.lilypond include'
+ my $file = shift; # argument is a file name
+ $file = &path2abs($file);
+ unless ( $file ) {
+ die "Line '.lilypond include' without argument";
+ return '';
+ }
+ unless ( -f $file && -r $file ) {
+ die "Argument '$file' in '.lilypond include' is not a readable file";
+ }
+
+ return $file;
+ }; # end sub &$check_file()
+
+
+ my $increase_ly_number = sub {
+ ++$ly_number;
+ $Read->{'file_numbered'} = $Args->{'prefix'} . '_' . $ly_number;
+ $Read->{'file_ly'} = $Read->{'file_numbered'} . '.ly';
+ $path_ly = File::Spec->catdir($Temp->{'temp_dir'}, $Read->{'file_ly'} );
+ };
+
+
+ my %eps_subs =
+ (
+ 'ly' => \&create_ly2eps, # lilypond creates EPS files
+ 'pdf' => \&create_pdf2eps, # lilypond creates PDF file
+ );
+
+ # about lines starting with '.lilypond'
+
+ my $ly;
+ my $fh_include_file;
+ my %lilypond_args =
+ (
+
+ 'start' => sub {
+ $v->print( "\nline: '.lilypond start'" );
+ die "Line '.lilypond stop' expected." if ( $lilypond_mode );
+
+ $lilypond_mode = TRUE;
+ &$increase_ly_number;
+
+ $v->print( "ly-file: '" . $path_ly . "'" );
+
+ $ly = new FH_FILE($path_ly);
+ },
+
+
+ 'end' => sub {
+ $v->print( "line: '.lilypond end'\n" );
+ die "Expected line '.lilypond start'." unless ( $lilypond_mode );
+
+ $lilypond_mode = FALSE;
+ $ly->close();
+
+ if ( exists $eps_subs{ $Args->{'eps_func'} } ) {
+ $eps_subs{ $Args->{'eps_func'} }->();
+ } else {
+ die "Wrong argument for \%eps_subs: " . $Args->{'eps_func'} . "'";
+ }
+ },
+
+
+ 'include' => sub { # '.lilypond include file...'
+
+ # this may not be used within lilypond mode
+ next LILYPOND if ( $lilypond_mode );
+
+ my $file_arg = shift;
+
+ my $file = &$check_file($file_arg);
+ next LILYPOND unless ( $file );
+ # file can be read now
+
+
+ # '$fh_write_ly' must be opened
+ &$increase_ly_number;
+
+ $ly = new FH_FILE($path_ly);
+
+ my $include = new FH_READ_FILE($file);
+ my $res = $include->read_all(); # is a reference to an array
+ foreach ( @$res ) {
+ chomp;
+ $ly->print($_);
+ }
+ $ly->close();
+
+ if ( exists $eps_subs{ $Args->{'eps_func'} } ) {
+ $eps_subs{ $Args->{'eps_func'} }->();
+ } else {
+ die "Wrong argument for \$eps_subs: '" . $Args->{'eps_func'} . "'";
+ }
+ }, # end '.lilypond include'
+
+ ); # end definition %lilypond_args
+
+
+ LILYPOND: foreach my $filename (@ARGV) {
+ my $input;
+ if ($filename eq '-') {
+ $input = \*STDIN;
+ } elsif (not open $input, '<', $filename) {
+ warn $!;
+ next;
+ }
+ while (<$input>) {
+ chomp;
+ my $line = $_;
+
+
+ # now the lines with '.lilypond ...'
+
+ if ( /
+ ^
+ [.']
+ \s*
+ lilypond
+ (
+ .*
+ )
+ $
+ /x ) { # .lilypond ...
+ my $args = $1;
+ $args =~ s/
+ ^
+ \s*
+ //x;
+ $args =~ s/
+ \s*
+ $
+ //x;
+ $args =~ s/
+ ^
+ (
+ \S*
+ )
+ \s*
+ //x;
+ my $arg1 = $1; # 'start', 'end' or 'include'
+ $args =~ s/["'`]//g;
+ my $arg2 = $args; # file argument for '.lilypond include'
+
+ if ( exists $lilypond_args{$arg1} ) {
+ $lilypond_args{$arg1}->($arg2);
+ next;
+ } else {
+ # not a suitable argument of '.lilypond'
+ $stderr->print( "Unknown command: '$arg1' '$arg2': '$line'" );
+ }
+
+ next LILYPOND;
+ } # end if for .lilypond
+
+
+ if ( $lilypond_mode ) { # do lilypond-mode
+ # see '.lilypond start'
+ $ly->print( $line );
+ next LILYPOND;
+ } # do lilypond-mode
+
+ # unknown line without lilypond
+ unless ( /
+ ^
+ [.']
+ \s*
+ lilypond
+ /x ) { # not a '.lilypond' line
+ $out->print($line);
+ next LILYPOND;
+ }
+ } # end while <$input>
+ } # end foreach $filename
+} # end Read
+
+
+########################################################################
+# clean up
+########################################################################
+
+END {
+
+ exit unless ( defined($Temp->{'temp_dir'}) );
+
+ if ( $Args->{'keep_all'} ) {
+ # With --keep_all, no temporary files are removed.
+ $v->print( "keep_all: 'TRUE'" );
+ $v->print( "No temporary files will be deleted:" );
+
+ opendir my $dh_temp, $Temp->{'temp_dir'} or
+ die "Cannot open " . $Temp->{'temp_dir'} . ": $!";
+ for ( sort readdir $dh_temp ) {
+ next if ( / # omit files starting with a dot
+ ^
+ \.
+ /x );
+ if ( /
+ ^
+ $Args->{'prefix'}
+ _
+ /x ) {
+ my $file = File::Spec->catfile( $Temp->{'temp_dir'}, $_ );
+ $v->print( "- " . $file );
+ next;
+ }
+ next;
+ } # end for sort readdir
+ closedir $dh_temp;
+
+ } else { # keep_all is not set
+ # Remove all temporary files except the eps files.
+
+ $v->print( "keep_all: 'FALSE'" );
+ $v->print( "All temporary files except *.eps will be deleted" );
+
+
+ if ( $Temp->{'eps_dir'} ) {
+ # EPS files are in another dir, remove temp dir
+
+ if ( &is_subdir( $Temp->{'eps_dir'}, $Temp->{'temp_dir'} ) ) {
+ $v->print( "EPS dir is subdir of temp dir, so keep both." );
+ } else { # remove temp dir
+ $v->print( "Try to remove temporary directory '" .
+ $Temp->{'temp_dir'} ."':" );
+ if ( File::Path::remove_tree($Temp->{'temp_dir'}) ) {
+ # remove succeeds
+ $v->print( "...done." );
+ } else { # did not remove
+ $v->print( "Failure to remove temporary directory." );
+ } # end test on remove
+ } # end is subdir
+
+ } else { # no EPS dir, so keep EPS files
+
+ opendir my $dh_temp, $Temp->{'temp_dir'} or
+ die "Cannot open " . $Temp->{'temp_dir'} . ": $!";
+ for ( sort readdir $dh_temp ) {
+ next if ( / # omit files starting with a dot
+ ^
+ \.
+ /x );
+ next if ( / # omit EPS-files
+ \.eps
+ $
+ /x );
+ if ( /
+ ^
+ $Args->{'prefix'}
+ _
+ /x ) { # this includes 'PREFIX_temp*'
+ my $file = File::Spec->catfile( $Temp->{'temp_dir'}, $_ );
+ $v->print( "Remove '" . $file . "'" );
+ unlink $file or $stderr->print( "Could not remove '$file': $!" );
+ next;
+ } # end if prefix
+ next;
+ } # end for readdir temp dir
+ closedir $dh_temp;
+ } # end if-else EPS files
+ } # end if-else keep files
+
+
+ if ( $Temp->{'eps_dir'} ) {
+ # EPS files in $Temp->{'eps_dir'} are always kept
+ $v->print( "As EPS directory is set as '" .
+ $Temp->{'eps_dir'} . "', no EPS files there will be deleted." );
+
+ opendir my $dh_temp, $Temp->{'eps_dir'} or
+ die "Cannot open '" . $Temp->{'eps_dir'} . ": $!";
+ for ( sort readdir $dh_temp ) {
+ next if ( / # omit files starting with a dot
+ ^
+ \.
+ /x );
+ if ( /
+ ^
+ $Args->{'prefix'}
+ _
+ .*
+ \.eps
+ $
+ /x ) {
+ my $file = File::Spec->catfile( $Temp->{'eps_dir'}, $_ );
+ $v->print( "- " . $file );
+ next;
+ } # end if *.eps
+ next;
+ } # end for sort readdir
+ closedir $dh_temp;
+
+ }
+
+ 1;
+} # end package Clean
+
+
+1;
+# Local Variables:
+# fill-column: 72
+# mode: CPerl
+# End:
+# vim: set autoindent textwidth=72: