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