]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
(mark_smob): bugfix.
[lilypond.git] / scripts / lilypond-book.py
1 #!@PYTHON@
2 # vim: set noexpandtab:
3 # TODO:
4 # * junk --outdir for--output
5 # * Figure out clean set of options.
6 # *
7 # * texinfo: add support for @pagesize
8
9 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
10 #       because the values are taken directly from texinfo.tex,
11 #       geometry.sty and article.cls. Give me a hint, and I'll
12 #       fix it.)
13
14 #
15 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
16 #
17
18 # This is was the idea for handling of comments:
19 #       Multiline comments, @ignore .. @end ignore is scanned for
20 #       in read_doc_file, and the chunks are marked as 'ignore', so
21 #       lilypond-book will not touch them any more. The content of the
22 #       chunks are written to the output file. Also 'include' and 'input'
23 #       regex has to check if they are commented out.
24 #
25 #       Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
26 #       These three regex's has to check if they are on a commented line,
27 #       % for latex, @c for texinfo.
28 #
29 #       Then lines that are commented out with % (latex) and @c (Texinfo)
30 #       are put into chunks marked 'ignore'. This cannot be done before
31 #       searching for the lilypond-blocks because % is also the comment character
32 #       for lilypond.
33 #
34 #       The the rest of the rexeces are searched for. They don't have to test
35 #       if they are on a commented out line.
36
37
38 import stat
39 import string
40
41
42 ################################################################
43 # Users of python modules should include this snippet
44 # and customize variables below.
45
46 # We'll suffer this path init stuff as long as we don't install our
47 # python packages in <prefix>/lib/pythonx.y (and don't kludge around
48 # it as we do with teTeX on Red Hat Linux: set some environment var
49 # (PYTHONPATH) in profile)
50
51 # If set, LILYPONDPREFIX must take prevalence
52 # if datadir is not set, we're doing a build and LILYPONDPREFIX
53 import getopt, os, sys
54 datadir = '@local_lilypond_datadir@'
55 if not os.path.isdir (datadir):
56         datadir = '@lilypond_datadir@'
57 if os.environ.has_key ('LILYPONDPREFIX') :
58         datadir = os.environ['LILYPONDPREFIX']
59         while datadir[-1] == os.sep:
60                 datadir= datadir[:-1]
61
62 sys.path.insert (0, os.path.join (datadir, 'python'))
63
64 # Customize these
65 #if __name__ == '__main__':
66
67 import lilylib as ly
68 global _;_=ly._
69 global re;re = ly.re
70
71 # lilylib globals
72 program_name = 'lilypond-book'
73 verbose_p = 0
74 pseudo_filter_p = 0
75 original_dir = os.getcwd ()
76 #temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
77 #urg
78 temp_dir = '/tmp'
79 keep_temp_dir_p = 0
80 preview_resolution = 90
81
82 ## FIXME
83 ## ly2dvi: silly name?
84 ## do -P or -p by default?
85 ##help_summary = _ ("Run LilyPond using LaTeX for titling")
86 help_summary = _ ("Process LilyPond snippets in hybrid html, LaTeX or texinfo document")
87 copyright = ('Tom Cato Amundsen <tca@gnu.org>',
88              'Han-Wen Nienhuys <hanwen@cs.uu.nl>')
89
90 option_definitions = [
91         (_ ("EXT"), 'f', 'format', _ ("use output format EXT (texi [default], texi-html, latex, html)")),
92         (_ ("DIM"),  '', 'default-music-fontsize', _ ("default fontsize for music.  DIM is assumed to be in points")),
93         (_ ("DIM"),  '', 'default-lilypond-fontsize', _ ("deprecated, use --default-music-fontsize")),
94         (_ ("OPT"), '', 'extra-options', _ ("pass OPT quoted to the lilypond command line")),
95         (_ ("DIM"), '', 'force-music-fontsize', _ ("force fontsize for all inline lilypond. DIM is assumed be to in points")),
96         (_ ("DIM"), '', 'force-lilypond-fontsize', _ ("deprecated, use --force-music-fontsize")),
97         ('', 'h', 'help', _ ("this help")),
98         (_ ("DIR"), 'I', 'include', _ ("include path")),
99         ('', 'M', 'dependencies', _ ("write dependencies")),
100         (_ ("PREF"), '',  'dep-prefix', _ ("prepend PREF before each -M dependency")),
101         ('', 'n', 'no-lily', _ ("don't run lilypond")),
102         ('', '', 'no-pictures', _ ("don't generate pictures")),
103         ('', '', 'no-music', _ ("strip all lilypond blocks from output")),
104         ('', '', 'read-lys', _ ("don't write ly files.")),
105         (_ ("FILE"), 'o', 'outname', _ ("filename main output file")),
106         (_ ("FILE"), '', 'outdir', _ ("where to place generated files")),
107         (_ ('RES'), '', 'preview-resolution',
108          _ ("set the resolution of the preview to RES")),
109         ('', 'V', 'verbose', _ ("verbose")),
110         ('', 'v', 'version', _ ("print version information")),
111         ('', 'w', 'warranty', _ ("show warranty and copyright")),
112         ]
113
114 # format specific strings, ie. regex-es for input, and % strings for output
115
116 # global variables
117
118 include_path = [os.getcwd ()]
119
120
121 lilypond_cmd = 'lilypond'
122 #lilypond_cmd = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp  --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
123
124
125 g_extra_opts = ''
126 g_here_dir = os.getcwd ()
127 g_dep_prefix = ''
128 g_outdir = ''
129 g_force_music_fontsize = 0
130 g_read_lys = 0
131 g_do_pictures = 1
132 g_do_music = 1
133 g_make_html = 0
134
135 format = ''
136 g_run_lilypond = 1
137 no_match = 'a\ba'
138
139 default_music_fontsize = 16
140 default_text_fontsize = 12
141 paperguru = None
142
143
144 class LatexPaper:
145         def __init__ (self):
146                 self.m_document_preamble = []
147                 self.m_num_cols = 1
148                 self.m_multicols = 1
149
150         def find_latex_dims (self):
151                 if g_outdir:
152                         fname = os.path.join (g_outdir, "lily-tmp.tex")
153                 else:
154                         fname = "lily-tmp.tex"
155                 try:
156                         f = open (fname, "w")
157                 except IOError:
158                         error ("Error creating temporary file '%s'" % fname)
159
160                 for s in self.m_document_preamble:
161                         f.write (s)
162                 f.write (r"""
163 \begin{document}
164 \typeout{---}
165 \typeout{\columnsep \the\columnsep}
166 \typeout{\textwidth \the\textwidth}
167 \typeout{---}
168 \end{document}
169                 """)
170                 f.close ()
171                 re_dim = re.compile (r"\\(\w+)\s+(\d+\.\d+)")
172
173                 cmd = "latex '\\nonstopmode \input %s'" % fname
174                 # Ugh.  (La)TeX writes progress and error messages on stdout
175                 # Redirect to stderr
176                 cmd += ' 1>/dev/stderr'
177                 status = ly.system (cmd, ignore_error = 1)
178                 signal = 0xf & status
179                 exit_status = status >> 8
180                 
181                 if status:
182                         ly.error (_ ("LaTeX failed."))
183                         ly.error (_ ("The error log is as follows:"))
184                         
185                         #URG see ly2dvi
186                         try:
187                                 lns = open ('lily-tmp.log').readlines ()
188                         except:
189                                 lns = ''
190                         countdown = -3
191                         for ln in lns:
192                                 sys.stderr.write (ln)
193                                 if re.match ('^!', ln):
194                                         countdown = 3
195
196                                 if countdown == 0:
197                                         break
198
199                                 if countdown > 0:
200                                         countdown = countdown -1
201
202                         sys.stderr.write ("  ... (further messages elided)...\n")
203                         sys.exit (1)
204
205                 lns = open ('lily-tmp.log').readlines ()
206                 for ln in lns:
207                         ln = string.strip (ln)
208                         m = re_dim.match (ln)
209                         if m:
210                                 if m.groups ()[0] in ('textwidth', 'columnsep'):
211                                         self.__dict__['m_%s' % m.groups ()[0]] = float (m.groups ()[1])
212
213                 try:
214                         os.remove (fname)
215                         os.remove (os.path.splitext (fname)[0]+".aux")
216                         os.remove (os.path.splitext (fname)[0]+".log")
217                 except:
218                         pass
219
220                 if not self.__dict__.has_key ('m_textwidth'):
221                         raise 'foo!'
222
223         def get_linewidth (self):
224                 if self.m_num_cols == 1:
225                         w = self.m_textwidth
226                 else:
227                         w = (self.m_textwidth - self.m_columnsep)/2
228                 if self.m_multicols > 1:
229                         return (w - self.m_columnsep* (self.m_multicols-1)) \
230                            / self.m_multicols
231                 return w
232
233
234 class HtmlPaper:
235         def __init__ (self):
236                 self.m_papersize = 'letterpaper'
237                 self.m_fontsize = 12
238         def get_linewidth (self):
239                 return html_linewidths[self.m_papersize][self.m_fontsize]
240
241 class TexiPaper:
242         def __init__ (self):
243                 self.m_papersize = 'letterpaper'
244                 self.m_fontsize = 12
245         def get_linewidth (self):
246                 return texi_linewidths[self.m_papersize][self.m_fontsize]
247
248 def mm2pt (x):
249         return x * 2.8452756
250 def in2pt (x):
251         return x * 72.26999
252 def em2pt (x, fontsize = 10):
253         return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
254 def ex2pt (x, fontsize = 10):
255         return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
256
257 def pt2pt (x):
258         return x
259
260 dimension_conversion_dict ={
261         'mm': mm2pt,
262         'cm': lambda x: mm2pt (10*x),
263         'in': in2pt,
264         'em': em2pt,
265         'ex': ex2pt,
266         'pt': pt2pt
267         }
268
269 # Convert numeric values, with or without specific dimension, to floats.
270 # Keep other strings
271 def conv_dimen_to_float (value):
272         if type (value) == type (""):
273                 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
274                 if m:
275                         unit = m.group (2)
276                         num = string.atof (m.group (1))
277                         conv =  dimension_conversion_dict[m.group (2)]
278
279                         value = conv (num)
280
281                 elif re.match ("^[0-9.]+$",value):
282                         value = float (value)
283
284         return value
285
286 texi_linewidths = {
287         'afourpaper': {12: mm2pt (160)},
288         'afourwide': {12: in2pt (6.5)},
289         'afourlatex': {12: mm2pt (150)},
290         'smallbook': {12: in2pt (5)},
291         'letterpaper': {12: in2pt (6)}}
292
293 html_linewidths = {
294         'afourpaper': {12: mm2pt (160)},
295         'afourwide': {12: in2pt (6.5)},
296         'afourlatex': {12: mm2pt (150)},
297         'smallbook': {12: in2pt (5)},
298         'letterpaper': {12: in2pt (6)}}
299
300 output_dict= {
301
302
303         'html' : {
304
305                 'output-lilypond': '''<lilypond%s>
306 %s
307 </lilypond>''',
308                 'output-filename' : r'''
309 <!-- %s >
310 <a href="%s">
311 <pre>%s</pre></a>:''',
312                 'output-lilypond-fragment': '''<lilypond%s>
313 \context Staff\context Voice{ %s }
314 </lilypond>''',
315                 'output-noinline': r'''
316 <!-- generated: %(fn)s.png !-->
317 ''',
318                 ## maybe <hr> ?
319                 'pagebreak': None,
320                 # Verbatim text is always finished with \n.  FIXME: For HTML,
321                 # this newline should be removed.
322                 'output-verbatim': r'''<pre>
323 %s</pre>''',
324                 # Verbatim text is always finished with \n.  FIXME: For HTML,
325                 # this newline should be removed.
326                 'output-small-verbatim': r'''<font size=-1><pre>
327 %s</pre></font>''',
328                 ## Ugh we need to differentiate on origin:
329                 ## lilypond-block origin wants an extra <p>, but
330                 ## inline music doesn't.
331                 ## possibly other center options?
332                 'output-html': r'''
333 <a href="%(fn)s.png">
334 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
335 ''',
336                 },
337
338
339         'latex': {
340
341                 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
342   \context Staff <
343     \context Voice{
344       %s
345     }
346   >
347 \end{lilypond}''',
348                 'output-filename' : r'''\verb+%s+:\\
349 %% %s
350 %% %s
351 ''',
352                 'output-lilypond': r'''\begin[%s]{lilypond}
353 %s
354 \end{lilypond}
355 ''',
356                 # verbatim text is always finished with \n
357                 'output-verbatim': r'''\begin{verbatim}
358 %s\end{verbatim}
359 ''',
360                 # verbatim text is always finished with \n
361                 'output-small-verbatim': r'''{\small\begin{verbatim}
362 %s\end{verbatim}}
363 ''',
364                 'output-default-post': "\\def\postLilypondExample{}\n",
365                 'output-default-pre': "\\def\preLilypondExample{}\n",
366                 'usepackage-graphics': '\\usepackage{graphics}\n',
367                 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s}}',
368                 'output-noinline': r'''
369 %% generated: %(fn)s.eps
370 ''',
371                 'output-latex-quoted': r'''{\preLilypondExample
372 \input %(fn)s.tex
373 \postLilypondExample}''',
374                 'output-latex-noquote': r'''{\parindent 0pt
375 \preLilypondExample
376 \input %(fn)s.tex
377 \postLilypondExample}''',
378                 'pagebreak': r'\pagebreak',
379                 },
380
381
382         'texi' : {
383
384                 'output-lilypond': '''@lilypond[%s]
385 %s
386 @end lilypond
387 ''',
388                 'output-filename' : r'''@ifnothtml
389 @file{%s}:@*
390 @end ifnothtml
391 @ifhtml
392 @uref{%s,@file{%s}}
393 @end ifhtml
394 ''',
395                 'output-lilypond-fragment': '''@lilypond[%s]
396 \context Staff\context Voice{ %s }
397 @end lilypond ''',
398                 'output-noinline': r'''
399 @c generated: %(fn)s.png
400 ''',
401                 'pagebreak': None,
402                 # verbatim text is always finished with \n
403                 'output-small-verbatim': r'''@smallexample
404 %s@end smallexample
405 ''',
406                 # verbatim text is always finished with \n
407                 'output-verbatim': r'''@example
408 %s@end example
409 ''',
410                 # do some tweaking: @ is needed in some ps stuff.
411                 #
412                 # ugh, the <p> below breaks inline images...
413                 'output-texi-noquote': r'''@tex
414 \catcode`\@=12
415 \parindent 0pt
416 \def\lilypondbook{}
417 \input %(fn)s.tex
418 \catcode`\@=0
419 @end tex
420 @html
421 <p><a href="%(fn)s.png">
422 <img border=0 src="%(fn)s.png" alt="[picture of music]">
423 </a><p>
424 @end html
425 ''',
426                 'output-texi-quoted': r'''@quotation
427 @tex
428 \catcode`\@=12
429 \def\lilypondbook{}
430 \input %(fn)s.tex
431 \catcode`\@=0
432 @end tex
433 @html
434 <a href="%(fn)s.png">
435 <img border=0 src="%(fn)s.png" alt="[picture of music]">
436 </a>
437 @end html
438 @end quotation
439 ''',
440                 }
441
442         }
443
444 def output_verbatim (body, small):
445         global format
446         if format == 'html':
447                 body = re.sub ('&', '&amp;', body)
448                 body = re.sub ('>', '&gt;', body)
449                 body = re.sub ('<', '&lt;', body)
450         elif format == 'texi':
451                 # clumsy workaround for python 2.2 pre bug.
452                 body = re.sub ('@', '@@', body)
453                 body = re.sub ('{', '@{', body)
454                 body = re.sub ('}', '@}', body)
455
456         if small:
457                 key = 'output-small-verbatim'
458         else:
459                 key = 'output-verbatim'
460         return get_output (key) % body
461
462
463 # Warning: This uses extended regular expressions.  Treat with care.
464 #
465 # legenda
466 #
467 # (?P<name>regex) -- assign result of REGEX to NAME
468 # *? -- match non-greedily.
469 # (?m) -- multiline regex: make ^ and $ match at each line
470 # (?s) -- make the dot match all characters including newline
471 re_dict = {
472         'html': {
473                 'include':  no_match,
474                 'input': no_match,
475                 'header': no_match,
476                 'preamble-end': no_match,
477                 'landscape': no_match,
478                 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
479                 'verb': r'''(?P<code><pre>.*?</pre>)''',
480                 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
481                 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
482                 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
483                 'option-sep' : '\s*',
484                 'intertext': r',?\s*intertext=\".*?\"',
485                 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
486                 'singleline-comment': no_match,
487                 'numcols': no_match,
488                 'multicols': no_match,
489                 },
490
491         'latex': {
492                 'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
493                 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
494                 'option-sep' : ',\s*',
495                 'header': r"\n*\\documentclass\s*(\[.*?\])?",
496                 'preamble-end': r'(?P<code>\\begin\s*{document})',
497                 'verbatim': r"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
498                 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
499                 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
500                 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
501                 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
502                 'def-post-re': r"\\def\\postLilypondExample",
503                 'def-pre-re': r"\\def\\preLilypondExample",
504                 'usepackage-graphics': r"\usepackage\s*{graphics}",
505                 'intertext': r',?\s*intertext=\".*?\"',
506                 'multiline-comment': no_match,
507                 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
508                 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
509                 'multicols': r"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
510                 },
511
512         # why do we have distinction between @mbinclude and @include?
513
514         'texi': {
515                 'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
516                 'input': no_match,
517                 'header': no_match,
518                 'preamble-end': no_match,
519                 'landscape': no_match,
520                 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
521                 'verb': r'''(?P<code>@code{.*?})''',
522                 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
523                 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
524                 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
525                 'option-sep' : ',\s*',
526                 'intertext': r',?\s*intertext=\".*?\"',
527                 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
528                 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
529                 'numcols': no_match,
530                 'multicols': no_match,
531                 }
532         }
533
534
535 for r in re_dict.keys ():
536         olddict = re_dict[r]
537         newdict = {}
538         for k in olddict.keys ():
539                 try:
540                         newdict[k] = re.compile (olddict[k])
541                 except:
542                         print 'invalid regexp: %s' % olddict[k]
543
544                         ## we'd like to catch and reraise a more
545                         ## detailed error, but alas, the exceptions
546                         ## changed across the 1.5/2.1 boundary.
547
548                         raise "Invalid re"
549         re_dict[r] = newdict
550
551
552 def uniq (list):
553         list.sort ()
554         s = list
555         list = []
556         for x in s:
557                 if x not in list:
558                         list.append (x)
559         return list
560
561
562 def get_output (name):
563         return  output_dict[format][name]
564
565 def get_re (name):
566         return  re_dict[format][name]
567
568 def bounding_box_dimensions (fname):
569         if g_outdir:
570                 fname = os.path.join (g_outdir, fname)
571         try:
572                 fd = open (fname)
573         except IOError:
574                 error ("Error opening `%s'" % fname)
575         str = fd.read ()
576         s = re.search ('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
577         if s:
578
579                 gs = map (lambda x: string.atoi (x), s.groups ())
580                 return (int (gs[2] - gs[0] + 0.5),
581                         int (gs[3] - gs[1] + 0.5))
582         else:
583                 return (0,0)
584
585 def error (str):
586         sys.stderr.write ("\n\n" + str + "\nExiting ... \n\n")
587         raise 'Exiting.'
588
589
590 def compose_full_body (body, opts):
591         '''Construct the lilypond code to send to Lilypond.
592         Add stuff to BODY using OPTS as options.'''
593         music_size = default_music_fontsize
594         if g_force_music_fontsize:
595                 music_size = g_force_music_fontsize
596         indent = ''
597         linewidth = ''
598         notime = ''
599         for o in opts:
600                 if not g_force_music_fontsize:
601                         m = re.match ('([0-9]+)pt', o)
602                         if m:
603                                 music_size = string.atoi (m.group (1))
604
605                 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
606                 if m:
607                         f = float (m.group (1))
608                         indent = 'indent = %f\\%s' % (f, m.group (2))
609
610                 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
611                 if m:
612                         f = float (m.group (1))
613                         linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
614
615         if re.search ('\\\\score', body):
616                 is_fragment = 0
617         else:
618                 is_fragment = 1
619         if 'fragment' in opts:
620                 is_fragment = 1
621         if 'nofragment' in opts:
622                 is_fragment = 0
623
624         if is_fragment and not 'multiline' in opts:
625                 opts.append ('singleline')
626
627         if 'singleline' in opts:
628                 if not linewidth:
629                         linewidth = 'linewidth = -1.0'
630                 if not indent:
631                         indent = 'indent = 0.0\mm'
632         elif not linewidth:
633                 global paperguru
634                 l = paperguru.get_linewidth ()
635                 linewidth = 'linewidth = %f\pt' % l
636
637         if 'noindent' in opts:
638                 indent = 'indent = 0.0\mm'
639
640         if 'notime' in opts:
641                 notime = r'''
642 \translator {
643   \StaffContext
644   \remove Time_signature_engraver
645 }
646 '''
647
648         for o in opts:
649                 m= re.search ('relative(.*)', o)
650                 v = 0
651                 if m:
652                         try:
653                                 v = string.atoi (m.group (1))
654                         except ValueError:
655                                 pass
656
657                         v = v + 1
658                         pitch = 'c'
659                         if v < 0:
660                                 pitch = pitch + '\,' * v
661                         elif v > 0:
662                                 pitch = pitch + '\'' * v
663
664                         body = '\\relative %s { %s }' % (pitch, body)
665
666         if is_fragment:
667                 body = r'''
668 \score {
669   \notes {
670 %s
671   }
672 }
673 ''' % body
674
675         opts = uniq (opts)
676         optstring = string.join (opts, ' ')
677         optstring = re.sub ('\n', ' ', optstring)
678         body = r'''
679 %% Generated automatically by: lilypond-book.py
680 %% options are %s
681 \include "paper%d.ly"
682 \paper  {
683   %s
684   %s
685   %s
686 }
687 ''' % (optstring, music_size, linewidth, indent, notime) + body
688
689         # ughUGH not original options
690         return body
691
692 def scan_html_preamble (chunks):
693         return
694
695 def scan_latex_preamble (chunks):
696         # First we want to scan the \documentclass line
697         # it should be the first non-comment line.
698         # The only thing we really need to know about the \documentclass line
699         # is if there are one or two columns to begin with.
700         idx = 0
701         while 1:
702                 if chunks[idx][0] == 'ignore':
703                         idx = idx + 1
704                         continue
705                 m = get_re ('header').match (chunks[idx][1])
706                 if not m:
707                         error ("Latex documents must start with a \documentclass command")
708                 if m.group (1):
709                         options = re.split (',[\n \t]*', m.group (1)[1:-1])
710                 else:
711                         options = []
712                 if 'twocolumn' in options:
713                         paperguru.m_num_cols = 2
714                 break
715
716
717         # Then we add everything before \begin{document} to
718         # paperguru.m_document_preamble so that we can later write this header
719         # to a temporary file in find_latex_dims() to find textwidth.
720         while idx < len (chunks) and chunks[idx][0] != 'preamble-end':
721                 if chunks[idx] == 'ignore':
722                         idx = idx + 1
723                         continue
724                 paperguru.m_document_preamble.append (chunks[idx][1])
725                 idx = idx + 1
726
727         if len (chunks) == idx:
728                 error ("Didn't find end of preamble (\\begin{document})")
729
730         paperguru.find_latex_dims ()
731
732 def scan_texi_preamble (chunks):
733         # this is not bulletproof..., it checks the first 10 chunks
734         for c in chunks[:10]:
735                 if c[0] == 'input':
736                         for s in ('afourpaper', 'afourwide', 'letterpaper',
737                                   'afourlatex', 'smallbook'):
738                                 if string.find (c[1], "@%s" % s) != -1:
739                                         paperguru.m_papersize = s
740
741
742 def scan_preamble (chunks):
743         global format
744         if format == 'html':
745                 scan_html_preamble (chunks)
746         elif format == 'latex':
747                 scan_latex_preamble (chunks)
748         elif format == 'texi':
749                 scan_texi_preamble (chunks)
750
751
752 def completize_preamble (chunks):
753         global format
754         if format != 'latex':
755                 return chunks
756         pre_b = post_b = graphics_b = None
757         for chunk in chunks:
758                 if chunk[0] == 'preamble-end':
759                         break
760                 if chunk[0] == 'input':
761                         m = get_re ('def-pre-re').search (chunk[1])
762                         if m:
763                                 pre_b = 1
764                 if chunk[0] == 'input':
765                         m = get_re ('def-post-re').search (chunk[1])
766                         if m:
767                                 post_b = 1
768
769                 if chunk[0] == 'input':
770                         m = get_re ('usepackage-graphics').search (chunk[1])
771                         if m:
772                                 graphics_b = 1
773         x = 0
774         while x < len (chunks) and   chunks[x][0] != 'preamble-end':
775                 x = x + 1
776
777         if x == len (chunks):
778                 return chunks
779
780         if not pre_b:
781                 chunks.insert (x, ('input', get_output ('output-default-pre')))
782         if not post_b:
783                 chunks.insert (x, ('input', get_output ('output-default-post')))
784         if not graphics_b:
785                 chunks.insert (x, ('input', get_output ('usepackage-graphics')))
786
787         return chunks
788
789
790 read_files = []
791 def find_file (name):
792         '''
793         Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
794         '''
795
796         if name == '-':
797                 return (sys.stdin.read (), '<stdin>')
798         f = None
799         nm = ''
800         for a in include_path:
801                 try:
802                         nm = os.path.join (a, name)
803                         f = open (nm)
804                         global read_files
805                         read_files.append (nm)
806                         break
807                 except IOError:
808                         pass
809         if f:
810                 sys.stderr.write ("Reading `%s'\n" % nm)
811                 return (f.read (), nm)
812         else:
813                 error ("File not found `%s'\n" % name)
814                 return ('', '')
815
816 def do_ignore (match_object):
817         return [('ignore', match_object.group ('code'))]
818 def do_preamble_end (match_object):
819         return [('preamble-end', match_object.group ('code'))]
820
821 def make_verbatim (match_object):
822         return [('verbatim', match_object.group ('code'))]
823
824 def make_verb (match_object):
825         return [('verb', match_object.group ('code'))]
826
827 def do_include_file (m):
828         "m: MatchObject"
829         return [('input', get_output ('pagebreak'))] \
830              + read_doc_file (m.group ('filename')) \
831              + [('input', get_output ('pagebreak'))]
832
833 def do_input_file (m):
834         return read_doc_file (m.group ('filename'))
835
836 def make_lilypond (m):
837         if m.group ('options'):
838                 options = m.group ('options')
839         else:
840                 options = ''
841         return [('input', get_output ('output-lilypond-fragment') %
842                         (options, m.group ('code')))]
843
844 def make_lilypond_file (m):
845         '''
846
847         Find @lilypondfile{bla.ly} occurences and substitute bla.ly
848         into a @lilypond .. @end lilypond block.
849
850         '''
851
852         if m.group ('options'):
853                 options = m.group ('options')
854         else:
855                 options = ''
856         (content, nm) = find_file (m.group ('filename'))
857         options = "filename=%s," % nm + options
858
859         return [('input', get_output ('output-lilypond') %
860                         (options, content))]
861
862 def make_lilypond_block (m):
863         if not g_do_music:
864                 return []
865
866         if m.group ('options'):
867                 options = get_re ('option-sep').split (m.group ('options'))
868         else:
869                 options = []
870         options = filter (lambda s: s != '', options)
871         return [('lilypond', m.group ('code'), options)]
872
873 def do_columns (m):
874         global format
875         if format != 'latex':
876                 return []
877         if m.group ('num') == 'one':
878                 return [('numcols', m.group ('code'), 1)]
879         if m.group ('num') == 'two':
880                 return [('numcols', m.group ('code'), 2)]
881
882 def do_multicols (m):
883         global format
884         if format != 'latex':
885                 return []
886         if m.group ('be') == 'begin':
887                 return [('multicols', m.group ('code'), int (m.group ('num')))]
888         else:
889                 return [('multicols', m.group ('code'), 1)]
890         return []
891
892 def chop_chunks (chunks, re_name, func, use_match=0):
893         newchunks = []
894         for c in chunks:
895                 if c[0] == 'input':
896                         str = c[1]
897                         while str:
898                                 m = get_re (re_name).search (str)
899                                 if m == None:
900                                         newchunks.append (('input', str))
901                                         str = ''
902                                 else:
903                                         if use_match:
904                                                 newchunks.append (('input', str[:m.start ('match')]))
905                                         else:
906                                                 newchunks.append (('input', str[:m.start (0)]))
907                                         #newchunks.extend (func (m))
908                                         # python 1.5 compatible:
909                                         newchunks = newchunks + func (m)
910                                         str = str [m.end (0):]
911                 else:
912                         newchunks.append (c)
913         return newchunks
914
915 def determine_format (str):
916         global format
917         if format == '':
918                 html = re.search ('(?i)<[dh]tml', str[:200])
919                 latex = re.search (r'''\\document''', str[:200])
920                 texi = re.search ('@node|@setfilename', str[:200])
921
922                 f = ''
923                 g = None
924
925                 if html and not latex and not texi:
926                         f = 'html'
927                 elif latex and not html and not texi:
928                         f = 'latex'
929                 elif texi and not html and not latex:
930                         f = 'texi'
931                 else:
932                         error ("can't determine format, please specify")
933                 format = f
934
935         global paperguru
936         if paperguru == None:
937                 if format == 'html':
938                         g = HtmlPaper ()
939                 elif format == 'latex':
940                         g = LatexPaper ()
941                 elif format == 'texi':
942                         g = TexiPaper ()
943
944                 paperguru = g
945
946
947 def read_doc_file (filename):
948         '''Read the input file, find verbatim chunks and do \input and \include
949         '''
950         (str, path) = find_file (filename)
951         determine_format (str)
952
953         chunks = [('input', str)]
954
955         # we have to check for verbatim before doing include,
956         # because we don't want to include files that are mentioned
957         # inside a verbatim environment
958         chunks = chop_chunks (chunks, 'verbatim', make_verbatim)
959
960         chunks = chop_chunks (chunks, 'verb', make_verb)
961         chunks = chop_chunks (chunks, 'multiline-comment', do_ignore)
962         #ugh fix input
963         chunks = chop_chunks (chunks, 'include', do_include_file, 1)
964         chunks = chop_chunks (chunks, 'input', do_input_file, 1)
965         return chunks
966
967
968 taken_file_names = {}
969 def schedule_lilypond_block (chunk):
970         '''Take the body and options from CHUNK, figure out how the
971         real .ly should look, and what should be left MAIN_STR (meant
972         for the main file).  The .ly is written, and scheduled in
973         TODO.
974
975         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
976
977         TODO has format [basename, extension, extension, ... ]
978         '''
979         (type, body, opts) = chunk
980         assert type == 'lilypond'
981         file_body = compose_full_body (body, opts)
982         ## Hmm, we should hash only lilypond source, and skip the
983         ## %options are ...
984         ## comment line
985         basename = 'lily-' + `abs (hash (file_body))`
986         for o in opts:
987                 m = re.search ('filename="(.*?)"', o)
988                 if m:
989                         basename = m.group (1)
990                         if not taken_file_names.has_key (basename):
991                                 taken_file_names[basename] = 0
992                         else:
993                                 taken_file_names[basename] = taken_file_names[basename] + 1
994                                 basename = basename + "-%i" % taken_file_names[basename]
995         if not g_read_lys:
996                 update_file (file_body, os.path.join (g_outdir, basename) + '.ly')
997         needed_filetypes = ['tex']
998
999         if format == 'html' or g_make_html:
1000                 needed_filetypes.append ('eps')
1001                 needed_filetypes.append ('png')
1002         if 'eps' in opts and not ('eps' in needed_filetypes):
1003                 needed_filetypes.append ('eps')
1004         pathbase = os.path.join (g_outdir, basename)
1005         def f (base, ext1, ext2):
1006                 a = os.path.isfile (base + ext2)
1007                 if (os.path.isfile (base + ext1) and
1008                     os.path.isfile (base + ext2) and
1009                                 os.stat (base+ext1)[stat.ST_MTIME] >
1010                                 os.stat (base+ext2)[stat.ST_MTIME]) or \
1011                                 not os.path.isfile (base + ext2):
1012                         return 1
1013         todo = []
1014         if 'tex' in needed_filetypes and f (pathbase, '.ly', '.tex'):
1015                 todo.append ('tex')
1016         if 'eps' in needed_filetypes and f (pathbase, '.tex', '.eps'):
1017                 todo.append ('eps')
1018         if 'png' in needed_filetypes and f (pathbase, '.eps', '.png'):
1019                 todo.append ('png')
1020         newbody = ''
1021
1022         if 'printfilename' in opts:
1023                 for o in opts:
1024                         m= re.match ("filename=(.*)", o)
1025                         if m:
1026                                 newbody = newbody + get_output ("output-filename") % (m.group (1), basename + '.ly', m.group (1))
1027                                 break
1028
1029
1030         if 'smallverbatim' in opts:
1031                 newbody = newbody + output_verbatim (body, 1)
1032         elif 'verbatim' in opts:
1033                 newbody = newbody + output_verbatim (body, 0)
1034
1035         for o in opts:
1036                 m = re.search ('intertext="(.*?)"', o)
1037                 if m:
1038                         newbody = newbody + "\n"
1039                         if format == 'texi':
1040                                 newbody = newbody + "@noindent\n"
1041                         elif format == 'latex':
1042                                 newbody = newbody + "\\noindent\n"
1043                         newbody = newbody + m.group (1) + "\n"
1044
1045         if 'noinline' in opts:
1046                 s = 'output-noinline'
1047         elif format == 'latex':
1048                 if 'eps' in opts:
1049                         s = 'output-eps'
1050                 else:
1051                         if 'noquote' in opts:
1052                                 s = 'output-latex-noquote'
1053                         else:
1054                                 s = 'output-latex-quoted'
1055         elif format == 'texi':
1056                 if 'noquote' in opts:
1057                         s = 'output-texi-noquote'
1058                 else:
1059                         s = 'output-texi-quoted'
1060         else: # format == 'html'
1061                 s = 'output-html'
1062         newbody = newbody + get_output (s) % {'fn': basename }
1063         return ('lilypond', newbody, opts, todo, basename)
1064
1065 def process_lilypond_blocks (chunks):#ugh rename
1066         newchunks = []
1067         # Count sections/chapters.
1068         for c in chunks:
1069                 if c[0] == 'lilypond':
1070                         c = schedule_lilypond_block (c)
1071                 elif c[0] == 'numcols':
1072                         paperguru.m_num_cols = c[2]
1073                 elif c[0] == 'multicols':
1074                         paperguru.m_multicols = c[2]
1075                 newchunks.append (c)
1076         return newchunks
1077
1078 def compile_all_files (chunks):
1079         global foutn
1080         eps = []
1081         tex = []
1082         png = []
1083
1084         for c in chunks:
1085                 if c[0] != 'lilypond':
1086                         continue
1087                 base  = c[4]
1088                 exts = c[3]
1089                 for e in exts:
1090                         if e == 'eps':
1091                                 eps.append (base)
1092                         elif e == 'tex':
1093                                 #ugh
1094                                 if base + '.ly' not in tex:
1095                                         tex.append (base + '.ly')
1096                         elif e == 'png' and g_do_pictures:
1097                                 png.append (base)
1098         d = os.getcwd ()
1099         if g_outdir:
1100                 os.chdir (g_outdir)
1101         if tex:
1102                 # fixme: be sys-independent.
1103                 def incl_opt (x):
1104                         if g_outdir and x[0] != '/' :
1105                                 x = os.path.join (g_here_dir, x)
1106                         return ' -I %s' % x
1107
1108                 incs = map (incl_opt, include_path)
1109                 lilyopts = string.join (incs)
1110                 if do_deps:
1111                         lilyopts += ' --dependencies'
1112                         if g_outdir:
1113                                 lilyopts += ' --dep-prefix=' + g_outdir + '/'
1114                 lilyopts += ' --header=texidoc'
1115                 texfiles = string.join (tex)
1116                 cmd = string.join ((lilypond_cmd, lilyopts, g_extra_opts,
1117                                     texfiles))
1118                 ly.system (cmd, ignore_error = 0, progress_p = 1)
1119
1120                 #
1121                 # Ugh, fixing up dependencies for .tex generation
1122                 #
1123                 if do_deps:
1124                         depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep',
1125                                                         x), tex)
1126
1127                         for i in depfiles:
1128                                 f =open (i)
1129                                 text=f.read ()
1130                                 f.close ()
1131                                 text=re.sub ('\n([^:\n]*):',
1132                                              '\n' + foutn + ':', text)
1133                                 f = open (i, 'w')
1134                                 f.write (text)
1135                                 f.close ()
1136
1137         def to_eps (file):
1138                 cmd = r"latex '\nonstopmode \input %s'" % file
1139                 # Ugh.  (La)TeX writes progress and error messages on stdout
1140                 # Redirect to stderr
1141                 cmd += ' 1>/dev/stderr'
1142                 ly.system (cmd)
1143                 ly.system ("dvips -E -o %s.eps %s" % (file, file))
1144         map (to_eps, eps)
1145
1146         map (ly.make_preview, png)
1147         os.chdir (d)
1148
1149
1150 def update_file (body, name):
1151         '''
1152         write the body if it has changed
1153         '''
1154         same = 0
1155         try:
1156                 f = open (name)
1157                 fs = f.read (-1)
1158                 same = (fs == body)
1159         except:
1160                 pass
1161
1162         if not same:
1163                 f = open (name , 'w')
1164                 f.write (body)
1165                 f.close ()
1166
1167         return not same
1168
1169
1170 def write_deps (fn, target, chunks):
1171         global read_files
1172         sys.stderr.write ('Writing `%s\'\n' % os.path.join (g_outdir, fn))
1173         f = open (os.path.join (g_outdir, fn), 'w')
1174         f.write ('%s%s: ' % (g_dep_prefix, target))
1175         for d in read_files:
1176                 f.write ('%s ' %  d)
1177         basenames=[]
1178         for c in chunks:
1179                 if c[0] == 'lilypond':
1180                         (type, body, opts, todo, basename) = c;
1181                         basenames.append (basename)
1182         for d in basenames:
1183                 if g_outdir:
1184                         d=g_outdir + '/' + d
1185                 if g_dep_prefix:
1186                         #if not os.isfile (d): # thinko?
1187                         if not re.search ('/', d):
1188                                 d = g_dep_prefix + d
1189                 f.write ('%s.tex ' %  d)
1190         f.write ('\n')
1191         #if len (basenames):
1192         #       for d in basenames:
1193         #               f.write ('%s.ly ' %  d)
1194         #       f.write (' : %s' % target)
1195         f.write ('\n')
1196         f.close ()
1197         read_files = []
1198
1199 def check_texidoc (chunks):
1200         n = []
1201         for c in chunks:
1202                 if c[0] == 'lilypond':
1203                         (type, body, opts, todo, basename) = c;
1204                         pathbase = os.path.join (g_outdir, basename)
1205                         if os.path.isfile (pathbase + '.texidoc') \
1206                            and 'notexidoc' not in opts:
1207                                 body = '\n@include %s.texidoc\n' % basename + body
1208                                 c = (type, body, opts, todo, basename)
1209                 n.append (c)
1210         return n
1211
1212
1213 ## what's this? Docme --hwn
1214 ##
1215 def fix_epswidth (chunks):
1216         newchunks = []
1217         for c in chunks:
1218                 if c[0] != 'lilypond' or 'eps' not in c[2]:
1219                         newchunks.append (c)
1220                         continue
1221
1222                 mag = 1.0
1223                 for o in c[2]:
1224                         m  = re.match ('magnification=([0-9.]+)', o)
1225                         if m:
1226                                 mag = string.atof (m.group (1))
1227
1228                 def replace_eps_dim (match, lmag = mag):
1229                         filename = match.group (1)
1230                         dims = bounding_box_dimensions (filename)
1231
1232                         return '%fpt' % (dims[0] *lmag)
1233
1234                 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1235                 newchunks.append (('lilypond', body, c[2], c[3], c[4]))
1236
1237         return newchunks
1238
1239
1240 ##docme: why global?
1241 foutn=""
1242
1243 def do_file (input_filename):
1244         chunks = read_doc_file (input_filename)
1245         chunks = chop_chunks (chunks, 'lilypond', make_lilypond, 1)
1246         chunks = chop_chunks (chunks, 'lilypond-file', make_lilypond_file, 1)
1247         chunks = chop_chunks (chunks, 'lilypond-block', make_lilypond_block, 1)
1248         chunks = chop_chunks (chunks, 'singleline-comment', do_ignore, 1)
1249         chunks = chop_chunks (chunks, 'preamble-end', do_preamble_end)
1250         chunks = chop_chunks (chunks, 'numcols', do_columns)
1251         chunks = chop_chunks (chunks, 'multicols', do_multicols)
1252         #print "-" * 50
1253         #for c in chunks: print "c:", c;
1254         #sys.exit ()
1255         scan_preamble (chunks)
1256         chunks = process_lilypond_blocks (chunks)
1257
1258         # Do It.
1259         global g_run_lilypond
1260         if g_run_lilypond:
1261                 compile_all_files (chunks)
1262                 chunks = fix_epswidth (chunks)
1263
1264         global format
1265         if format == 'texi':
1266                 chunks = check_texidoc (chunks)
1267
1268         x = 0
1269         chunks = completize_preamble (chunks)
1270
1271         global foutn
1272
1273         if outname:
1274                 my_outname = outname
1275         elif input_filename == '-' or input_filename == "/dev/stdin":
1276                 my_outname = '-'
1277         else:
1278                 my_outname = os.path.basename (os.path.splitext (input_filename)[0]) + '.' + format
1279         my_depname = my_outname + '.dep'
1280
1281         if my_outname == '-' or my_outname == '/dev/stdout':
1282                 fout = sys.stdout
1283                 foutn = "<stdout>"
1284                 global do_deps
1285                 do_deps = 0
1286         else:
1287                 foutn = os.path.join (g_outdir, my_outname)
1288                 sys.stderr.write ("Writing `%s'\n" % foutn)
1289                 fout = open (foutn, 'w')
1290         for c in chunks:
1291                 fout.write (c[1])
1292         fout.close ()
1293         # should chmod -w
1294
1295         if do_deps:
1296                 write_deps (my_depname, foutn, chunks)
1297
1298 outname = ''
1299 try:
1300         (sh, long) = ly.getopt_args (option_definitions)
1301         (options, files) = getopt.getopt (sys.argv[1:], sh, long)
1302         
1303 except getopt.error, msg:
1304         sys.stderr.write ('\n')
1305         ly.error (_ ("getopt says: `%s\'" % s))
1306         sys.stderr.write ('\n')
1307         ly.help ()
1308         ly.exit (2)
1309
1310 do_deps = 0
1311 for opt in options:
1312         o = opt[0]
1313         a = opt[1]
1314
1315         if o == '--include' or o == '-I':
1316                 include_path.append (a)
1317         elif o == '--version' or o == '-v':
1318                 ly.identify (sys.stdout)
1319                 sys.exit (0)
1320         elif o == '--verbose' or o == '-V':
1321                 verbose_p = 1
1322         elif o == '--format' or o == '-f':
1323                 format = a
1324                 if a == 'texi-html':
1325                         format = 'texi'
1326                         g_make_html = 1
1327         elif o == '--outname' or o == '-o':
1328                 if len (files) > 1:
1329                         #HACK
1330                         sys.stderr.write ("Lilypond-book is confused by --outname on multiple files")
1331                         sys.exit (1)
1332                 outname = a
1333         elif o == '--help' or o == '-h':
1334                 ly.help ()
1335                 sys.exit (0)
1336         elif o == '--no-lily' or o == '-n':
1337                 g_run_lilypond = 0
1338         elif o == '--preview-resolution':
1339                 preview_resolution = string.atoi (a)
1340         elif o == '--dependencies' or o == '-M':
1341                 do_deps = 1
1342         elif o == '--default-music-fontsize':
1343                 default_music_fontsize = string.atoi (a)
1344         elif o == '--default-lilypond-fontsize':
1345                 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1346                 default_music_fontsize = string.atoi (a)
1347         elif o == '--extra-options':
1348                 g_extra_opts = a
1349         elif o == '--force-music-fontsize':
1350                 g_force_music_fontsize = string.atoi (a)
1351         elif o == '--force-lilypond-fontsize':
1352                 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1353                 g_force_music_fontsize = string.atoi (a)
1354         elif o == '--dep-prefix':
1355                 g_dep_prefix = a
1356         elif o == '--no-pictures':
1357                 g_do_pictures = 0
1358         elif o == '--no-music':
1359                 g_do_music = 0
1360         elif o == '--read-lys':
1361                 g_read_lys = 1
1362         elif o == '--outdir':
1363                 g_outdir = a
1364         elif o == '--warranty' or o == '-w':
1365                 #status = os.system ('lilypond -w')
1366                 if 1 or status:
1367                         ly.warranty ()
1368                 sys.exit (0)
1369
1370 ly.identify (sys.stderr)
1371
1372 if g_outdir:
1373         if os.path.isfile (g_outdir):
1374                 error ("outdir is a file: %s" % g_outdir)
1375         if not os.path.exists (g_outdir):
1376                 os.mkdir (g_outdir)
1377                 
1378 if not files:
1379         ly.help ()
1380         ly.error (_ ("no files specified on command line"))
1381         ly.exit (2)
1382
1383 ly.setup_environment ()
1384
1385 for input_filename in files:
1386         do_file (input_filename)
1387
1388
1389 #
1390 # Petr, ik zou willen dat ik iets zinvoller deed,
1391 # maar wat ik kan ik doen, het verandert toch niets?
1392 #   --hwn 20/aug/99