]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
patch::: 1.3.150.jcn2
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2 # Run lilypond, latex, dvips.
3 #
4 # This is the third incarnation of ly2dvi.
5 #
6 # Earlier incarnations of ly2dvi were written by
7 # Jeffrey B. Reed<daboys@austin.rr.com> (Python version)
8 # Jan Arne Fagertun <Jan.A.Fagertun@@energy.sintef.no> (Bourne shell script)
9 #
10
11
12 # Note: gettext work best if we use ' for docstrings and "
13 # for gettextable strings
14
15 '''
16 TODO:
17
18   * figure out which set of command line options should make ly2dvi:
19
20       na: create tex only?  
21       na: create latex only? 
22       na: create tex and latex
23       default: create dvi only
24       na: create tex, latex and dvi
25       -P: create dvi and ps
26       na: * create ps only
27
28      etc.
29
30      for foo.ly, rename ly2dvi.dir to out-ly2dvi, foo.ly2dvi, foo.dir ?
31      
32   * move versatile taglines, 
33   
34      \header {
35         beginfooter=\mutopiaPD
36         endfooter=\tagline  -> 'lily was here <version>'
37      }
38
39      lilytagline (->lily was here), usertagline, copyright etc.
40
41   * head/header tagline/endfooter
42
43   * dvi from lilypond .tex output?  This is hairy, because we create dvi
44     from lilypond .tex *and* header output.
45
46   * multiple \score blocks?
47   
48 '''
49
50
51 import os
52 import stat
53 import string
54 import re
55 import getopt
56 import sys
57 import shutil
58 import __main__
59 import operator
60 import tempfile
61
62 datadir = '@datadir@'
63 sys.path.append (datadir + '/python')
64 try:
65         import gettext
66         gettext.bindtextdomain ('lilypond', '@localedir@')
67         gettext.textdomain('lilypond')
68         _ = gettext.gettext
69 except:
70         def _ (s):
71                 return s
72
73
74 layout_fields = ['title', 'subtitle', 'subsubtitle', 'footer', 'head',
75           'composer', 'arranger', 'instrument', 'opus', 'piece', 'metre',
76           'meter', 'poet']
77
78
79 # init to empty; values here take precedence over values in the file 
80 extra_init = {
81         'language' : [],
82         'latexheaders' : [],
83         'latexpackages' :  ['geometry'],
84         'latexoptions' : [],
85         'papersize' : [],
86         'pagenumber' : [1],
87         'textheight' : [], 
88         'linewidth' : [],
89         'orientation' : []
90 }
91
92 extra_fields = extra_init.keys ()
93
94 fields = layout_fields + extra_fields
95 program_name = 'ly2dvi'
96 help_summary = _ ("Generate .dvi with LaTeX for LilyPond")
97
98 include_path = ['.']
99 lily_p = 1
100 paper_p = 1
101
102 output_name = ''
103 targets = {
104         'DVI' : 0,
105         'LATEX' : 0,
106         'MIDI' : 0,
107         'TEX' : 0,
108         }
109
110 track_dependencies_p = 0
111 dependency_files = []
112
113
114 # lily_py.py -- options and stuff
115
116 # source file of the GNU LilyPond music typesetter
117
118 # BEGIN Library for these?
119 # cut-n-paste from ly2dvi
120
121 program_version = '@TOPLEVEL_VERSION@'
122 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
123         program_version = '1.3.148'
124
125
126 original_dir = os.getcwd ()
127 temp_dir = '%s.dir' % program_name
128 keep_temp_dir_p = 0
129 verbose_p = 0
130
131 #
132 # Try to cater for bad installations of LilyPond, that have
133 # broken TeX setup.  Just hope this doesn't hurt good TeX
134 # setups.  Maybe we should check if kpsewhich can find
135 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
136 #
137 environment = {
138         'MFINPUTS' : datadir + '/mf:',
139         'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
140         'TFMFONTS' : datadir + '/tfm:',
141         'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
142         'GS_LIB' : datadir + '/ps',
143 }
144
145 def setup_environment ():
146         for key in environment.keys ():
147                 val = environment[key]
148                 if os.environ.has_key (key):
149                         val = val + os.pathsep + os.environ[key]
150                 os.environ[key] = val
151
152 def identify ():
153         sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
154
155 def warranty ():
156         identify ()
157         sys.stdout.write ('\n')
158         sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001'))
159         sys.stdout.write ('\n')
160         sys.stdout.write ('  Han-Wen Nienhuys')
161         sys.stdout.write ('  Jan Nieuwenhuizen')
162         sys.stdout.write ('\n')
163         sys.stdout.write (_ (r'''
164 Distributed under terms of the GNU General Public License. It comes with
165 NO WARRANTY.'''))
166         sys.stdout.write ('\n')
167
168 if ( os.name == 'posix' ):
169         errorport=sys.stderr
170 else:
171         errorport=sys.stdout
172
173 def progress (s):
174         errorport.write (s + '\n')
175
176 def warning (s):
177         progress (_ ("warning: ") + s)
178                 
179 def error (s):
180
181
182         """Report the error S.  Exit by raising an exception. Please
183         do not abuse by trying to catch this error. If you donn't want
184         a stack trace, write to the output directly.
185
186         RETURN VALUE
187
188         None
189         
190         """
191         
192         progress (_ ("error: ") + s)
193         raise _ ("Exiting ... ")
194
195 def getopt_args (opts):
196         '''Construct arguments (LONG, SHORT) for getopt from  list of options.'''
197         short = ''
198         long = []
199         for o in opts:
200                 if o[1]:
201                         short = short + o[1]
202                         if o[0]:
203                                 short = short + ':'
204                 if o[2]:
205                         l = o[2]
206                         if o[0]:
207                                 l = l + '='
208                         long.append (l)
209         return (short, long)
210
211 def option_help_str (o):
212         '''Transform one option description (4-tuple ) into neatly formatted string'''
213         sh = '  '       
214         if o[1]:
215                 sh = '-%s' % o[1]
216
217         sep = ' '
218         if o[1] and o[2]:
219                 sep = ','
220                 
221         long = ''
222         if o[2]:
223                 long= '--%s' % o[2]
224
225         arg = ''
226         if o[0]:
227                 if o[2]:
228                         arg = '='
229                 arg = arg + o[0]
230         return '  ' + sh + sep + long + arg
231
232
233 def options_help_str (opts):
234         '''Convert a list of options into a neatly formatted string'''
235         w = 0
236         strs =[]
237         helps = []
238
239         for o in opts:
240                 s = option_help_str (o)
241                 strs.append ((s, o[3]))
242                 if len (s) > w:
243                         w = len (s)
244
245         str = ''
246         for s in strs:
247                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
248         return str
249
250 def help ():
251         ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
252                 ('\n\n'),
253                 (help_summary),
254                 ('\n\n'),
255                 (_ ("Options:")),
256                 ('\n'),
257                 (options_help_str (option_definitions)),
258                 ('\n\n'),
259                 (_ ("Report bugs to %s") % 'bug-gnu-music@gnu.org'),
260                 ('\n')]
261         map (sys.stdout.write, ls)
262         
263 def setup_temp ():
264         global temp_dir
265         if not keep_temp_dir_p:
266                 temp_dir = tempfile.mktemp (program_name)
267         try:
268                 os.mkdir (temp_dir, 0777)
269         except OSError:
270                 pass
271         os.chdir (temp_dir)
272
273
274 def system (cmd, ignore_error = 0):
275         """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
276
277         RETURN VALUE
278
279         Exit status of CMD
280         """
281         
282         if ( os.name != 'posix' ):
283                 cmd = re.sub (r'''\\''', r'''\\\\\\''', cmd)
284                 cmd = "sh -c \'%s\'" % cmd
285         if verbose_p:
286                 progress (_ ("Invoking `%s\'") % cmd)
287         st = os.system (cmd) >> 8
288         if st:
289                 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
290                 msg = name + ': ' + _ ("command exited with value %d") % st
291                 if ignore_error:
292                         warning (msg + ' ' + _ ("(ignored)") + ' ')
293                 else:
294                         error (msg)
295
296         return st
297
298
299 def cleanup_temp ():
300         if not keep_temp_dir_p:
301                 if verbose_p:
302                         progress (_ ("Cleaning %s...") % temp_dir)
303                 shutil.rmtree (temp_dir)
304
305
306 #what a name.
307 def set_setting (dict, key, val):
308         try:
309                 val = string.atof (val)
310         except ValueError:
311                 #warning (_ ("invalid value: %s") % `val`)
312                 pass
313
314         try:
315                 dict[key].append (val)
316         except KeyError:
317                 warning (_ ("no such setting: %s") % `key`)
318                 dict[key] = [val]
319
320
321 def strip_extension (f, ext):
322         (p, e) = os.path.splitext (f)
323         if e == ext:
324                 e = ''
325         return p + e
326
327 # END Library
328
329 option_definitions = [
330         ('', 'd', 'dependencies', _ ("write Makefile dependencies for every input file")),
331         ('', 'h', 'help', _ ("this help")),
332         (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
333         ('', 'k', 'keep', _ ("keep all output, and name the directory %s.dir") % program_name),
334         ('', '', 'no-lily', _ ("don't run LilyPond")),
335         ('', 'm', 'no-paper', _ ("produce MIDI output only")),
336         (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
337         # why capital P?
338         ('', 'P', 'postscript', _ ("generate PostScript output")),
339         (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
340         ('', 'V', 'verbose', _ ("verbose")),
341         ('', 'v', 'version', _ ("print version number")),
342         ('', 'w', 'warranty', _ ("show warranty and copyright")),
343         ]
344
345 def run_lilypond (files, outbase, dep_prefix):
346         opts = ''
347 #       opts = opts + '--output=%s.tex' % outbase
348         opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
349                                               include_path))
350         if paper_p:
351                 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
352                                                       fields))
353         else:
354                 opts = opts + ' --no-paper'
355                 
356         if track_dependencies_p:
357                 opts = opts + " --dependencies"
358                 if dep_prefix:
359                         opts = opts + ' --dep-prefix=%s' % dep_prefix
360
361         fs = string.join (files)
362
363         if not verbose_p:
364                 progress ( _("Running %s...") % 'LilyPond')
365                 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
366         else:
367                 opts = opts + ' --verbose'
368         
369         system ('lilypond %s %s ' % (opts, fs))
370
371 def analyse_lilypond_output (filename, extra):
372         
373         # urg
374         '''Grep FILENAME for interesting stuff, and
375         put relevant info into EXTRA.'''
376         filename = filename+'.tex'
377         progress (_ ("Analyzing %s...") % filename)
378         s = open (filename).read ()
379
380         # search only the first 10k
381         s = s[:10240]
382         for x in ('textheight', 'linewidth', 'papersize', 'orientation'):
383                 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
384                 if m:
385                         set_setting (extra, x, m.group (1))
386
387 def find_tex_files_for_base (base, extra):
388
389         """
390         Find the \header fields dumped from BASE.
391         """
392         
393         headerfiles = {}
394         for f in layout_fields:
395                 if os.path.exists (base + '.' + f):
396                         headerfiles[f] = base+'.'+f
397
398         if os.path.exists (base  +'.dep'):
399                 dependency_files.append (base + '.dep')
400
401         for f in extra_fields:
402                 if os.path.exists (base + '.' + f):
403                         extra[f].append (open (base + '.' + f).read ())
404         
405         return (base  +'.tex',headerfiles)
406          
407
408 def find_tex_files (files, extra):
409         """
410         Find all .tex files whose prefixes start with some name in FILES. 
411
412         """
413         
414         tfiles = []
415         
416         for f in files:
417                 x = 0
418                 while 1:
419                         fname = os.path.basename (f)
420                         fname = strip_extension (fname, '.ly')
421                         if x:
422                                 fname = fname + '-%d' % x
423
424                         if os.path.exists (fname + '.tex'):
425                                 tfiles.append (find_tex_files_for_base (fname, extra))
426                                 analyse_lilypond_output (fname, extra)
427                         else:
428                                 break
429
430                         x = x + 1
431         if not x:
432                 fstr = string.join (files, ', ')
433                 warning (_ ("no lilypond output found for %s") % fstr)
434         return tfiles
435
436 def one_latex_definition (defn, first):
437         s = '\n'
438         for (k,v) in defn[1].items ():
439                 val = open (v).read ()
440                 if (string.strip (val)):
441                         s = s + r'''\def\lilypond%s{%s}''' % (k, val)
442                 else:
443                         s = s + r'''\let\lilypond%s\relax''' % k
444                 s = s + '\n'
445
446         if first:
447                 s = s + '\\def\\mustmakelilypondtitle{}\n'
448         else:
449                 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
450                 
451         s = s + '\\input %s' % defn[0]
452         return s
453
454
455 ly_paper_to_latexpaper =  {
456         'a4' : 'a4paper',
457         'letter' : 'letterpaper', 
458 }
459
460 def global_latex_definition (tfiles, extra):
461
462         '''construct preamble from EXTRA, dump Latex stuff for each
463 lily output file in TFILES after that, and return the Latex file constructed.  '''
464
465
466         s = ""
467         s = s + '% generation tag\n'
468
469         options = ''
470
471         if extra['papersize']:
472                 try:
473                         options = '%s' % ly_paper_to_latexpaper[extra['papersize'][0]]
474                 except KeyError:
475                         warning (_ ("invalid value: %s") % `extra['papersize'][0]`)
476                         pass
477
478         if extra['latexoptions']:
479                 options = options + ',' + extra['latexoptions'][-1]
480
481         s = s + '\\documentclass[%s]{article}\n' % options
482
483         if extra['language']:
484                 s = s + r'\usepackage[%s]{babel}\n' % extra['language'][-1]
485
486
487         s = s + '\\usepackage{%s}\n' \
488                 % string.join (extra['latexpackages'], ',')
489
490         if extra['latexheaders']:
491                 s = s + '\\include{%s}\n' \
492                         % string.join (extra['latexheaders'], '}\n\\include{')
493
494         textheight = ''
495         if extra['textheight']:
496                 textheight = ',textheight=%fpt' % extra['textheight'][0]
497
498         orientation = 'portrait'
499         if extra['orientation']:
500                 orientation = extra['orientation'][0]
501
502         # set sane geometry width (a4-width) for linewidth = -1.
503         if not extra['linewidth'] or extra['linewidth'][0] < 0:
504                 linewidth = 597
505         else:
506                 linewidth = extra['linewidth'][0]
507         s = s + '\geometry{width=%spt%s,headheight=2mm,headsep=12pt,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
508
509         if extra['latexoptions']:
510                 s = s + '\geometry{twosideshift=4mm}\n'
511
512         s = s + r'''
513 \usepackage[latin1]{inputenc}
514 \input{titledefs}
515 \makeatletter
516 \renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\thefooter}}%
517 \renewcommand{\@evenfoot}{\parbox{\textwidth}{\mbox{}\thefooter}}%
518 '''
519         
520         if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
521                 s = s + r'''
522 \renewcommand{\@evenhead}{\hbox to\textwidth{\textbf{\thepage}\hfill{\small\theheader}}}
523 \renewcommand{\@oddhead}{\hbox to \textwidth{{\small\theheader}\hfill\textbf{\thepage}}}
524 '''
525         else:
526                 s = s + '\\pagestyle{empty}\n'
527
528         s = s + '\\makeatother\n'
529         s = s + '\\begin{document}\n'
530
531
532         first = 1
533         for t in tfiles:
534                 s = s + one_latex_definition (t, first)
535                 first = 0
536
537         s = s + r'''
538 % I do not see why we want to clobber the footer here
539 \vfill\hfill\parbox{\textwidth}{\mbox{}\makelilypondtagline}
540 %\makeatletter
541 %\renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\makelilypondtagline}}%
542 %\makeatother
543 '''
544         s = s + '\\end{document}'
545
546         return s
547
548 def run_latex (files, outbase, extra):
549
550         """Construct latex file, for FILES and EXTRA, dump it into
551 OUTBASE.latex. Run LaTeX on it.
552
553 RETURN VALUE
554
555 None
556         """
557         latex_fn = outbase + '.latex'
558         
559         wfs = find_tex_files (files, extra)
560         s = global_latex_definition (wfs, extra)
561
562         f = open (latex_fn, 'w')
563         f.write (s)
564         f.close ()
565
566         cmd = 'latex \\\\nonstopmode \\\\input %s' % latex_fn
567
568         if not verbose_p and os.name == 'posix':
569                 progress ( _("Running %s...") % 'LaTeX')
570                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
571
572         system (cmd)
573
574 def run_dvips (outbase, extra):
575
576
577         """Run dvips using the correct options taken from EXTRA,
578 leaving a PS file in OUTBASE.ps
579
580 RETURN VALUE
581
582 None.
583 """
584         opts = ''
585         if extra['papersize']:
586                 opts = opts + ' -t%s' % extra['papersize'][0]
587
588         if extra['orientation'] and extra['orientation'][0] == 'landscape':
589                 opts = opts + ' -tlandscape'
590
591         cmd = 'dvips %s -o%s %s' % (opts, outbase + '.ps', outbase + '.dvi')
592         
593         if not verbose_p and os.name == 'posix':
594                 progress ( _("Running %s...") % 'dvips')
595                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
596                 
597         system (cmd)
598
599 def generate_dependency_file (depfile, outname):
600         df = open (depfile, 'w')
601         df.write (outname + ':' )
602         
603         for d in dependency_files:
604                 s = open (d).read ()
605                 s = re.sub ('#[^\n]*\n', '', s)
606                 s = re.sub (r'\\\n', ' ', s)
607                 m = re.search ('.*:(.*)\n', s)
608
609                 # ugh. Different targets?
610                 if m:
611                         df.write ( m.group (1)  + ' ' )
612
613         df.write ('\n')
614         df.close ();
615
616 (sh, long) = getopt_args (__main__.option_definitions)
617 try:
618         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
619 except getopt.error, s: 
620         errorport.write ("\nerror: getopt says `%s\'\n\n" % s)
621         help ()
622         sys.exit (2)
623         
624 for opt in options:     
625         o = opt[0]
626         a = opt[1]
627
628         if 0:
629                 pass
630         elif o == '--help' or o == '-h':
631                 help ()
632                 sys.exit (0)
633         elif o == '--include' or o == '-I':
634                 include_path.append (a)
635         elif o == '--postscript' or o == '-P':
636                 targets['PS'] = 0
637         elif o == '--keep' or o == '-k':
638                 keep_temp_dir_p = 1
639         elif o == '--no-lily':
640                 lily_p = 0
641         elif o == '--no-paper' or o == '-m':
642                 targets = {}
643                 targets['MIDI'] = 0
644                 paper_p = 0
645         elif o == '--output' or o == '-o':
646                 output_name = a
647         elif o == '--set' or o == '-s':
648                 ss = string.split (a, '=')
649                 set_setting (extra_init, ss[0], ss[1])
650         elif o == '--dependencies' or o == '-d':
651                 track_dependencies_p = 1
652         elif o == '--verbose' or o == '-V':
653                 verbose_p = 1
654         elif o == '--version' or o == '-v':
655                 identify ()
656                 sys.exit (0)
657         elif o == '--warranty' or o == '-w':
658                 status = system ('lilypond -w', ignore_error = 1)
659                 if status:
660                         warranty ()
661
662                 sys.exit (0)
663
664
665 def cp_to_dir (pattern, dir):
666         "Copy files matching re PATTERN from cwd to DIR"
667         # Duh.  Python style portable: cp *.EXT OUTDIR
668         # system ('cp *.%s %s' % (ext, outdir), 1)
669         files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
670         map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
671
672 # Python < 1.5.2 compatibility
673 #
674 # On most platforms, this is equivalent to
675 #`normpath(join(os.getcwd()), PATH)'.  *Added in Python version 1.5.2*
676 if os.path.__dict__.has_key ('abspath'):
677         abspath = os.path.abspath
678 else:
679         def abspath (path):
680                 return os.path.normpath (os.path.join (os.getcwd (), path))
681
682 if os.__dict__.has_key ('makedirs'):
683         makedirs = os.makedirs
684 else:
685         def makedirs (dir, mode=0777):
686                 system ('mkdir -p %s' % dir)
687
688 def mkdir_p (dir, mode=0777):
689         if not os.path.isdir (dir):
690                 makedirs (dir, mode)
691
692 include_path = map (abspath, include_path)
693
694 original_output = output_name
695
696 if files and files[0] != '-':
697
698         files = map (lambda x: strip_extension (x, '.ly'), files)
699
700         if not output_name:
701                 output_name = os.path.basename (files[0])
702
703         for i in ('.dvi', '.latex', '.ly', '.ps', '.tex'):
704                 output_name = strip_extension (output_name, i)
705
706         files = map (abspath, files) 
707
708         if os.path.dirname (output_name) != '.':
709                 dep_prefix = os.path.dirname (output_name)
710         else:
711                 dep_prefix = 0
712
713         reldir = os.path.dirname (output_name)
714         (outdir, outbase) = os.path.split (abspath (output_name))
715         
716         setup_environment ()
717         setup_temp ()
718         
719         extra = extra_init
720         
721         if lily_p:
722 ##              try:
723                         run_lilypond (files, outbase, dep_prefix)
724 ## #            except:
725 ##                      # TODO: friendly message about LilyPond setup/failing?
726 ##                      #
727 ##                      # TODO: lilypond should fail with different
728 ##                      # error codes for:
729 ##                      #   - guile setup/startup failure
730 ##                      #   - font setup failure
731 ##                      #   - init.ly setup failure
732 ##                      #   - parse error in .ly
733 ##                      #   - unexpected: assert/core dump
734 ## #                    targets = {}
735
736         if targets.has_key ('DVI') or targets.has_key ('PS'):
737 #               try:
738                         run_latex (files, outbase, extra)
739                         # unless: add --tex, or --latex?
740                         del targets['TEX']
741                         del targets['LATEX']
742 #               except Foobar:
743 #                       # TODO: friendly message about TeX/LaTeX setup,
744 #                       # trying to run tex/latex by hand
745 #                       if targets.has_key ('DVI'):
746 #                               del targets['DVI']
747 #                       if targets.has_key ('PS'):
748 #                               del targets['PS']
749
750         # TODO: does dvips ever fail?
751         if targets.has_key ('PS'):
752                 run_dvips (outbase, extra)
753
754         if outdir != '.' and (track_dependencies_p or targets.keys ()):
755                 mkdir_p (outdir, 0777)
756
757         # add DEP to targets?
758         if track_dependencies_p:
759                 depfile = os.path.join (outdir, outbase + '.dep')
760                 generate_dependency_file (depfile, depfile)
761                 if os.path.isfile (depfile):
762                         progress (_ ("dependencies output to %s...") % depfile)
763
764         for i in targets.keys ():
765                 ext = string.lower (i)
766                 cp_to_dir ('.*\.%s$' % ext, outdir)
767                 outname = outbase + '.' + string.lower (i)
768                 abs = os.path.join (outdir, outname)
769                 if reldir != '.':
770                         outname = os.path.join (reldir, outname)
771                 if os.path.isfile (abs):
772                         progress (_ ("%s output to %s...") % (i, outname))
773                 elif verbose_p:
774                         warning (_ ("can't find file: `%s'") % outname)
775
776         os.chdir (original_dir)
777         cleanup_temp ()
778         
779 else:
780         # FIXME
781         help ()
782         errorport.write ("ly2dvi: error: " + _ ("no files specified on command line.\n"))
783         sys.exit (2)
784
785
786