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