]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
release: 1.3.125
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2
3
4 # TODO:
5 #
6 # * Rewrite this.  The control structure is too hairy.
7 # * (c) on page 1
8 # * more helpful info on lily crashes
9 # * Should use files in /tmp/ only.  This potentially messes with
10 # usergenerated files in the CWD
11
12
13 """
14 =======================================================================
15 LilyPond to dvi converter
16
17 Features include Title information, paper size specification, and image
18 orientation.  
19
20 Usage: ly2dvi.py [OPTION]... [FILE]...
21 Input: LilyPond source or LilyPond generated TeX files
22 Output: DVI file
23 =======================================================================
24 """
25
26 name = 'ly2dvi'
27 version = '@TOPLEVEL_VERSION@'
28 errorlog = ''
29
30 import sys
31 import os
32 import getopt
33 import re
34 import string
35 import time
36 import glob
37 import tempfile
38
39 # Can't grep localized msgs
40 os.environ['LC_ALL'] = ''
41 os.environ['LANG'] = ''
42 os.environ['LC_LANG'] = ''
43
44
45
46 class Input:
47     """
48     This class handles all ly2dvi.py input file methods
49     
50     Public methods:
51     
52     __init__()  Constructor
53     open(file)  Open a .ly file or .tex file using lilyinclude path
54     close()     Close current file
55     type()      Determine file type .ly (input) or .tex (output)
56     setVars()   Set title definitions found in .tex (output) file
57     """
58
59     #
60     # Constructors
61     #
62
63     def __init__(this):
64        this.__fd = None 
65
66     #
67     # open
68     #
69     def open(this,file):
70         """
71         open file and set private class variable __fd.  The search
72         sequence is: current directory followed by the directories
73         found in include property list.  Each directory is searched
74         for file, file.ly, file.sly and file.fly.
75         
76         input:  file   filename
77         output: void
78         error:  ExitNotFound Exception
79         """
80
81         for i in [''] + Props.get('include')[0:]:
82             ifile = os.path.join(i,file)
83             for j in ['','.ly','.fly', '.sly']:
84                 jfile = ifile+j
85                 try:
86                     this.__fd = open( jfile, 'r' )
87                     return
88                 except:
89                     pass
90         sys.exit('ExitNotFound', file)
91
92
93     #
94     # close
95     #
96     def close(this):
97         """
98         close file object __fd
99         
100         input:  void
101         output: void
102         error:  None
103         """
104         this.__fd.close()
105
106
107     #
108     # type
109     #
110     def type(this):
111         """
112         Determine input file type.  LilyPond source is 'input' type
113         and LilyPond generated TeX file is 'output' type
114
115         input:  void
116         output: 'input' | 'output'
117         error:  None
118         """
119
120         firstline = this.__fd.readline()
121         this.__fd.seek(0)
122         if  re.match('%created by: GNU LilyPond [0-9]+[.0-9]+',firstline ):
123             return 'output'
124         else:
125             return 'source'
126
127
128     #
129     # setVars
130     #
131     def setVars(this):  
132         """
133         Search for properties in the current input file and set the
134         appropriate values.  The supported properties names are in
135         local variable varTable along with the property list
136         titledefs.
137
138         input:  void
139         output: None
140         error:  None
141         """
142
143         varTable = [
144             #   regexp              set method
145             #   ------              ----------
146             ( 'language',         Props.setLanguage ),
147             ( 'latexheaders',     Props.setHeader ),
148             ( 'paperorientation', Props.setOrientation ),
149             ( 'paperpapersize',   Props.setPaperZize ),
150             ( 'papertextheight',  Props.setTextHeight ),
151             ( 'paperlinewidth',   Props.setLineWidth ),
152             ( 'filename',         Props.setFilename ),
153             ]
154
155         titles={}
156         for line in this.__fd.readlines():
157             m=re.match('\\\\def\\\\lilypond([\w]+){(.*)}',line)
158             if m:
159                 for var in varTable:
160                     if m.group(1) == var[0]:
161                         var[1](m.group(2),'file')
162                         break
163                 for var in Props.get('titledefs'):
164                     if m.group(1) == var:
165                         titles[var]=m.group(2)
166                         break
167         Props.setTitles(titles,'file')
168         this.__fd.seek(0)
169
170 \f
171
172 class TeXOutput:
173     """
174     This class handles all ly2dvi.py output file methods
175
176     private methods:
177      __lilypondDefs(opt)  Send title info to output file
178
179     Public methods:
180     __init__()  Constructor
181     write(str)  Write a string to output file 
182     start(file) Start the latex file
183     next()      Process next output file
184     end()       Finish latex file and run latex 
185     """
186
187     #
188     # constructor
189     #
190     def __init__(this):
191        this.__fd = None 
192        this.__base = ''
193        this.__outfile = ''
194
195     #
196     # __medelaDefs
197     #
198     def __lilypondDefs(this,opt):
199         """
200         Write titles to output
201
202         input:  opt   Supports header and subheader output
203         output: None
204         error:  None
205         """
206
207         titles = Props.get('titles')
208         for key in titles.keys():
209             this.write('%s\\lilypond%s{%s}%%\n' % (opt,key,titles[key]))
210
211     #
212     # write
213     #
214     def write(this,str):
215         """
216         Write str to current output file
217
218         input:  str  String to write
219         output: None
220         error:  None
221         """
222         
223         this.__fd.write(str)
224
225     #
226     # start
227     #
228     def start(this,file):
229         """
230         Start LaTeX file. Sets the linewidth (and possibly the
231         textheight) and leaves the page layout to the geometry
232         package. Creates temporary output filename and opens it
233         for write. Sends the LaTeX header information to output.
234         Lastly sends the title information to output.
235
236         input:  file  output file name 
237         output: None
238         error:  None
239         """
240
241         now=time.asctime(time.localtime(time.time()))
242
243         # Only set the textheight if it was explicitly set by the user,
244         # otherwise use the default. Helps to handle landscape correctly!
245         if Props.get('textheight') > 0:
246             textheightsetting = ',textheight=' + `Props.get('textheight')` + 'pt'
247         else:
248             textheightsetting = ''
249
250
251         top= r"""
252 %% Creator: %s
253 %% Automatically generated from  %s, %s
254
255 \documentclass[%s]{article}
256
257 %s 
258 \usepackage{geometry}
259 \usepackage[latin1]{inputenc} 
260 %%\usepackage[T1]{fontenc} 
261 %%
262 %s 
263 %% don not waste unused space at bottom of page
264 %% (unless we have footnotes ...)
265 %%\headheight9pt
266 %%\headsep0pt
267 %% Maybe this is too drastic, but let us give it a try.
268 \geometry{width=%spt%s,headheight=2mm,headsep=0pt,footskip=2mm,%s} 
269 \input{titledefs}
270 %s
271 \makeatletter
272 \renewcommand{\@oddhead}{\parbox{\textwidth}%%
273     {\mbox{}\small\theheader\hfill\textbf{\thepage}}}%%
274 %% UGR.
275 %%\renewcommand{\@evenhead}{eve!{\small\lilypondinstrument{,}\quad\textbf{\thepage}}\hfil}%%
276 \renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\thefooter}}%%
277 \begin{document}
278 """ % ( program_id(), Props.get('filename'), now, Props.get('papersize'),
279         Props.get('language'), Props.get('pagenumber'), Props.get('linewidth'),
280         textheightsetting, Props.get('orientation'), Props.get('header') )
281         
282         base, ext = os.path.splitext(file)
283         this.__base = base
284         tempfile.template= base + '_ly'
285         this.__outfile = tempfile.mktemp(ext)
286         base, ext = os.path.splitext(this.__outfile)
287         this.__tmpbase = base
288         try:
289             this.__fd = open(this.__outfile,"w")
290         except:
291             sys.exit('ExitNoWrite', this.__outfile)
292         this.write(top)
293         this.__lilypondDefs('')
294         this.write("""\
295 \\makelilytitle
296 """) 
297
298     #
299     # next
300     #
301     def next(this):
302         """
303         Write LaTeX subheader information to support more than one
304         score in a document.  Lastly send current title information to
305         output.
306
307         input:  None
308         output: None
309         error:  None
310         """
311
312         this.write("""\
313 \\def\\theopus{}%
314 \\def\\thepiece{}%
315 \\def\\lilypondopus{}%
316 \\def\\lilypondpiece{}%
317 """)
318         this.__lilypondDefs("\\def")
319         this.write("""\
320 \\def\\theopus{\\lilypondopus}% ugh
321 \\def\\thepiece{\\lilypondpiece}%
322 \\makelilypiecetitle
323 """)
324
325
326     #
327     # end
328     #
329     def end(this):
330         """
331         Close output file and run latex on it.
332
333         input:  None
334         output: None
335         error:  ExitBadLatex Exception
336         """
337
338         outfile=this.__base + '.dvi'
339         if Props.get('output') != '':
340             if not os.path.exists(Props.get('output')):
341                     os.mkdir(Props.get('output'))
342
343             outfile = os.path.join(Props.get('output'), outfile )
344             
345         this.write(r"""
346 %% \vfill\hfill{\lilypondtagline}
347 \makeatletter
348 \renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\lilypondtagline}}%%
349 \makeatother
350 \end{document}
351 """)
352         this.__fd.close()
353         if os.path.isfile(outfile):
354             os.remove(outfile)
355         if ( os.name == 'posix' ):
356             stat = os.system('latex \'\\nonstopmode \\input %s\'' %
357                              (this.__outfile))
358         else: # Windows shells don't eat the single quotes
359             stat = os.system('latex \\nonstopmode \\input %s' %
360                              (this.__outfile))
361         if stat:
362             sys.exit('ExitBadLatex')
363         if not os.path.isfile(outfile):
364                 os.rename(this.__tmpbase + '.dvi', outfile)
365                 
366         sys.stderr.write('\n' + program_id() + ': dvi file name is %s\n\n'
367                          % (outfile))
368
369         if Props.get('postscript'):
370             dvipsopts=''
371             if Props.get('orientation') == 'landscape':
372                 dvipsopts=dvipsopts + ' -t landscape'
373             psoutfile=this.__base + '.ps'
374             if Props.get('output') != '':
375                 psoutfile = os.path.join(Props.get('output'), psoutfile )
376             stat = os.system('dvips %s -o %s %s' % (dvipsopts,psoutfile,outfile))
377             if stat:
378                 sys.exit('ExitBadPostscript')
379             
380
381 \f
382
383
384 # ARG! THIS CODE IS BLOATED:
385 # FIXME: Junk all set/get methods.
386
387 class Properties:
388     """
389     This class handles all ly2dvi.py property manipulation
390
391     Public methods:
392     
393     __init__()  Constructor
394     set<property> methods
395     """
396
397     def __init__(this):
398
399         #
400         # Following is the order of priority for property assignment.  The
401         # list is organized from lowest to highest priority.  Each
402         # assignment is overridden by the next requester in the list.
403         #
404         # Requester     Description
405         # ---------     -----------
406         # init          Initial default values
407         # file          The values found in the LilyPond generated TeX files
408         # environment   Envrionment variables LILYINCLUDE, LILYPONDPREFIX
409         # rcfile        $LILYPONDPREFIX/.lilyrc
410         # rcfile        $HOME/.lilyrc
411         # rcfile        ./.lilyrc
412         # commandline   command line arguments
413         # 
414         this.__overrideTable = {
415             'init'        : 0,
416             'file'        : 1,
417             'environment' : 2,
418             'rcfile'      : 3,
419             'commandline' : 4,
420             'program'     : 5
421             }
422
423         this.__roverrideTable = {} # reverse lookup used for debug
424         for i in this.__overrideTable.items():
425             this.__roverrideTable[i[1]]=i[0]
426         
427         this.__data = {
428             'papersize'    :  ['a4paper', this.__overrideTable['init']],
429             'textheight'   :  [0, this.__overrideTable['init']],
430             'linewidth'    :  [500, this.__overrideTable['init']],
431             'orientation'  :  ['portrait', this.__overrideTable['init']],
432             'language'     :  ['%', this.__overrideTable['init']],
433             'include'      :  [[], this.__overrideTable['init']],
434             'debug'        :  [0, this.__overrideTable['init']],
435             'keeplilypond' :  [0, this.__overrideTable['init']],
436             'keeply2dvi'   :  [0, this.__overrideTable['init']],
437             'pagenumber'   :  ['%', this.__overrideTable['init']],
438             'separate'     :  [0, this.__overrideTable['init']],
439             'output'       :  ['', this.__overrideTable['init']],
440             'header'       :  ['%', this.__overrideTable['init']],
441             'dependencies' :  [0, this.__overrideTable['init']],
442             'root'         :  ['', this.__overrideTable['init']],
443             'tmp'          :  ['d:\tmp', this.__overrideTable['init']],
444             'filename'     :  ['', this.__overrideTable['init']],
445             'titledefs'    :  [[], this.__overrideTable['init']],
446             'titles'       :  [{}, this.__overrideTable['init']],
447             'lilyOutputFiles' :  [[], this.__overrideTable['init']],
448             'postscript'   :  [0, this.__overrideTable['init']],
449             }
450
451         #
452         # Try to set root and HOME first before calling rcfile
453         #
454         if os.environ.has_key('LILYPONDPREFIX'):
455             this.setRoot(os.environ['LILYPONDPREFIX'], 'environment')
456         else:
457             p=os.path.split(sys.argv[0])
458             p=os.path.split(p[0])
459             # bit silly. for ly2dvi, overrules compiled-in datadir...
460             # how to do this better (without running lily, of course?
461             this.setRoot(os.path.join(p[0],'share','lilypond'), 'init')
462
463         if not os.environ.has_key('HOME'):
464             if os.environ.has_key('HOMEDRIVE') and \
465                  os.environ.has_key('HOMEPATH'):
466                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
467                                      os.environ['HOMEPATH']
468             else:
469                 os.environ['HOME'] = os.curdir
470
471         this.rcfile() # Read initialization file(s)
472
473         if os.environ.has_key('LILYINCLUDE'):
474             tmp=this.get('include')
475             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
476                 tmp.append(s)
477             this.__set('include', tmp, 'environment')    
478
479
480         t= os.pathsep
481         if os.environ.has_key ('TEXINPUTS'):
482                 t = os.environ['TEXINPUTS'] + os.pathsep
483         os.environ['TEXINPUTS'] = t + \
484         os.path.join(this.get('root'), 'tex' ) + \
485         os.pathsep + os.path.join(this.get('root'), 'ps' )
486
487         t=''
488         if os.environ.has_key ('MFINPUTS'):
489                t = os.environ['MFINPUTS'] 
490         os.environ['MFINPUTS'] = t + os.pathsep + \
491                                  os.path.join(this.get('root'), 'mf')
492
493         if os.environ.has_key('TMP'):
494             this.__set('tmp',os.environ['TMP'],'environment')
495
496
497     def read_titledefs (this):
498         fd=this.get_texfile_path ('titledefs.tex')
499         mudefs=[]    
500
501         for line in fd.readlines():
502             m=re.match('\\\\newcommand\*{\\\\lilypond([\w]+)}',line)
503             if m:
504                 mudefs.append(m.group(1))
505         fd.close
506         this.__set('titledefs', mudefs, 'init')
507
508     #
509     # __set
510     #
511     def __set(this,var,value,requester):
512         """
513         All of the set methods call this to set a property.  If the value
514         was last set by a requestor of lesser priority the new value is
515         assigned, else the old value has priority and is unchanged.
516         """
517
518         if this.__overrideTable[requester] < this.__data[var][1]:
519             return 0
520         else:
521             this.__data[var] = [value, this.__overrideTable[requester]]
522
523     #
524     # get
525     #
526     def get(this,var):
527         """
528         All of the get methods call this to get a property value.  List
529         variable types are return by value to facilitate an append operation.
530         """
531
532         if var == 'include' or var == 'lilyOutputFiles':
533             return this.__data[var][0][0:]  # return a copy not a ref
534         else:
535             return this.__data[var][0]
536
537     #
538     # get_texfile_path
539     #
540     def get_texfile_path (this, var):
541         """
542         locate and open titledefs.tex file
543         """
544
545         if os.name == 'nt':
546             path = os.path.join(this.get('root'), 'tex', var)
547         else:
548             path =''
549             cmd =('kpsewhich tex %s %s' % (var,errorlog))
550             pipe = os.popen (cmd, 'r')
551             path = pipe.readline ()[:-1] # chop off \n
552             return_status =  pipe.close()
553             if return_status and not path:
554                 path = os.path.join(this.get('root'), 'tex', var)
555         fd = open(path, 'r')
556         return fd
557
558
559     #
560     # Read rc file
561     #
562     def rcfile(this):
563         """
564         Read initialization file(s)
565         """
566         varTable = [
567             #   name              set method
568             #   ----              ----------
569             ( 'DEBUG',          this.setDebug ),
570             ( 'DEPENDENCIES',   this.setDependencies ),
571             ( 'KEEPLILYPOND',   this.setKeeplilypond ),
572             ( 'KEEPLY2DVI',     this.setKeeply2dvi ),
573             ( 'LANGUAGE',       this.setLanguage ),
574             ( 'LATEXHF',        this.setHeader ),
575             ( 'LILYINCLUDE',    this.setInclude ),
576             ( 'LILYPONDPREFIX', this.setRoot ),
577             ( 'NONUMBER',       this.setNonumber ),
578             ( 'ORIENTATION',    this.setOrientation ),
579             ( 'OUTPUTDIR',      this.setOutput ),
580             ( 'PAPERSIZE',      this.setPaperZize ),
581             ( 'PHEIGHT',        this.setTextHeight ),
582             ( 'POSTSCRIPT',     this.setPostscript ),
583             ( 'PWIDTH',         this.setLineWidth ),
584             ( 'SEPARATE',       this.setSeparate ),
585             ( 'TMP',            this.setTmp ),
586             ]
587
588         if ( os.name == 'posix' ):
589             dotFilename='.lilyrc'
590         else: # Windows apps like edit choke on .lilyrc
591             dotFilename='_lilyrc'
592
593         for d in [os.path.join(this.get('root'),'ly'), \
594                   os.environ['HOME'], os.curdir ]:
595             file=os.path.join(d,dotFilename)
596             try:
597                 fd = open( file, 'r' )
598             except:
599                 continue
600             
601             for line in fd.readlines():
602                 if re.match('#.*',line):
603                     continue
604                 m=re.search('([\w]+)=(.*)',line)
605                 if m:
606                     for var in varTable:
607                         if m.group(1) == var[0]:
608                             var[1](m.group(2),'rcfile')
609                             break
610             fd.close
611
612     #
613     # setPaperZize
614     #
615     def setPaperZize(this,size,requester):
616         """
617         Set paper size properties
618         """
619
620         paperTable = [
621             # regex          width    height      name
622             # -----          -----    ------      ----
623             ( 'a0.*',        2389,    3381,    'a0paper' ),
624             ( 'a1$|a1p.*',   1690,    2389,    'a1paper' ),
625             ( 'a2.*',        1194,    1690,    'a2paper' ),
626             ( 'a3.*',        845,     1194,    'a3paper' ),
627             ( 'a4.*',        597,     845,     'a4paper' ),
628             ( 'a5.*',        423,     597,     'a5paper' ),
629             ( 'a6.*',        298,     423,     'a6paper' ),
630             ( 'a7.*',        211,     298,     'a7paper' ),
631             ( 'a8.*',        305,     211,     'a8paper' ),
632             ( 'a9.*',        105,     305,     'a9paper' ),
633             ( 'a10.*',       74,      105,     'a10paper' ),
634             ( 'b0.*',        2847,    4023,    'b0paper' ),
635             ( 'b1.*',        2012,    2847,    'b1paper' ),
636             ( 'b2.*',        1423,    2012,    'b2paper' ),
637             ( 'b3.*',        1006,    1423,    'b3paper' ),
638             ( 'b4.*',        712,     1006,    'b4paper' ),
639             ( 'b5.*',        503,     712,     'b5paper' ),
640             ( 'archA$',      650,     867,     'archApaper' ),
641             ( 'archB$',      867,     1301,    'archBpaper' ),
642             ( 'archC$',      1301,    1734,    'archCpaper' ),
643             ( 'archD$',      1734,    2602,    'archDpaper' ),
644             ( 'archE$',      2602,    3469,    'archEpaper' ),
645             ( 'flsa$|flse$', 614,     940,     'flsapaper' ),
646             ( 'halfletter$', 397,     614,     'halfletterpaper' ),
647             ( 'ledger$',     1229,    795,     'ledgerpaper' ),
648             ( 'legal$',      614,     1012,    'legalpaper' ),
649             ( 'letter$',     614,     795,     'letterpaper' ),
650             ( 'note$',       542,     723,     'notepaper' )
651             ]
652
653         found=0
654         for paper in paperTable:
655             if re.match(paper[0],size):
656                 found=1
657                 this.__set('papersize',paper[3],requester)
658                 break
659
660         if not found:
661             sys.exit('ExitBadPaper',size)
662
663     #
664     # setTextHeight
665     #
666     def setTextHeight(this,size,requester):
667         """
668         Set textheight property
669         """
670
671         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
672         if m:
673             if m.group(2) == 'cm':
674                 this.__set('textheight',\
675                            float(m.group(1)) * 72.27/2.54, requester )
676             elif m.group(2) == 'mm':
677                 this.__set('textheight',\
678                            float(m.group(1)) * 72.27/25.4, requester )
679             elif m.group(2) == 'pt':
680                 this.__set('textheight', float(m.group(1)), requester )
681             elif m.group(2) == '':
682                 this.__set('textheight', float(m.group(1)), requester )
683             else:
684                 sys.exit('ExitBadHeight', m.group(2))
685         else:           
686             sys.exit('ExitBadHeight', size)
687
688     #
689     # setLineWidth
690     #
691     def setLineWidth(this,size,requester):
692         """
693         Set linewidth propery
694         """
695
696         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
697         if m:
698             if m.group(2) == 'cm':
699                 this.__set('linewidth', \
700                 float(m.group(1)) * 72.27/2.54, requester )
701             elif m.group(2) == 'mm':
702                 this.__set('linewidth', \
703                 float(m.group(1)) * 72.27/25.4, requester )
704             elif m.group(2) == 'pt':
705                 this.__set('linewidth', float(m.group(1)), requester )
706             elif m.group(2) == '':
707                 this.__set('linewidth', float(m.group(1)), requester )
708             else:
709                 sys.exit('ExitBadWidth', m.group(2))
710         else:           
711             sys.stderr.write ('ly2dvi: warning: ignoring linewidth: ' + size + '\n')
712
713     #
714     # setOrientation
715     #
716     def setOrientation(this,orient,requester):
717         """
718         Set orientation property
719         """
720
721         if orient == 'landscape' or orient == 'portrait':
722             this.__set('orientation', orient, requester )
723         else:
724             sys.exit('ExitBadOrient', orient)
725
726     #
727     # setLanguage
728     #
729     def setLanguage(this,lang,requester):
730         """
731         Set language property
732         """
733
734         this.__set('language', '\\usepackage[%s]{babel}' % (lang), requester )
735
736     #
737     # setInclude
738     #
739     def setInclude(this,inc, requester):
740         """
741         Append an include path
742         """
743
744         tmp = this.get('include')
745         tmp.append(inc)
746         this.__set('include', tmp, requester )
747
748     #
749     # setDebug
750     #
751     def setDebug(this,value,requester):
752         """
753         Set or Clear debug flag
754         """
755
756         if int(value) == 1:
757             this.__set('debug',1,requester)
758         else:
759             this.__set('debug',0,requester)
760
761     #
762     # setKeeplilypond
763     #
764     def setKeeplilypond(this, value, requester):        
765         """
766         Set or Clear keeplilypond flag
767         """
768
769         if int(value) == 1:
770             this.__set('keeplilypond',1,requester)
771         else:
772             this.__set('keeplilypond',0,requester)
773
774     #
775     # setKeeply2dvi
776     #
777     def setKeeply2dvi(this, value, requester):  
778         """
779         Set or Clear keeply2dvi flag
780         """
781
782         if int(value) == 1:
783             this.__set('keeply2dvi',1,requester)
784         else:
785             this.__set('keeply2dvi',0,requester)
786
787     #
788     # setNonumber 
789     #
790     def setNonumber(this, value, requester):    
791         """
792         Set nonumber flag
793         """
794
795         if int(value) == 1:
796             this.__set('pagenumber','\\pagestyle{empty}',requester)
797         else:
798             this.__set('pagenumber','%',requester)
799
800     #
801     # setSeparate
802     #
803     def setSeparate(this, value, requester):    
804         """
805         Set or Clear separate flag
806         """
807
808         if int(value) == 1:
809             this.__set('separate',1,requester)
810         else:
811             this.__set('separate',0,requester)
812
813     #
814     # Set output directory name
815     #
816     def setOutput(this,out,requester):
817         this.__set('output',out,requester)
818
819     #
820     # Set latex header name
821     #
822     def setHeader(this,head, requester):
823         this.__set('header','\\input{' + head + '}',requester)
824
825     #
826     # Set or Clear Dependencies flag to generate makefile dependencies
827     #
828     def setDependencies(this, value, requester):        
829         """
830         Set or Clear dependencies flag
831         """
832
833         if int(value) == 1:
834             this.__set('dependencies',1,requester)
835         else:
836             this.__set('dependencies',0,requester)
837
838     #
839     # Set tmp directory
840     #
841     def setTmp(this,dir, requester):    
842         this.__set('tmp',dir,requester)
843
844     #
845     # Set lilypond source file name
846     #
847     def setFilename(this,file, requester):      
848         this.__set('filename',file,requester)
849
850     #
851     # Set title commands
852     #
853     def setTitles(this,titles, requester):      
854         this.__set('titles',titles,requester)
855
856     #
857     # Set title commands
858     #
859     def addLilyOutputFiles(this,filelist,requester):
860         """
861         Add a to the lily output list
862         """
863
864         tmp = this.get('lilyOutputFiles')
865         tmp = tmp + filelist
866         this.__set('lilyOutputFiles',tmp,requester)
867
868     #
869     # Set/Clear postscript flag
870     #
871     def setPostscript(this,value,requester):
872         """
873         Set postscript flag
874         """
875
876         if int(value) == 1:
877             this.__set('postscript',1,requester)
878         else:
879             this.__set('postscript',0,requester)
880
881     #
882     # Set root
883     #
884     def setRoot(this,path, requester):  
885         """
886         Set LilyPond root directory
887         """
888
889         os.environ['LILYPONDPREFIX'] = path
890         if os.name == 'nt' or os.name == 'dos':
891             path = unc2dos(path);
892
893         this.__set('root',path,requester)
894         
895
896     #
897     # printProps
898     #
899     def printProps(this):
900         """
901         Print properties
902         """
903         
904         for key in this.__data.keys():
905             print "%s <%s>:<%s>" % (key,this.get(key),
906                                     this.__roverrideTable[this.__data[key][1]])
907
908 \f
909
910 #
911 # Misc functions
912 #
913
914 def getLilyopts():
915     inc = ''    
916     if len(Props.get('include')) > 0: 
917         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
918     else:
919
920         if Props.get('dependencies'):
921             dep=' -M'
922         else:
923             dep=''
924         return inc + dep
925     return inc
926
927 def writeLilylog(file,contents):
928     if Props.get('keeplilypond'):
929         base, ext = os.path.splitext(file)
930         tempfile.template=base + "_li"
931         file=tempfile.mktemp('.log')
932         output = Props.get('output')
933         if output != '':
934             file = os.path.join( output, file )
935         try:
936             fd = open( file, 'w' )
937         except:
938             sys.exit('ExitNoWrite', file)
939         fd.write(contents)
940         fd.close()
941
942 def getTeXFile(contents):
943     texfiles=[]
944     for line in string.split(contents,'\n'):
945         m = re.search('paper output to (.+)\.\.\.', line)
946         if m:
947             texfiles.append(m.group(1))
948
949     if texfiles == []:
950         sys.exit('ExitNoTeXName')
951     else:
952         return texfiles
953
954 def unc2dos(path):
955     """
956     Convert a path of format //<drive>/this/that/the/other to
957     <drive>:\this\that\the\other
958     """
959     m=re.match('^//([A-Za-z])(/.*)$',path)
960     if m:
961         return m.group(1) + ':' + os.path.normpath(m.group(2))
962     
963     
964
965 def program_id ():
966     return 'ly2dvi (GNU LilyPond) ' + version;
967
968
969 def mailaddress():
970     try:
971         return os.environ['MAILADDRESS']
972     except KeyError:
973         return '(address unknown)'
974
975
976 def identify ():
977     sys.stderr.write (program_id () + '\n')
978
979 def print_version ():
980     sys.stdout.write (program_id () + '\n')
981
982 def help ():
983     sys.stdout.write (
984 """Usage: %s [OPTION]... [FILE]...
985
986 Generate dvi file from LilyPond source/output
987
988 Options:
989   -D,--debug           increase verbosity
990   -F,--headers=        name of additional LaTeX headers file
991   -H,--Height=         set paper height (points) (see manual page)
992   -I,--include=DIR     add DIR to LilyPond\'s search path
993   -K,--keeplilypond    keep LilyPond output files
994   -L,--landscape       set landscape orientation
995   -N,--nonumber        switch off page numbering
996   -O,--orientation=    set orientation (obsolete -- use -L instead)
997   -P,--postscript      generate PostScript file
998   -W,--Width=          set paper width (points) (see manual page)
999   -M,--dependencies    tell LilyPond to make a dependencies file
1000   -h,--help            this help text
1001   -k,--keeply2dvi      keep ly2dvi output files
1002   -l,--language=       give LaTeX language (babel)
1003   -o,--output=         set output directory
1004   -p,--papersize=      give LaTeX papersize (eg. a4)
1005   -s,--separate        run all files separately through LaTeX
1006
1007 files may be (a mix of) input to or output from LilyPond(1)
1008 """ % name)
1009
1010 \f
1011
1012 #
1013 # main
1014 #
1015
1016 def main():
1017     """Generate dvi files from LilyPond source/output"""
1018
1019     infile = Input()
1020     outfile = TeXOutput()
1021     texInputFiles=[]
1022     tempfile.tempdir=""
1023
1024     (options, files) = getopt.getopt (sys.argv[1:],
1025                                       'DF:H:I:KLNPW:Mhkl:o:p:s',
1026                                       ['debug', 'headers=', 'Height=',
1027                                        'include=', 'keeplilypond', 'landscape',
1028                                        'nonumber', 'Width=', 'dependencies',
1029                                        'help', 'keeply2dvi', 'language=',
1030                                        'output=', 'version', 'papersize=', 'separate',
1031                                        'postscript'])
1032
1033     for opt in options:
1034         o = opt[0]
1035         a = opt[1]
1036         if o == '--debug' or o == '-D':
1037             Props.setDebug(1,'commandline')
1038         elif o == '--headers' or o == '-F':
1039             Props.setHeader(a,'commandline')
1040         elif o == '--include' or o == '-I':
1041             Props.setInclude(a,'commandline')
1042         elif o == '--Height' or o == '-H':
1043             Props.setTextHeight(a,'commandline')
1044         elif o == '--keeplilypond' or o == '-K':
1045             Props.setKeeplilypond(1,'commandline')
1046         elif o == '--landscape' or o == '-L':
1047             Props.setOrientation('landscape','commandline')
1048         elif o == '--nonumber' or o == '-N':
1049             Props.setNonumber(1,'commandline')
1050         elif o == '--Width' or o == '-W':
1051             Props.setLineWidth(a,'commandline')
1052         elif o == '--dependencies' or o == '-M':
1053             Props.setDependencies(1,'commandline')
1054         elif o == '--help' or o == '-h':
1055             help()
1056             sys.exit (0)
1057         elif o == '--keeply2dvi' or o == '-k':
1058             Props.setKeeply2dvi(1,'commandline')
1059         elif o == '--language' or o == '-l':
1060             Props.setLanguage(a,'commandline')
1061         elif o == '--output' or o == '-o':
1062             Props.setOutput(a,'commandline')
1063         elif o == '--papersize' or o == '-p':
1064             Props.setPaperZize(a,'commandline')
1065         elif o == '--separate' or o == '-s':
1066             Props.setSeparate(1,'commandline')
1067         elif o == '--postscript' or o == '-P':
1068             Props.setPostscript(1,'commandline')
1069         elif o == '--version':
1070             print_version ()
1071             return 0
1072         else:
1073             print o
1074             raise getopt.error
1075             
1076     identify()
1077     Props.read_titledefs ()
1078     
1079     if len(files):
1080         for file in files:
1081             infile.open(file)
1082             type = infile.type()
1083             infile.close()
1084             if type == 'source':
1085                 if os.environ.has_key('OS') and \
1086                    os.environ['OS'] == 'Windows_95':
1087                     cmd = 'ash -c "lilypond %s %s 2>&1"' %(getLilyopts(), file)
1088                 else:
1089                     cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1090                 sys.stderr.write ('executing: %s\n'% cmd)
1091                 
1092                 fd = os.popen(cmd , 'r')
1093                 log = ''
1094                 
1095                 s = fd.readline()
1096                 while len(s) > 0:
1097                         sys.stderr.write (s)
1098                         sys.stderr.flush ()
1099                         log = log + s
1100                         s = fd.readline ()
1101                 if 0:
1102                         s = fd.read (1)
1103                         while len(s) > 0:
1104                                 sys.stderr.write (s)
1105                                 sys.stderr.flush ()
1106                                 s = fd.read (1)                 
1107                         log = log + s
1108                 stat = fd.close()
1109                 if stat:
1110                     sys.exit('ExitBadLily', cmd )
1111                 texFiles=getTeXFile(log)
1112                 writeLilylog(file,log)
1113                 Props.addLilyOutputFiles(texFiles,'program')
1114                 texInputFiles = texInputFiles + texFiles
1115             else:
1116                 texInputFiles.append(file)
1117
1118         firstfile=1
1119         for file in texInputFiles:
1120             infile.open(file)
1121             infile.setVars() # first pass set variables
1122             infile.close()
1123             if Props.get('debug'):
1124                 Props.printProps()
1125             if firstfile:
1126                 outfile.start(file)  # allow for specified name
1127             else:
1128                 outfile.next()
1129             outfile.write("""\
1130 \\input{%s}
1131 """ % (file))
1132             if Props.get('separate'):
1133                 outfile.end()
1134             else:
1135                 firstfile=0
1136         if not Props.get('separate'):
1137             outfile.end()
1138     else:
1139         help()
1140         sys.exit('ExitBadArgs','No files specified')
1141
1142 #
1143 # Exit values
1144 #
1145 ExitTable = {
1146     'ExitInterupt'         : ['Ouch!', 1 ],
1147     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1148     'ExitNotFound'         : ['File not found', 3 ],
1149     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1150     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1151     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1152     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1153     'ExitNoWrite'          : ['Permission denied', 8 ],
1154     'ExitNoTeXName'        : ['Hmm, I could not find an output file name', 9 ],
1155     'ExitBadLily'          : ['LilyPond failed', 10 ],
1156     'ExitBadLatex'         : ['Latex failed', 11 ],
1157     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1158     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1159     }
1160
1161 def cleanup():
1162     lilyfiles = []
1163     tmpfiles = []
1164     if not Props.get('keeplilypond'):
1165         lilyfiles = Props.get('lilyOutputFiles')
1166     if not Props.get('keeply2dvi'):
1167         tmpfiles = glob.glob('*_ly[0-9]*.*')
1168     for file in lilyfiles + tmpfiles:
1169         if os.path.isfile(file):
1170             os.remove(file)
1171
1172
1173 Props = Properties()
1174
1175 try:
1176     main()
1177
1178 except KeyboardInterrupt:
1179     print ExitTable['ExitInterupt'][0]
1180     cleanup()
1181     sys.exit(ExitTable['ExitInterupt'][1])
1182
1183 except SystemExit, errno:
1184     if ExitTable.has_key(errno.args[0]):
1185         msg = ExitTable[errno.args[0]]
1186     else:
1187         msg = ExitTable['ExitUnknown']
1188     if len(errno.args) > 1:  
1189         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1190     else:
1191         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1192     if Props.get('debug'):
1193         Props.printProps()
1194     cleanup()
1195     sys.exit(msg[1])
1196 else:
1197     cleanup()