diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:44:05 +0000 |
commit | d318611dd6f23fcfedd50e9b9e24620b102ba96a (patch) | |
tree | 8b9eef82ca40fdd5a8deeabf07572074c236095d /contrib/glilypond | |
parent | Initial commit. (diff) | |
download | groff-upstream/1.23.0.tar.xz groff-upstream/1.23.0.zip |
Adding upstream version 1.23.0.upstream/1.23.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/glilypond')
-rw-r--r-- | contrib/glilypond/ChangeLog | 290 | ||||
-rw-r--r-- | contrib/glilypond/ChangeLog.0x | 104 | ||||
-rw-r--r-- | contrib/glilypond/README.txt | 43 | ||||
-rw-r--r-- | contrib/glilypond/examples/example.groff | 46 | ||||
-rw-r--r-- | contrib/glilypond/glilypond.1.man | 881 | ||||
-rw-r--r-- | contrib/glilypond/glilypond.am | 64 | ||||
-rwxr-xr-x | contrib/glilypond/glilypond.pl | 1907 |
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: |