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