]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
26f7bd263545c309d9380a982347985c9f8ac442
[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 verbose_p:
283                 progress (_ ("Invoking `%s\'") % cmd)
284         st = os.system (cmd) >> 8
285         if st:
286                 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
287                 msg = name + ': ' + _ ("command exited with value %d") % st
288                 if ignore_error:
289                         warning (msg + ' ' + _ ("(ignored)") + ' ')
290                 else:
291                         error (msg)
292
293         return st
294
295
296 def cleanup_temp ():
297         if not keep_temp_dir_p:
298                 if verbose_p:
299                         progress (_ ("Cleaning %s...") % temp_dir)
300                 shutil.rmtree (temp_dir)
301
302
303 #what a name.
304 def set_setting (dict, key, val):
305         try:
306                 val = string.atof (val)
307         except ValueError:
308                 #warning (_ ("invalid value: %s") % `val`)
309                 pass
310
311         try:
312                 dict[key].append (val)
313         except KeyError:
314                 warning (_ ("no such setting: %s") % `key`)
315                 dict[key] = [val]
316
317
318 def strip_extension (f, ext):
319         (p, e) = os.path.splitext (f)
320         if e == ext:
321                 e = ''
322         return p + e
323
324 # END Library
325
326 option_definitions = [
327         ('', 'd', 'dependencies', _ ("write Makefile dependencies for every input file")),
328         ('', 'h', 'help', _ ("this help")),
329         (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
330         ('', 'k', 'keep', _ ("keep all output, and name the directory %s.dir") % program_name),
331         ('', '', 'no-lily', _ ("don't run LilyPond")),
332         ('', 'm', 'no-paper', _ ("produce MIDI output only")),
333         (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
334         # why capital P?
335         ('', 'P', 'postscript', _ ("generate PostScript output")),
336         (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
337         ('', 'V', 'verbose', _ ("verbose")),
338         ('', 'v', 'version', _ ("print version number")),
339         ('', 'w', 'warranty', _ ("show warranty and copyright")),
340         ]
341
342 def run_lilypond (files, outbase, dep_prefix):
343         opts = ''
344 #       opts = opts + '--output=%s.tex' % outbase
345         opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
346                                               include_path))
347         if paper_p:
348                 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
349                                                       fields))
350         else:
351                 opts = opts + ' --no-paper'
352                 
353         if track_dependencies_p:
354                 opts = opts + " --dependencies"
355                 if dep_prefix:
356                         opts = opts + ' --dep-prefix=%s' % dep_prefix
357
358         fs = string.join (files)
359
360         if not verbose_p:
361                 progress ( _("Running %s...") % 'LilyPond')
362                 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
363         else:
364                 opts = opts + ' --verbose'
365         
366         system ('lilypond %s %s ' % (opts, fs))
367
368 def analyse_lilypond_output (filename, extra):
369         
370         # urg
371         '''Grep FILENAME for interesting stuff, and
372         put relevant info into EXTRA.'''
373         filename = filename+'.tex'
374         progress (_ ("Analyzing %s...") % filename)
375         s = open (filename).read ()
376
377         # search only the first 10k
378         s = s[:10240]
379         for x in ('textheight', 'linewidth', 'papersize', 'orientation'):
380                 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
381                 if m:
382                         set_setting (extra, x, m.group (1))
383
384 def find_tex_files_for_base (base, extra):
385
386         """
387         Find the \header fields dumped from BASE.
388         """
389         
390         headerfiles = {}
391         for f in layout_fields:
392                 if os.path.exists (base + '.' + f):
393                         headerfiles[f] = base+'.'+f
394
395         if os.path.exists (base  +'.dep'):
396                 dependency_files.append (base + '.dep')
397
398         for f in extra_fields:
399                 if os.path.exists (base + '.' + f):
400                         extra[f].append (open (base + '.' + f).read ())
401         
402         return (base  +'.tex',headerfiles)
403          
404
405 def find_tex_files (files, extra):
406         """
407         Find all .tex files whose prefixes start with some name in FILES. 
408
409         """
410         
411         tfiles = []
412         
413         for f in files:
414                 x = 0
415                 while 1:
416                         fname = os.path.basename (f)
417                         fname = strip_extension (fname, '.ly')
418                         if x:
419                                 fname = fname + '-%d' % x
420
421                         if os.path.exists (fname + '.tex'):
422                                 tfiles.append (find_tex_files_for_base (fname, extra))
423                                 analyse_lilypond_output (fname, extra)
424                         else:
425                                 break
426
427                         x = x + 1
428         if not x:
429                 fstr = string.join (files, ', ')
430                 warning (_ ("no lilypond output found for %s") % fstr)
431         return tfiles
432
433 def one_latex_definition (defn, first):
434         s = '\n'
435         for (k,v) in defn[1].items ():
436                 val = open (v).read ()
437                 if (string.strip (val)):
438                         s = s + r'''\def\lilypond%s{%s}''' % (k, val)
439                 else:
440                         s = s + r'''\let\lilypond%s\relax''' % k
441                 s = s + '\n'
442
443         if first:
444                 s = s + '\\def\\mustmakelilypondtitle{}\n'
445         else:
446                 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
447                 
448         s = s + '\\input %s' % defn[0]
449         return s
450
451
452 ly_paper_to_latexpaper =  {
453         'a4' : 'a4paper',
454         'letter' : 'letterpaper', 
455 }
456
457 def global_latex_definition (tfiles, extra):
458
459         '''construct preamble from EXTRA, dump Latex stuff for each
460 lily output file in TFILES after that, and return the Latex file constructed.  '''
461
462
463         s = ""
464         s = s + '% generation tag\n'
465
466         options = ''
467
468         if extra['papersize']:
469                 try:
470                         options = '%s' % ly_paper_to_latexpaper[extra['papersize'][0]]
471                 except KeyError:
472                         warning (_ ("invalid value: %s") % `extra['papersize'][0]`)
473                         pass
474
475         if extra['latexoptions']:
476                 options = options + ',' + extra['latexoptions'][-1]
477
478         s = s + '\\documentclass[%s]{article}\n' % options
479
480         if extra['language']:
481                 s = s + r'\usepackage[%s]{babel}\n' % extra['language'][-1]
482
483
484         s = s + '\\usepackage{%s}\n' \
485                 % string.join (extra['latexpackages'], ',')
486
487         if extra['latexheaders']:
488                 s = s + '\\include{%s}\n' \
489                         % string.join (extra['latexheaders'], '}\n\\include{')
490
491         textheight = ''
492         if extra['textheight']:
493                 textheight = ',textheight=%fpt' % extra['textheight'][0]
494
495         orientation = 'portrait'
496         if extra['orientation']:
497                 orientation = extra['orientation'][0]
498
499         # set sane geometry width (a4-width) for linewidth = -1.
500         if not extra['linewidth'] or extra['linewidth'][0] < 0:
501                 linewidth = 597
502         else:
503                 linewidth = extra['linewidth'][0]
504         s = s + '\geometry{width=%spt%s,headheight=2mm,headsep=12pt,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
505
506         if extra['latexoptions']:
507                 s = s + '\geometry{twosideshift=4mm}\n'
508
509         s = s + r'''
510 \usepackage[latin1]{inputenc}
511 \input{titledefs}
512 \makeatletter
513 \renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\thefooter}}%
514 \renewcommand{\@evenfoot}{\parbox{\textwidth}{\mbox{}\thefooter}}%
515 '''
516         
517         if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
518                 s = s + r'''
519 \renewcommand{\@evenhead}{\hbox to\textwidth{\textbf{\thepage}\hfill{\small\theheader}}}
520 \renewcommand{\@oddhead}{\hbox to \textwidth{{\small\theheader}\hfill\textbf{\thepage}}}
521 '''
522         else:
523                 s = s + '\\pagestyle{empty}\n'
524
525         s = s + '\\makeatother\n'
526         s = s + '\\begin{document}\n'
527
528
529         first = 1
530         for t in tfiles:
531                 s = s + one_latex_definition (t, first)
532                 first = 0
533
534         s = s + r'''
535 % I do not see why we want to clobber the footer here
536 \vfill\hfill\parbox{\textwidth}{\mbox{}\makelilypondtagline}
537 %\makeatletter
538 %\renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\makelilypondtagline}}%
539 %\makeatother
540 '''
541         s = s + '\\end{document}'
542
543         return s
544
545 def run_latex (files, outbase, extra):
546
547         """Construct latex file, for FILES and EXTRA, dump it into
548 OUTBASE.latex. Run LaTeX on it.
549
550 RETURN VALUE
551
552 None
553         """
554         latex_fn = outbase + '.latex'
555         
556         wfs = find_tex_files (files, extra)
557         s = global_latex_definition (wfs, extra)
558
559         f = open (latex_fn, 'w')
560         f.write (s)
561         f.close ()
562
563         if ( os.name == 'posix' ):
564                 cmd = 'latex \\\\nonstopmode \\\\input %s' % latex_fn
565         else:
566                 cmd = 'latex \\nonstopmode \\input %s' % latex_fn
567
568         if not verbose_p:
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:
594                 progress ( _("Running %s...") % 'dvips')
595                 if os.name == 'posix':
596                         cmd = cmd + ' 1> /dev/null 2> /dev/null'
597                 
598         system (cmd)
599
600 def generate_dependency_file (depfile, outname):
601         df = open (depfile, 'w')
602         df.write (outname + ':' )
603         
604         for d in dependency_files:
605                 s = open (d).read ()
606                 s = re.sub ('#[^\n]*\n', '', s)
607                 s = re.sub (r'\\\n', ' ', s)
608                 m = re.search ('.*:(.*)\n', s)
609
610                 # ugh. Different targets?
611                 if m:
612                         df.write ( m.group (1)  + ' ' )
613
614         df.write ('\n')
615         df.close ();
616
617 (sh, long) = getopt_args (__main__.option_definitions)
618 try:
619         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
620 except getopt.error, s: 
621         errorport.write ("\nerror: getopt says `%s\'\n\n" % s)
622         help ()
623         sys.exit (2)
624         
625 for opt in options:     
626         o = opt[0]
627         a = opt[1]
628
629         if 0:
630                 pass
631         elif o == '--help' or o == '-h':
632                 help ()
633                 sys.exit (0)
634         elif o == '--include' or o == '-I':
635                 include_path.append (a)
636         elif o == '--postscript' or o == '-P':
637                 targets['PS'] = 0
638         elif o == '--keep' or o == '-k':
639                 keep_temp_dir_p = 1
640         elif o == '--no-lily':
641                 lily_p = 0
642         elif o == '--no-paper' or o == '-m':
643                 targets = {}
644                 targets['MIDI'] = 0
645                 paper_p = 0
646         elif o == '--output' or o == '-o':
647                 output_name = a
648         elif o == '--set' or o == '-s':
649                 ss = string.split (a, '=')
650                 set_setting (extra_init, ss[0], ss[1])
651         elif o == '--dependencies' or o == '-d':
652                 track_dependencies_p = 1
653         elif o == '--verbose' or o == '-V':
654                 verbose_p = 1
655         elif o == '--version' or o == '-v':
656                 identify ()
657                 sys.exit (0)
658         elif o == '--warranty' or o == '-w':
659                 status = system ('lilypond -w', ignore_error = 1)
660                 if status:
661                         warranty ()
662
663                 sys.exit (0)
664
665
666 def cp_to_dir (pattern, dir):
667         "Copy files matching re PATTERN from cwd to DIR"
668         # Duh.  Python style portable: cp *.EXT OUTDIR
669         # system ('cp *.%s %s' % (ext, outdir), 1)
670         files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
671         map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
672
673 # Python < 1.5.2 compatibility
674 #
675 # On most platforms, this is equivalent to
676 #`normpath(join(os.getcwd()), PATH)'.  *Added in Python version 1.5.2*
677 if os.path.__dict__.has_key ('abspath'):
678         abspath = os.path.abspath
679 else:
680         def abspath (path):
681                 return os.path.normpath (os.path.join (os.getcwd (), path))
682
683 if os.__dict__.has_key ('makedirs'):
684         makedirs = os.makedirs
685 else:
686         def makedirs (dir, mode=0777):
687                 system ('mkdir -p %s' % dir)
688
689 def mkdir_p (dir, mode=0777):
690         if not os.path.isdir (dir):
691                 makedirs (dir, mode)
692
693 include_path = map (abspath, include_path)
694
695 original_output = output_name
696
697 if files and files[0] != '-':
698
699         files = map (lambda x: strip_extension (x, '.ly'), files)
700
701         if not output_name:
702                 output_name = os.path.basename (files[0])
703
704         for i in ('.dvi', '.latex', '.ly', '.ps', '.tex'):
705                 output_name = strip_extension (output_name, i)
706
707         files = map (abspath, files) 
708
709         if os.path.dirname (output_name) != '.':
710                 dep_prefix = os.path.dirname (output_name)
711         else:
712                 dep_prefix = 0
713
714         reldir = os.path.dirname (output_name)
715         (outdir, outbase) = os.path.split (abspath (output_name))
716         
717         setup_environment ()
718         setup_temp ()
719         
720         extra = extra_init
721         
722         if lily_p:
723 ##              try:
724                         run_lilypond (files, outbase, dep_prefix)
725 ## #            except:
726 ##                      # TODO: friendly message about LilyPond setup/failing?
727 ##                      #
728 ##                      # TODO: lilypond should fail with different
729 ##                      # error codes for:
730 ##                      #   - guile setup/startup failure
731 ##                      #   - font setup failure
732 ##                      #   - init.ly setup failure
733 ##                      #   - parse error in .ly
734 ##                      #   - unexpected: assert/core dump
735 ## #                    targets = {}
736
737         if targets.has_key ('DVI') or targets.has_key ('PS'):
738 #               try:
739                         run_latex (files, outbase, extra)
740                         # unless: add --tex, or --latex?
741                         del targets['TEX']
742                         del targets['LATEX']
743 #               except Foobar:
744 #                       # TODO: friendly message about TeX/LaTeX setup,
745 #                       # trying to run tex/latex by hand
746 #                       if targets.has_key ('DVI'):
747 #                               del targets['DVI']
748 #                       if targets.has_key ('PS'):
749 #                               del targets['PS']
750
751         # TODO: does dvips ever fail?
752         if targets.has_key ('PS'):
753                 run_dvips (outbase, extra)
754
755         if outdir != '.' and (track_dependencies_p or targets.keys ()):
756                 mkdir_p (outdir, 0777)
757
758         # add DEP to targets?
759         if track_dependencies_p:
760                 depfile = os.path.join (outdir, outbase + '.dep')
761                 generate_dependency_file (depfile, depfile)
762                 if os.path.isfile (depfile):
763                         progress (_ ("dependencies output to %s...") % depfile)
764
765         for i in targets.keys ():
766                 ext = string.lower (i)
767                 cp_to_dir ('.*\.%s$' % ext, outdir)
768                 outname = outbase + '.' + string.lower (i)
769                 abs = os.path.join (outdir, outname)
770                 if reldir != '.':
771                         outname = os.path.join (reldir, outname)
772                 if os.path.isfile (abs):
773                         progress (_ ("%s output to %s...") % (i, outname))
774                 elif verbose_p:
775                         warning (_ ("can't find file: `%s'") % outname)
776
777         os.chdir (original_dir)
778         cleanup_temp ()
779         
780 else:
781         # FIXME
782         help ()
783         errorport.write ("ly2dvi: error: " + _ ("no files specified on command line.\n"))
784         sys.exit (2)
785
786
787