5 ########################################################################
7 ########################################################################
9 # See `Mastering Perl', chapter 4.
16 $SIG[__DIE__] = sub { &Carp::croak; };
20 ########################################################################
22 ########################################################################
27 use constant VERSION => 'v1.3.1'; # version of glilypond
29 ### This constant `LICENSE' is the license for this file `GPL' >= 2
30 use constant LICENSE => q*
31 glilypond - integrate `lilypond' into `groff' files
33 Source file position: `<groff-source>/contrib/glilypond/glilypond.pl'
34 Installed position: `<prefix>/bin/glilypond'
36 Copyright (C) 2013-2014 Free Software Foundation, Inc.
37 Written by Bernd Warken <groff-bernd.warken-72@web.de>
39 This file is part of `GNU groff'.
41 `GNU groff' is free software: you can redistribute it and/or modify it
42 under the terms of the `GNU General Public License' as published by the
43 `Free Software Foundation', either version 2 of the License, or (at your
44 option) any later version.
46 `GNU groff' is distributed in the hope that it will be useful, but
47 WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the `GNU
49 General Public License' for more details.
51 You should have received a copy of the 'GNU General Public License`
52 along with `groff', see the files `COPYING' and `LICENSE' in the top
53 directory of the `groff' source package. If not, see
54 <http://www.gnu.org/licenses/>.
69 ########################################################################
70 # global variables and BEGIN
71 ########################################################################
77 use File::Basename qw[];
79 use File::HomeDir qw[];
89 use constant FALSE => 0;
90 use constant TRUE => 1;
91 use constant EMPTYSTRING => '';
92 use constant EMPTYARRAY => ();
93 use constant EMPTYHASH => ();
97 'before_make' => FALSE,
98 'groff_version' => EMPTYSTRING,
99 'prog' => EMPTYSTRING,
103 ( my $volume, my $directory, $Globals->{'prog'} ) =
104 File::Spec->splitpath($0);
105 # $Globals->{'prog'} is `glilypond' when installed,
106 # `glilypond.pl' when not
110 $\ = "\n"; # adds newline at each print
111 $/ = "\n"; # newline separates input
112 $| = 1; # flush after each print or write command
117 # script before run of `make'
119 $Globals->{'before_make'} = TRUE if '@VERSION@' eq "${at}VERSION${at}";
123 my $glilypond_libdir;
125 if ( $Globals->{'before_make'} ) { # in source, not yet installed
126 my $glilypond_dir = $FindBin::Bin;
127 $glilypond_dir = Cwd::realpath($glilypond_dir);
128 $glilypond_libdir = $glilypond_dir;
130 } else { # already installed
131 $Globals->{'groff_version'} = '@VERSION@';
132 $glilypond_libdir = '@glilypond_dir@';
135 unshift(@INC, $glilypond_libdir);
137 umask 0077; # octal output: `printf "%03o", umask;'
144 ########################################################################
145 # OOP declarations for some file handles
146 ########################################################################
150 our $stdout = new FH_STDOUT();
151 our $stderr = new FH_STDERR();
153 # verbose printing, not clear wether this will be set by `--verbose',
154 # so store this now into a string, which can be gotten later on, when
155 # it will become either STDERR or /dev/null
156 our $v = new FH_STRING();
158 # for standard output, either STDOUT or output file
164 ########################################################################
165 # Args: command line arguments
166 ########################################################################
168 # command line arguments are handled in 2 runs:
169 # 1) split short option collections, `=' optargs, and transfer abbrevs
170 # 2) handle the transferred options with subs
174 'eps_dir' => EMPTYSTRING, # can be overwritten by `--eps_dir'
176 # `eps-func' has 2 possible values:
177 # 1) `ly' from `--ly2eps' (default)
178 # 2) `pdf' `--pdf2eps'
181 # files names of temporary files start with this string,
182 # can be overwritten by `--prefix'
185 # delete or do not delete temporary files
188 # the roff output goes normally to STDOUT, can be a file with `--output'
189 'output' => EMPTYSTRING,
191 # temporary directory, can be overwritten by `--temp_dir',
192 # empty for default of the program
193 'temp_dir' => EMPTYSTRING,
195 # regulates verbose output (on STDERR), overwritten by `--verbose'
210 ########################################################################
211 # temporary directory .../tmp/groff/USER/lilypond/TIME
212 ########################################################################
216 # store the current directory
217 'cwd' => Cwd::getcwd(),
219 # directory for EPS files
220 'eps_dir' => EMPTYSTRING,
222 # temporary directory
223 'temp_dir' => EMPTYSTRING,
228 if ( $Args->{'temp_dir'} ) {
231 # temporary directory was set by `--temp_dir'
234 my $dir = $Args->{'temp_dir'};
236 $dir = &path2abs($dir);
237 $dir = &make_dir($dir) or
238 die "The directory `$dir' cannot be used temporarily: $!";
241 # now `$dir' is a writable directory
243 opendir( my $dh, $dir ) or
244 die "Could not open temporary directory `$dir': $!";
247 my $prefix = $Args->{'prefix'};
254 READDIR: while ( defined($file_name = readdir($dh)) ) {
256 if ( $file_name =~ /$re/ ) { # file name starts with $prefix_
263 $Temp->{'temp_dir'} = $dir;
266 $dir = File::Spec->catdir( $Temp->{'temp_dir'}, ++$n );
269 $dir = &make_dir($dir) or next;
275 $Temp->{'temp_dir'} = $dir;
278 } else { # $Args->{'temp_dir'} not given by `--temp_dir'
281 # temporary directory was not set
284 { # search for or create a temporary directory
286 my @tempdirs = EMPTYARRAY;
288 my $tmpdir = File::Spec->tmpdir();
289 push @tempdirs, $tmpdir if ( $tmpdir && -d $tmpdir && -w $tmpdir );
291 my $root_dir = File::Spec->rootdir(); # `/' in Unix
292 my $root_tmp = File::Spec->catdir($root_dir, 'tmp');
293 push @tempdirs, $root_tmp
294 if ( $root_tmp ne $tmpdir && -d $root_tmp && -w $root_tmp );
296 # home directory of the actual user
297 my $home = File::HomeDir->my_home;
298 my $home_tmp = File::Spec->catdir($home, 'tmp');
299 push @tempdirs, $home_tmp if ( -d $home_tmp && -w $home_tmp );
302 my $var_tmp = File::Spec->catdir('', 'var', 'tmp');
303 push @tempdirs, $var_tmp if ( -d $var_tmp && -w $var_tmp );
307 my @path_extension = qw( groff ); # TEMPDIR/groff/USER/lilypond/<NUMBER>
309 # `$<' is UID of actual user,
310 # `getpwuid' gets user name in scalar context
311 my $user = getpwuid($<);
312 push @path_extension, $user if ( $user );
314 push @path_extension, qw( lilypond );
318 TEMPS: foreach ( @tempdirs ) {
320 my $dir; # final directory name in `while' loop
321 $dir = &path2abs($_);
322 next TEMPS unless ( $dir );
324 # beginning of directory name
326 ( File::Spec->splitdir($dir), @path_extension );
330 my $dir_blocked = TRUE;
331 BLOCK: while ( $dir_blocked ) {
332 # should become the final dir name
333 $dir = File::Spec->catdir(@dir_begin, ++$n);
334 next BLOCK if ( -d $dir );
336 # dir name is now free, create it, and end the blocking
337 my $res = &make_dir( $dir );
338 die "Could not create directory: $dir" unless ( $res );
341 $dir_blocked = FALSE;
344 next TEMPS unless ( -d $dir && -w $dir );
346 # $dir is now a writable directory
347 $Temp->{'temp_dir'} = $dir; # tmp/groff/USER/lilypond/TIME
349 } # end foreach tmp directories
350 } # end to create a temporary directory
352 die "Could not find a temporary directory" unless
353 ( $Temp->{'temp_dir'} && -d $Temp->{'temp_dir'} &&
354 -w $Temp->{'temp_dir'} );
356 } # end temporary directory
358 $v->print( "Temporary directory: `" . $Temp->{'temp_dir'} . "'\n" );
359 $v->print( "file_prefix: `" . $Args->{'prefix'} . "'" );
366 my $make_dir = FALSE;
367 if ( $Args->{'eps_dir'} ) { # set by `--eps_dir'
368 my $dir = $Args->{'eps_dir'};
370 $dir = &path2abs($dir);
373 goto EMPTY unless ( -w $dir );
377 my $upper_dir = $dir;
380 opendir( my $dh, $upper_dir ) or $found = TRUE;
381 my $prefix = $Args->{'prefix'};
387 while ( not $found ) {
388 my $file_name = readdir($dh);
389 if ( $file_name =~ /$re/ ) { # file name starts with $prefix_
398 $dir = File::Spec->catdir($upper_dir, ++$n);
403 $Temp->{'eps_dir'} = $dir;
404 } else { # `$dir' is not a dir, so unlink it to create it as dir
405 if ( unlink $dir ) { # could remove `$dir'
406 $Temp->{'eps_dir'} = $dir;
408 } else { # could not remove
409 stderr->print( "Could not use EPS dir `" . $dir .
410 "', use temp dir." );
412 } # end test of -d $dir
415 } # end of if -e $dir
418 if ( $make_dir ) { # make directory `$dir'
420 $dir = &make_dir($dir) and $made = TRUE;
423 $Temp->{'eps_dir'} = $dir;
424 $v->print( "Directory for useful EPS files is `" . $dir . "'." );
426 $v->print( "The EPS directory `" . $dir . "' cannot be used: $!" );
428 } else { # `--eps_dir' was not set, so take the temporary directory
429 $Temp->{'eps_dir'} = $Args->{'temp_dir'};
433 EMPTY: unless ( $Temp->{'eps_dir'} ) {
434 # EPS-dir not set or available, use temp dir,
435 # but leave $Temp->{'}eps_dir'} empty
436 $v->print( "Directory for useful EPS files is the " .
437 "temporary directory `" . $Temp->{'temp_dir'} . "'." );
443 ########################################################################
444 # Read: read files or stdin
445 ########################################################################
449 'file_numbered' => EMPTYSTRING,
450 'file_ly' => EMPTYSTRING, # `$file_numbered.ly'
453 { # read files or stdin
455 my $ly_number = 0; # number of lilypond file
457 # `$Args->{'prefix'}_[0-9]'
459 my $lilypond_mode = FALSE;
461 my $arg1; # first argument for `.lilypond'
462 my $arg2; # argument for `.lilypond include'
464 my $path_ly; # path of ly-file
467 my $check_file = sub { # for argument of `.lilypond include'
468 my $file = shift; # argument is a file name
469 $file = &path2abs($file);
471 die "Line `.lilypond include' without argument";
474 unless ( -f $file && -r $file ) {
475 die "Argument `$file' in `.lilypond include' is not a readable file";
479 }; # end sub &$check_file()
482 my $increase_ly_number = sub {
484 $Read->{'file_numbered'} = $Args->{'prefix'} . '_' . $ly_number;
485 $Read->{'file_ly'} = $Read->{'file_numbered'} . '.ly';
486 $path_ly = File::Spec->catdir($Temp->{'temp_dir'}, $Read->{'file_ly'} );
492 'ly' => \&create_ly2eps, # lilypond creates EPS files
493 'pdf' => \&create_pdf2eps, # lilypond creates PDF file
496 # about lines starting with `.lilypond'
504 $v->print( "\nline: `.lilypond start'" );
505 die "Line `.lilypond stop' expected." if ( $lilypond_mode );
507 $lilypond_mode = TRUE;
508 &$increase_ly_number;
510 $v->print( "ly-file: `" . $path_ly . "'" );
512 $ly = new FH_FILE($path_ly);
517 $v->print( "line: `.lilypond end'\n" );
518 die "Expected line `.lilypond start'." unless ( $lilypond_mode );
520 $lilypond_mode = FALSE;
523 if ( exists $eps_subs{ $Args->{'eps_func'} } ) {
524 $eps_subs{ $Args->{'eps_func'} }->();
526 die "Wrong argument for \%eps_subs: " . $Args->{'eps_func'} . "'";
531 'include' => sub { # `.lilypond include file...'
533 # this may not be used within lilypond mode
534 next LILYPOND if ( $lilypond_mode );
536 my $file_arg = shift;
538 my $file = &$check_file($file_arg);
539 next LILYPOND unless ( $file );
540 # file can be read now
543 # `$fh_write_ly' must be opened
544 &$increase_ly_number;
546 $ly = new FH_FILE($path_ly);
548 my $include = new FH_READ_FILE($file);
549 my $res = $include->read-all(); # is a refernce to an array
556 if ( exists $eps_subs{ $Args->{'eps_func'} } ) {
557 $eps_subs{ $Args->{'eps_func'} }->();
559 die "Wrong argument for \$eps_subs: `" . $Args->{'eps_func'} . "'";
561 }, # end `.lilypond include'
563 ); # end definition %lilypond_args
566 LILYPOND: foreach (<>) {
571 # now the lines with '.lilypond ...'
582 /x ) { # .lilypond ...
599 my $arg1 = $1; # `start', `end' or `include'
601 my $arg2 = $args; # file argument for `.lilypond include'
603 if ( exists $lilypond_args{$arg1} ) {
604 $lilypond_args{$arg1}->($arg2);
607 # not a suitable argument of `.lilypond'
608 $stderr->print( "Unknown command: `$arg1' `$arg2': `$line'" );
612 } # end if for .lilypond
615 if ( $lilypond_mode ) { # do lilypond-mode
616 # see `.lilypond start'
621 # unknown line without lilypond
627 /x ) { # not a `.lilypond' line
636 ########################################################################
638 ########################################################################
642 exit unless ( defined($Temp->{'temp_dir'}) );
644 if ( $Args->{'keep_all'} ) {
645 # With --keep_all, no temporary files are removed.
646 $v->print( "keep_all: `TRUE'" );
647 $v->print( "No temporary files will be deleted:" );
649 opendir my $dh_temp, $Temp->{'temp_dir'} or
650 die "Cannot open " . $Temp->{'temp_dir'} . ": $!";
651 for ( sort readdir $dh_temp ) {
652 next if ( / # omit files starting with a dot
661 my $file = File::Spec->catfile( $Temp->{'temp_dir'}, $_ );
662 $v->print( "- " . $file );
666 } # end for sort readdir
669 } else { # keep_all is not set
670 # Remove all temporary files except the eps files.
672 $v->print( "keep_all: `FALSE'" );
673 $v->print( "All temporary files except *.eps will be deleted" );
676 if ( $Temp->{'eps_dir'} ) {
677 # EPS files are in another dir, remove temp dir
679 if ( &is_subdir( $Temp->{'eps_dir'}, $Temp->{'temp_dir'} ) ) {
680 $v->print( "EPS dir is subdir of temp dir, so keep both." );
681 } else { # remove temp dir
682 $v->print( "Try to remove temporary directory `" .
683 $Temp->{'temp_dir'} ."':" );
684 if ( File::Path::remove_tree($Temp->{'temp_dir'}) ) {
686 $v->print( "...done." );
687 } else { # did not remove
688 $v->print( "Failure to remove temporary directory." );
689 } # end test on remove
692 } else { # no EPS dir, so keep EPS files
694 opendir my $dh_temp, $Temp->{'temp_dir'} or
695 die "Cannot open " . $Temp->{'temp_dir'} . ": $!";
696 for ( sort readdir $dh_temp ) {
697 next if ( / # omit files starting with a dot
701 next if ( / # omit EPS-files
709 /x ) { # this includes `PREFIX_temp*'
710 my $file = File::Spec->catfile( $Temp->{'temp_dir'}, $_ );
711 $v->print( "Remove `" . $file . "'" );
712 unlink $file or $stderr->print( "Could not remove `$file': $!" );
716 } # end for readdir temp dir
718 } # end if-else EPS files
719 } # end if-else keep files
722 if ( $Temp->{'eps_dir'} ) {
723 # EPS files in $Temp->{'eps_dir'} are always kept
724 $v->print( "As EPS directrory is set as `" .
725 $Temp->{'eps_dir'} . "', no EPS files there will be deleted." );
727 opendir my $dh_temp, $Temp->{'eps_dir'} or
728 die "Cannot open `" . $Temp->{'eps_dir'} . ": $!";
729 for ( sort readdir $dh_temp ) {
730 next if ( / # omit files starting with a dot
742 my $file = File::Spec->catfile( $Temp->{'eps_dir'}, $_ );
743 $v->print( "- " . $file );
747 } # end for sort readdir
753 } # end package Clean
757 ########################################################################