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