]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
96f0a43b638659b2387a330410e191ae232f95f0
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2
3 # Run lilypond, latex, dvips.
4 #
5 # This is the third incarnation of ly2dvi.
6 #
7 # Earlier incarnations of ly2dvi were written by
8 # Jeffrey B. Reed<daboys@austin.rr.com> (Python version)
9 # Jan Arne Fagertun <Jan.A.Fagertun@@energy.sintef.no> (Bourne shell script)
10 #
11
12 #
13 # Note: gettext work best if we use ' for docstrings and "
14 #       for gettextable strings.
15 #       --> DO NOT USE """ for docstrings.
16
17 '''
18 TODO:
19
20   * figure out which set of command line options should make ly2dvi:
21
22       na: create tex only?  
23       na: create latex only? 
24       na: create tex and latex
25       default: create dvi only
26       na: create tex, latex and dvi
27       -P: create dvi and ps
28       na: * create ps only
29
30      etc.
31
32      for foo.ly, rename ly2dvi.dir to out-ly2dvi, foo.ly2dvi, foo.dir ?
33      
34   * move versatile taglines, 
35   
36      \header {
37         beginfooter=\mutopiaPD
38         endfooter=\tagline  -> 'lily was here <version>'
39      }
40
41      lilytagline (->lily was here), usertagline, copyright etc.
42
43   * head/header tagline/endfooter
44
45   * dvi from lilypond .tex output?  This is hairy, because we create dvi
46     from lilypond .tex *and* header output.
47
48   * multiple \score blocks?
49   
50 '''
51
52
53 import os
54 import stat
55 import string
56 import re
57 import getopt
58 import sys
59 import shutil
60 import __main__
61 import operator
62 import tempfile
63 import traceback
64
65
66 ################################################################
67 # lilylib.py -- options and stuff
68
69 # source file of the GNU LilyPond music typesetter
70
71 # Handle bug in Python 1.6-2.1
72 #
73 # there are recursion limits for some patterns in Python 1.6 til 2.1. 
74 # fix this by importing pre instead. Fix by Mats.
75
76 # todo: should check Python version first.
77 try:
78         import pre
79         re = pre
80         del pre
81 except ImportError:
82         import re
83
84 # Attempt to fix problems with limited stack size set by Python!
85 # Sets unlimited stack size. Note that the resource module only
86 # is available on UNIX.
87 try:
88        import resource
89        resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
90 except:
91        pass
92
93 try:
94         import gettext
95         gettext.bindtextdomain ('lilypond', localedir)
96         gettext.textdomain ('lilypond')
97         _ = gettext.gettext
98 except:
99         def _ (s):
100                 return s
101
102 program_version = '@TOPLEVEL_VERSION@'
103 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
104         program_version = '1.5.54'
105
106 def identify ():
107         sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
108
109 def warranty ():
110         identify ()
111         sys.stdout.write ('\n')
112         sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2002'))
113         sys.stdout.write ('\n')
114         sys.stdout.write ('  Han-Wen Nienhuys')
115         sys.stdout.write ('  Jan Nieuwenhuizen')
116         sys.stdout.write ('\n')
117         sys.stdout.write (_ (r'''
118 Distributed under terms of the GNU General Public License. It comes with
119 NO WARRANTY.'''))
120         sys.stdout.write ('\n')
121
122 def progress (s):
123         errorport.write (s + '\n')
124
125 def warning (s):
126         progress (_ ("warning: ") + s)
127
128 def user_error (s, e=1):
129         errorport.write (program_name + ":" + _ ("error: ") + s + '\n')
130         sys.exit (e)
131         
132 def error (s):
133         '''Report the error S.  Exit by raising an exception. Please
134         do not abuse by trying to catch this error. If you do not want
135         a stack trace, write to the output directly.
136
137         RETURN VALUE
138
139         None
140         
141         '''
142         
143         progress (_ ("error: ") + s)
144         raise _ ("Exiting ... ")
145
146 def getopt_args (opts):
147         '''Construct arguments (LONG, SHORT) for getopt from  list of options.'''
148         short = ''
149         long = []
150         for o in opts:
151                 if o[1]:
152                         short = short + o[1]
153                         if o[0]:
154                                 short = short + ':'
155                 if o[2]:
156                         l = o[2]
157                         if o[0]:
158                                 l = l + '='
159                         long.append (l)
160         return (short, long)
161
162 def option_help_str (o):
163         '''Transform one option description (4-tuple ) into neatly formatted string'''
164         sh = '  '       
165         if o[1]:
166                 sh = '-%s' % o[1]
167
168         sep = ' '
169         if o[1] and o[2]:
170                 sep = ','
171                 
172         long = ''
173         if o[2]:
174                 long= '--%s' % o[2]
175
176         arg = ''
177         if o[0]:
178                 if o[2]:
179                         arg = '='
180                 arg = arg + o[0]
181         return '  ' + sh + sep + long + arg
182
183
184 def options_help_str (opts):
185         '''Convert a list of options into a neatly formatted string'''
186         w = 0
187         strs =[]
188         helps = []
189
190         for o in opts:
191                 s = option_help_str (o)
192                 strs.append ((s, o[3]))
193                 if len (s) > w:
194                         w = len (s)
195
196         str = ''
197         for s in strs:
198                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
199         return str
200
201 def help ():
202         ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
203                 ('\n\n'),
204                 (help_summary),
205                 ('\n\n'),
206                 (_ ("Options:")),
207                 ('\n'),
208                 (options_help_str (option_definitions)),
209                 ('\n\n'),
210                 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
211                 ('\n')]
212         map (sys.stdout.write, ls)
213         
214 def setup_temp ():
215         """
216         Create a temporary directory, and return its name. 
217         """
218         global temp_dir
219         if not keep_temp_dir_p:
220                 temp_dir = tempfile.mktemp (program_name)
221         try:
222                 os.mkdir (temp_dir, 0777)
223         except OSError:
224                 pass
225
226         return temp_dir
227
228
229 def system (cmd, ignore_error = 0):
230         """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
231
232         RETURN VALUE
233
234         Exit status of CMD
235         """
236         
237         if verbose_p:
238                 progress (_ ("Invoking `%s\'") % cmd)
239
240         st = os.system (cmd)
241         if st:
242                 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
243                 msg = name + ': ' + _ ("command exited with value %d") % st
244                 if ignore_error:
245                         warning (msg + ' ' + _ ("(ignored)") + ' ')
246                 else:
247                         error (msg)
248
249         return st
250
251
252 def cleanup_temp ():
253         if not keep_temp_dir_p:
254                 if verbose_p:
255                         progress (_ ("Cleaning %s...") % temp_dir)
256                 shutil.rmtree (temp_dir)
257
258
259 def strip_extension (f, ext):
260         (p, e) = os.path.splitext (f)
261         if e == ext:
262                 e = ''
263         return p + e
264
265
266 def cp_to_dir (pattern, dir):
267         "Copy files matching re PATTERN from cwd to DIR"
268         # Duh.  Python style portable: cp *.EXT OUTDIR
269         # system ('cp *.%s %s' % (ext, outdir), 1)
270         files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
271         map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
272
273
274 # Python < 1.5.2 compatibility
275 #
276 # On most platforms, this is equivalent to
277 #`normpath(join(os.getcwd()), PATH)'.  *Added in Python version 1.5.2*
278 if os.path.__dict__.has_key ('abspath'):
279         abspath = os.path.abspath
280 else:
281         def abspath (path):
282                 return os.path.normpath (os.path.join (os.getcwd (), path))
283
284 if os.__dict__.has_key ('makedirs'):
285         makedirs = os.makedirs
286 else:
287         def makedirs (dir, mode=0777):
288                 system ('mkdir -p %s' % dir)
289
290
291 def mkdir_p (dir, mode=0777):
292         if not os.path.isdir (dir):
293                 makedirs (dir, mode)
294
295
296 # if set, LILYPONDPREFIX must take prevalence
297 # if datadir is not set, we're doing a build and LILYPONDPREFIX 
298 datadir = '@datadir@'
299
300 if os.environ.has_key ('LILYPONDPREFIX') :
301         datadir = os.environ['LILYPONDPREFIX']
302 else:
303         datadir = '@datadir@'
304
305
306 while datadir[-1] == os.sep:
307         datadir= datadir[:-1]
308
309 sys.path.insert (0, os.path.join (datadir, 'python'))
310
311 ################################################################
312 # END Library
313
314
315 program_name = 'ly2dvi'
316
317 original_dir = os.getcwd ()
318 temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
319 errorport = sys.stderr
320 keep_temp_dir_p = 0
321 verbose_p = 0
322 preview_p = 0
323 preview_resolution = 90
324
325 help_summary = _ ("Generate .dvi with LaTeX for LilyPond")
326
327 option_definitions = [
328         ('', 'd', 'dependencies', _ ("write Makefile dependencies for every input file")),
329         ('', 'h', 'help', _ ("this help")),
330         (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
331         ('', 'k', 'keep', _ ("keep all output, and name the directory %s.dir") % program_name),
332         ('', '', 'no-lily', _ ("don't run LilyPond")),
333         ('', 'm', 'no-paper', _ ("produce MIDI output only")),
334         (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
335         (_ ("FILE"), 'f', 'find-pfa', _ ("find pfa fonts used in FILE")),
336         # why capital P?
337         ('', '', 'preview', _("Make a picture of the first system.")),
338         (_ ('RES'), '', 'preview-resolution', _("Set the resolution of the preview to RES.")),
339         ('', 'P', 'postscript', _ ("generate PostScript output")),
340         (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
341         ('', 'V', 'verbose', _ ("verbose")),
342         ('', 'v', 'version', _ ("print version number")),
343         ('', 'w', 'warranty', _ ("show warranty and copyright")),
344         ]
345
346 layout_fields = ['dedication', 'title', 'subtitle', 'subsubtitle',
347           'footer', 'head', 'composer', 'arranger', 'instrument',
348           'opus', 'piece', 'metre', 'meter', 'poet', 'texttranslator']
349
350
351 # init to empty; values here take precedence over values in the file
352
353 ## TODO: change name.
354 extra_init = {
355         'language' : [],
356         'latexheaders' : [],
357         'latexpackages' :  ['geometry'],
358         'latexoptions' : [],
359         'papersize' : [],
360         'pagenumber' : [1],
361         'textheight' : [], 
362         'linewidth' : [],
363         'orientation' : [],
364         'unit' : ['pt'],
365 }
366
367 extra_fields = extra_init.keys ()
368 fields = layout_fields + extra_fields
369
370 include_path = ['.']
371 lily_p = 1
372 paper_p = 1
373
374 output_name = ''
375
376 ## docme: what does this do?
377 targets = [ 'DVI', 'LATEX', 'MIDI', 'TEX']
378
379 track_dependencies_p = 0
380 dependency_files = []
381
382
383
384 kpse = os.popen ('kpsexpand \$TEXMF').read()
385 kpse = re.sub('[ \t\n]+$','', kpse)
386 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
387
388 environment = {
389         # TODO: * prevent multiple addition.
390         #       * clean TEXINPUTS, MFINPUTS, TFMFONTS,
391         #         as these take prevalence over $TEXMF
392         #         and thus may break tex run?
393         'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
394         'GS_FONTPATH' : type1_paths,
395         'GS_LIB' : datadir + '/ps',
396 }
397
398 # tex needs lots of memory, more than it gets by default on Debian
399 non_path_environment = {
400         'extra_mem_top' : '1000000',
401         'extra_mem_bottom' : '1000000',
402         'pool_size' : '250000',
403 }
404
405 def setup_environment ():
406         # $TEXMF is special, previous value is already taken care of
407         if os.environ.has_key ('TEXMF'):
408                 del os.environ['TEXMF']
409  
410         for key in environment.keys ():
411                 val = environment[key]
412                 if os.environ.has_key (key):
413                         val = os.environ[key] + os.pathsep + val 
414                 os.environ[key] = val
415
416         for key in non_path_environment.keys ():
417                 val = non_path_environment[key]
418                 os.environ[key] = val
419
420 #what a name.
421 def set_setting (dict, key, val):
422         try:
423                 val = string.atof (val)
424         except ValueError:
425                 #warning (_ ("invalid value: %s") % `val`)
426                 pass
427
428         try:
429                 dict[key].append (val)
430         except KeyError:
431                 warning (_ ("no such setting: %s") % `key`)
432                 dict[key] = [val]
433
434
435 def print_environment ():
436         for (k,v) in os.environ.items ():
437                 sys.stderr.write ("%s=\"%s\"\n" % (k,v)) 
438
439 def quiet_system (cmd, name):
440         if not verbose_p:
441                 progress ( _("Running %s...") % name)
442                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
443
444         return system (cmd)
445
446
447 def run_lilypond (files, outbase, dep_prefix):
448         opts = ''
449 #       opts = opts + '--output=%s.tex' % outbase
450         opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
451                                               include_path))
452         if paper_p:
453                 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
454                                                       fields))
455         else:
456                 opts = opts + ' --no-paper'
457                 
458         if track_dependencies_p:
459                 opts = opts + " --dependencies"
460                 if dep_prefix:
461                         opts = opts + ' --dep-prefix=%s' % dep_prefix
462
463         fs = string.join (files)
464
465         if not verbose_p:
466                 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
467                 progress ( _("Running %s...") % 'LilyPond')
468         else:
469                 opts = opts + ' --verbose'
470
471                 # for better debugging!
472                 print_environment ()
473         print opts, fs  
474         system ('lilypond %s %s ' % (opts, fs), 'lilypond')
475
476 def analyse_lilypond_output (filename, extra):
477         
478         # urg
479         '''Grep FILENAME for interesting stuff, and
480         put relevant info into EXTRA.'''
481         filename = filename+'.tex'
482         progress (_ ("Analyzing %s...") % filename)
483         s = open (filename).read ()
484
485         # search only the first 10k
486         s = s[:10240]
487         for x in extra_fields:
488                 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
489                 if m:
490                         set_setting (extra, x, m.group (1))
491
492 def find_tex_files_for_base (base, extra):
493
494         """
495         Find the \header fields dumped from BASE.
496         """
497         
498         headerfiles = {}
499         for f in layout_fields:
500                 if os.path.exists (base + '.' + f):
501                         headerfiles[f] = base+'.'+f
502
503         if os.path.exists (base  +'.dep'):
504                 dependency_files.append (base + '.dep')
505
506         for f in extra_fields:
507                 if os.path.exists (base + '.' + f):
508                         extra[f].append (open (base + '.' + f).read ())
509         
510         return (base  +'.tex',headerfiles)
511          
512
513 def find_tex_files (files, extra):
514         """
515         Find all .tex files whose prefixes start with some name in FILES. 
516
517         """
518         
519         tfiles = []
520         
521         for f in files:
522                 x = 0
523                 while 1:
524                         fname = os.path.basename (f)
525                         fname = strip_extension (fname, '.ly')
526                         if x:
527                                 fname = fname + '-%d' % x
528
529                         if os.path.exists (fname + '.tex'):
530                                 tfiles.append (find_tex_files_for_base (fname, extra))
531                                 analyse_lilypond_output (fname, extra)
532                         else:
533                                 break
534
535                         x = x + 1
536         if not x:
537                 fstr = string.join (files, ', ')
538                 warning (_ ("no lilypond output found for %s") % fstr)
539         return tfiles
540
541 def one_latex_definition (defn, first):
542         s = '\n'
543         for (k,v) in defn[1].items ():
544                 val = open (v).read ()
545                 if (string.strip (val)):
546                         s = s + r'''\def\lilypond%s{%s}''' % (k, val)
547                 else:
548                         s = s + r'''\let\lilypond%s\relax''' % k
549                 s = s + '\n'
550
551         if first:
552                 s = s + '\\def\\mustmakelilypondtitle{}\n'
553         else:
554                 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
555                 
556         s = s + '\\input %s\n' % defn[0] # The final \n seems important here. It ensures that the footers and taglines end up on the right page.
557         return s
558
559
560 ly_paper_to_latexpaper =  {
561         'a4' : 'a4paper',
562         'letter' : 'letterpaper', 
563 }
564
565 #TODO: should set textheight (enlarge) depending on papersize. 
566 def global_latex_preamble (extra):
567         '''construct preamble from EXTRA,'''
568         s = ""
569         s = s + '% generation tag\n'
570
571         options = ''
572
573
574         if extra['papersize']:
575                 try:
576                         options = ly_paper_to_latexpaper[extra['papersize'][0]]
577                 except KeyError:
578                         warning (_ ("invalid value: %s") % `extra['papersize'][0]`)
579                         pass
580
581         if extra['latexoptions']:
582                 options = options + ',' + extra['latexoptions'][-1]
583
584         s = s + '\\documentclass[%s]{article}\n' % options
585
586         if extra['language']:
587                 s = s + r'\usepackage[%s]{babel}\n' % extra['language'][-1]
588
589
590         s = s + '\\usepackage{%s}\n' \
591                 % string.join (extra['latexpackages'], ',')
592
593         if extra['latexheaders']:
594                 s = s + '\\include{%s}\n' \
595                         % string.join (extra['latexheaders'], '}\n\\include{')
596
597         unit = extra['unit'][-1]
598
599         textheight = ''
600         if extra['textheight']:
601                 textheight = ',textheight=%f%s' % (extra['textheight'][0], unit)
602
603         orientation = 'portrait'
604         if extra['orientation']:
605                 orientation = extra['orientation'][0]
606
607         # set sane geometry width (a4-width) for linewidth = -1.
608         maxlw = max (extra['linewidth'] + [-1])
609         if maxlw < 0:
610                 # who the hell is 597 ?
611                 linewidth = '597pt'
612         else:
613                 linewidth = '%d%s' % (maxlw, unit)
614         s = s + '\geometry{width=%s%s,headheight=2mm,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
615
616         if extra['latexoptions']:
617                 s = s + '\geometry{twosideshift=4mm}\n'
618
619         s = s + r'''
620 \usepackage[latin1]{inputenc}
621 \input{titledefs}
622 '''
623         
624         if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
625                 s = s + '\setcounter{page}{%s}\n' % (extra['pagenumber'][-1])
626                 s = s + '\\pagestyle{plain}\n'
627         else:
628                 s = s + '\\pagestyle{empty}\n'
629
630
631         return s
632
633         
634 def global_latex_definition (tfiles, extra):
635         '''construct preamble from EXTRA, dump Latex stuff for each
636 lily output file in TFILES after that, and return the Latex file constructed.  '''
637
638         
639         s = global_latex_preamble (extra) + '\\begin{document}\n'
640         s = s + '\\thispagestyle{firstpage}\n'
641
642         first = 1
643         for t in tfiles:
644                 s = s + one_latex_definition (t, first)
645                 first = 0
646
647
648         s = s + '\\thispagestyle{lastpage}\n'
649         s = s + '\\end{document}'
650
651         return s
652
653 def run_latex (files, outbase, extra):
654
655         """Construct latex file, for FILES and EXTRA, dump it into
656 OUTBASE.latex. Run LaTeX on it.
657
658 RETURN VALUE
659
660 None
661         """
662         latex_fn = outbase + '.latex'
663         
664         wfs = find_tex_files (files, extra)
665         s = global_latex_definition (wfs, extra)
666
667         f = open (latex_fn, 'w')
668         f.write (s)
669         f.close ()
670
671         cmd = 'latex \\\\nonstopmode \\\\input %s' % latex_fn
672         quiet_system (cmd, 'LaTeX')
673
674         if preview_p:
675                 # make a preview by rendering only the 1st line.
676                 preview_fn = outbase + '.preview.tex'
677                 f = open(preview_fn, 'w')
678                 f.write (r'''
679 %s
680 \input lilyponddefs
681 \pagestyle{empty}
682 \begin{document}
683 \def\interscoreline{\endinput}
684 \input %s
685 \end{document}
686 ''' % (global_latex_preamble (extra), outbase))
687
688                 f.close()
689                 cmd = 'latex \\\\nonstopmode \\\\input %s' % preview_fn
690                 quiet_system (cmd, "LaTeX for preview")
691         
692
693 def run_dvips (outbase, extra):
694
695
696         """Run dvips using the correct options taken from EXTRA,
697 leaving a PS file in OUTBASE.ps
698
699 RETURN VALUE
700
701 None.
702 """
703         opts = ''
704         if extra['papersize']:
705                 opts = opts + ' -t%s' % extra['papersize'][0]
706
707         if extra['orientation'] and extra['orientation'][0] == 'landscape':
708                 opts = opts + ' -tlandscape'
709
710         cmd = 'dvips %s -o%s %s' % (opts, outbase + '.ps', outbase + '.dvi')
711         quiet_system (cmd, 'dvips')
712
713         if preview_p:
714                 cmd = 'dvips -E -o%s %s' % ( outbase + '.preview.ps', outbase + '.preview.dvi')         
715                 quiet_system (cmd, 'dvips for preview')
716
717 def get_bbox (filename):
718         # cut & paste 
719         system ('gs -sDEVICE=bbox -q  -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
720
721         box = open (filename + '.bbox').read()
722         m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
723         gr = []
724         if m:
725                 gr = map (string.atoi, m.groups ())
726         
727         return gr
728
729 #
730 # cut & paste from lilypond-book.
731 #
732 def make_preview (name, extra):
733         bbox = get_bbox (name + '.preview.ps')
734         margin = 0
735         fo = open (name + '.trans.eps' , 'w')
736         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
737         fo.close ()
738         
739         x = (2* margin + bbox[2] - bbox[0]) * preview_resolution / 72.
740         y = (2* margin + bbox[3] - bbox[1]) * preview_resolution / 72.
741
742         cmd = r'''gs -g%dx%d -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s'''
743         
744         cmd = cmd % (x, y, preview_resolution, name + '.trans.eps', name + '.preview.ps',name + '.png')
745         quiet_system (cmd, 'gs')
746
747         try:
748                 status = system (cmd)
749         except:
750                 os.unlink (name + '.png')
751                 error ("Removing output file")
752
753
754
755 def generate_dependency_file (depfile, outname):
756         df = open (depfile, 'w')
757         df.write (outname + ':' )
758         
759         for d in dependency_files:
760                 s = open (d).read ()
761                 s = re.sub ('#[^\n]*\n', '', s)
762                 s = re.sub (r'\\\n', ' ', s)
763                 m = re.search ('.*:(.*)\n', s)
764
765                 # ugh. Different targets?
766                 if m:
767                         df.write ( m.group (1)  + ' ' )
768
769         df.write ('\n')
770         df.close ();
771
772 def find_file_in_path (path, name):
773         for d in string.split (path, os.pathsep):
774                 if name in os.listdir (d):
775                         return os.path.join (d, name)
776
777 # Added as functionality to ly2dvi, because ly2dvi may well need to do this
778 # in future too.
779 PS = '%!PS-Adobe'
780 def find_pfa_fonts (name):
781         s = open (name).read ()
782         if s[:len (PS)] != PS:
783                 # no ps header?
784                 errorport.write (_( "error: ") + _ ("not a PostScript file: `%s\'" % name))
785                 errorport.write ('\n')
786                 sys.exit (1)
787         here = 0
788         m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
789         pfa = []
790         while m:
791                 here = m.end (1)
792                 pfa.append (m.group (1))
793                 m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
794         return pfa
795
796         
797 (sh, long) = getopt_args (option_definitions)
798 try:
799         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
800 except getopt.error, s:
801         errorport.write ('\n')
802         errorport.write (_ ("error: ") + _ ("getopt says: `%s\'" % s))
803         errorport.write ('\n')
804         errorport.write ('\n')
805         help ()
806         sys.exit (2)
807         
808 for opt in options:     
809         o = opt[0]
810         a = opt[1]
811
812         if 0:
813                 pass
814         elif o == '--help' or o == '-h':
815                 help ()
816                 sys.exit (0)
817         elif o == '--find-pfa' or o == '-f':
818                 fonts = map (lambda x: x + '.pfa', find_pfa_fonts (a))
819                 files = map (lambda x:
820                              find_file_in_path (os.environ['GS_FONTPATH'], x),
821                              fonts)
822                 print string.join (files, ' ')
823                 sys.exit (0)
824         elif o == '--include' or o == '-I':
825                 include_path.append (a)
826         elif o == '--postscript' or o == '-P':
827                 targets.append ('PS')
828         elif o == '--keep' or o == '-k':
829                 keep_temp_dir_p = 1
830         elif o == '--no-lily':
831                 lily_p = 0
832         elif o == '--preview':
833                 preview_p = 1
834                 targets.append ('PNG')
835         elif o == '--preview-resolution':
836                 preview_resolution = string.atoi (a)
837         elif o == '--no-paper' or o == '-m':
838                 targets = ['MIDI'] 
839                 paper_p = 0
840         elif o == '--output' or o == '-o':
841                 output_name = a
842         elif o == '--set' or o == '-s':
843                 ss = string.split (a, '=')
844                 set_setting (extra_init, ss[0], ss[1])
845         elif o == '--dependencies' or o == '-d':
846                 track_dependencies_p = 1
847         elif o == '--verbose' or o == '-V':
848                 verbose_p = 1
849         elif o == '--version' or o == '-v':
850                 identify ()
851                 sys.exit (0)
852         elif o == '--warranty' or o == '-w':
853                 status = system ('lilypond -w', ignore_error = 1)
854                 if status:
855                         warranty ()
856
857                 sys.exit (0)
858
859 include_path = map (abspath, include_path)
860
861 original_output = output_name
862
863
864 if files and files[0] != '-':
865         
866         # Ugh, maybe make a setup () function
867         files = map (lambda x: strip_extension (x, '.ly'), files)
868
869         # hmmm. Wish I'd 've written comments when I wrote this.
870         # now it looks complicated.
871         
872         (outdir, outbase) = ('','')
873         if not output_name:
874                 outbase = os.path.basename (files[0])
875                 outdir = abspath('.')
876         elif output_name[-1] == os.sep:
877                 outdir = abspath (output_name)
878                 outbase = os.path.basename (files[0])
879         else:
880                 (outdir, outbase) = os.path.split (abspath (output_name))
881
882         for i in ('.dvi', '.latex', '.ly', '.ps', '.tex'):
883                 output_name = strip_extension (output_name, i)
884                 outbase = strip_extension (outbase, i)
885         files = map (abspath, files)
886
887         for i in files[:] + [output_name]:
888                 if string.find (i, ' ') >= 0:
889                         user_error (_ ("filename should not contain spaces: `%s'") % i)
890                         
891         if os.path.dirname (output_name) != '.':
892                 dep_prefix = os.path.dirname (output_name)
893         else:
894                 dep_prefix = 0
895
896         reldir = os.path.dirname (output_name)
897         if outdir != '.' and (track_dependencies_p or targets):
898                 mkdir_p (outdir, 0777)
899
900         setup_environment ()
901         tmpdir = setup_temp ()
902
903         # to be sure, add tmpdir *in front* of inclusion path.
904         #os.environ['TEXINPUTS'] =  tmpdir + ':' + os.environ['TEXINPUTS']
905         os.chdir (tmpdir)
906         
907         if lily_p:
908                 try:
909                         run_lilypond (files, outbase, dep_prefix)
910                 except:
911                         # TODO: friendly message about LilyPond setup/failing?
912                         #
913                         # TODO: lilypond should fail with different
914                         # error codes for:
915                         #   - guile setup/startup failure
916                         #   - font setup failure
917                         #   - init.ly setup failure
918                         #   - parse error in .ly
919                         #   - unexpected: assert/core dump
920                         targets = []
921                         traceback.print_exc ()
922
923         if 'PNG' in targets and 'PS' not in targets:
924                 targets.append ('PS')
925         if 'PS' in targets and 'DVI' not in targets:
926                 targets.append('DVI')
927
928         if 'DVI' in targets:
929                 try:
930                         run_latex (files, outbase, extra_init)
931                         # unless: add --tex, or --latex?
932                         targets.remove ('TEX')
933                         targets.remove('LATEX')
934                 except:
935                         # TODO: friendly message about TeX/LaTeX setup,
936                         # trying to run tex/latex by hand
937                         if 'DVI' in targets:
938                                 targets.remove ('DVI')
939                         if 'PS' in targets:
940                                 targets.remove ('PS')
941                         traceback.print_exc ()
942
943         if 'PS' in targets:
944                 try:
945                         run_dvips (outbase, extra_init)
946                 except: 
947                         if 'PS' in targets:
948                                 targets.remove ('PS')
949                         traceback.print_exc ()
950
951         if 'PNG' in  targets:
952                 make_preview (outbase, extra_init)
953                 
954         # add DEP to targets?
955         if track_dependencies_p:
956                 depfile = os.path.join (outdir, outbase + '.dep')
957                 generate_dependency_file (depfile, depfile)
958                 if os.path.isfile (depfile):
959                         progress (_ ("dependencies output to `%s'...") % depfile)
960
961         # Hmm, if this were a function, we could call it the except: clauses
962         for i in targets:
963                 ext = string.lower (i)
964                 cp_to_dir ('.*\.%s$' % ext, outdir)
965                 outname = outbase + '.' + string.lower (i)
966                 abs = os.path.join (outdir, outname)
967                 if reldir != '.':
968                         outname = os.path.join (reldir, outname)
969                 if os.path.isfile (abs):
970                         progress (_ ("%s output to `%s'...") % (i, outname))
971                 elif verbose_p:
972                         warning (_ ("can't find file: `%s'") % outname)
973                         
974         os.chdir (original_dir)
975         cleanup_temp ()
976         
977 else:
978         # FIXME: read from stdin when files[0] = '-'
979         help ()
980         user_error (_ ("no files specified on command line."), 2)
981
982
983