]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
25da9f8fce53a942c5982d455ceb079546208335
[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.12'
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','\\pagestyle{empty}',requester)
764         else:
765             this.__set('pagenumber','%',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         if os.name == 'nt' or os.name == 'dos':
858             path = unc2dos(path);
859
860         this.__set('root',path,requester)
861         
862
863     #
864     # printProps
865     #
866     def printProps(this):
867         """
868         Print properties
869         """
870         
871         for key in this.__data.keys():
872             print "%s <%s>:<%s>" % (key,this.get(key),
873                                     this.__roverrideTable[this.__data[key][1]])
874
875 \f
876
877 #
878 # Misc functions
879 #
880
881 def getLilyopts():
882     inc = ''    
883     if len(Props.get('include')) > 0: 
884         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
885     else:
886
887         if Props.get('dependencies'):
888             dep=' -d'
889         else:
890             dep=''
891         return inc + dep
892     return inc
893
894 def writeLilylog(contents):
895     if Props.get('keeplilypond'):
896         file='lilylog.' + str(os.getpid())
897         output = Props.get('output')
898         if output != '':
899             file = os.path.join( output, file )
900         try:
901             fd = open( file, 'w' )
902         except:
903             sys.exit('ExitNoWrite', file)
904         fd.write(contents)
905         fd.close()
906
907 def getTeXFile(contents):
908     texfiles=[]
909     for line in string.split(contents,'\n'):
910         m = re.search('^Paper output to (.+)\.\.\.', line)
911         if m:
912             texfiles.append(m.group(1))
913
914     if texfiles == []:
915         sys.exit('ExitNoTeXName')
916     else:
917         return texfiles
918
919 def unc2dos(path):
920     """
921     Convert a path of format //<drive>/this/that/the/other to
922     <drive>:\this\that\the\other
923     """
924     m=re.match('^//([A-Za-z])(/.*)$',path)
925     if m:
926         return m.group(1) + ':' + os.path.normpath(m.group(2))
927     
928     
929
930 def program_id ():
931     return name + ' ' + version;
932
933
934 def mailaddress():
935     try:
936         return os.environ['MAILADDRESS']
937     except KeyError:
938         return '(address unknown)'
939
940
941 def identify ():
942     sys.stderr.write (program_id () + '\n')
943
944 def help ():
945     sys.stderr.write (
946         'Generate dvi file from mudela or lilypond output\n'
947         'Usage: ' + name + ' [OPTION]... [FILE]...\n'
948         '\n'
949         'Options:\n'
950         '  -D,--debug           increase verbosity\n'
951         '  -F,--headers=        name of additional LaTeX headers file\n'
952         '  -H,--Height=         set paper height (points) (see manual page)\n'
953         '  -I,--include=DIR     add DIR to LilyPond\'s search path\n'
954         '  -K,--keeplilypond    keep lilypond output files\n'
955         '  -L,--landscape       set landscape orientation\n'
956         '  -N,--nonumber        switch off page numbering\n'
957         '  -O,--orientation=    set orientation (obsolete - use -L instead)\n'
958         '  -P,--postscript      generate postscript file\n'
959         '  -W,--Width=          set paper width (points) (see manual page)\n'
960         '  -d,--dependencies    tell lilypond make a dependencies file\n'
961         '  -h,--help            this help text\n'
962         '  -k,--keeply2dvi      keep ly2dvi output files\n'
963         '  -l,--language=       give LaTeX language (babel)\n'
964         '  -o,--output=         set output directory\n'
965         '  -p,--papersize=      give LaTeX papersize (eg. a4)\n'
966         '  -s,--separate        run all files separately through LaTeX\n'
967         '\n'
968         'files may be (a mix of) input to or output from lilypond(1)\n'
969         )
970
971 \f
972
973 #
974 # main
975 #
976
977 def main():
978     """Generate dvi files from lilypond source/output"""
979
980     infile = Input()
981     outfile = TeXOutput()
982     texInputFiles=[]
983
984     (options, files) = getopt.getopt (sys.argv[1:],
985                                       'DF:H:I:KLNPW:dhkl:o:p:s',
986                                       ['debug', 'headers=', 'Height=',
987                                        'include=', 'keeplilypond', 'landscape',
988                                        'nonumber', 'Width=', 'dependencies',
989                                        'help', 'keeply2dvi', 'language=',
990                                        'output=', 'papersize=', 'separate',
991                                        'postscript'])
992     for opt in options:
993         o = opt[0]
994         a = opt[1]
995         if o == '--debug' or o == '-D':
996             Props.setDebug(1,'commandline')
997         elif o == '--headers' or o == '-F':
998             Props.setHeader(a,'commandline')
999         elif o == '--include' or o == '-I':
1000             Props.setInclude(a,'commandline')
1001         elif o == '--Height' or o == '-H':
1002             Props.setTextHeight(a,'commandline')
1003         elif o == '--keeplilypond' or o == '-K':
1004             Props.setKeeplilypond(1,'commandline')
1005         elif o == '--landscape' or o == '-L':
1006             Props.setOrientation('landscape','commandline')
1007         elif o == '--nonumber' or o == '-N':
1008             Props.setNonumber(1,'commandline')
1009         elif o == '--Width' or o == '-W':
1010             Props.setLineWidth(a,'commandline')
1011         elif o == '--dependencies' or o == '-d':
1012             Props.setDependencies(1,'commandline')
1013         elif o == '--help' or o == '-h':
1014             help()
1015             return 0
1016         elif o == '--keeply2dvi' or o == '-k':
1017             Props.setKeeply2dvi(1,'commandline')
1018         elif o == '--language' or o == '-l':
1019             Props.setLanguage(a,'commandline')
1020         elif o == '--output' or o == '-o':
1021             Props.setOutput(a,'commandline')
1022         elif o == '--papersize' or o == '-p':
1023             Props.setPaperZize(a,'commandline')
1024         elif o == '--separate' or o == '-s':
1025             Props.setSeparate(1,'commandline')
1026         elif o == '--postscript' or o == '-P':
1027             Props.setPostscript(1,'commandline')
1028
1029     if len(files):
1030         for file in files:
1031             infile.open(file)
1032             type = infile.type()
1033             infile.close()
1034             if type == 'source':
1035                 if os.environ.has_key('OS') and \
1036                    os.environ['OS'] == 'Windows_95':
1037                     cmd = 'ash -c "lilypond %s %s 2>&1"' %(getLilyopts(), file)
1038                 else:
1039                     cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1040                 sys.stderr.write ('executing: %s\n'% cmd)
1041                 
1042                 fd = os.popen(cmd , 'r')
1043                 log = ''
1044                 
1045                 s = fd.readline()
1046                 while len(s) > 0:
1047                         sys.stderr.write (s)
1048                         sys.stderr.flush ()
1049                         log = log + s
1050                         s = fd.readline ()
1051                 if 0:
1052                         s = fd.read (1)
1053                         while len(s) > 0:
1054                                 sys.stderr.write (s)
1055                                 sys.stderr.flush ()
1056                                 s = fd.read (1)                 
1057                         log = log + s
1058                 stat = fd.close()
1059                 if stat:
1060                     sys.exit('ExitBadLily', cmd )
1061                 texFiles=getTeXFile(log)
1062                 writeLilylog(log)
1063                 Props.addLilyOutputFiles(texFiles,'program')
1064                 texInputFiles = texInputFiles + texFiles
1065             else:
1066                 texInputFiles.append(file)
1067
1068         firstfile=1
1069         for file in texInputFiles:
1070             infile.open(file)
1071             infile.setVars() # first pass set variables
1072             infile.close()
1073             if Props.get('debug'):
1074                 Props.printProps()
1075             if firstfile:
1076                 outfile.start(file)
1077             else:
1078                 outfile.next()
1079             outfile.write("""\
1080 \\input{%s}
1081 """ % (file))
1082             if Props.get('separate'):
1083                 outfile.end()
1084             else:
1085                 firstfile=0
1086         if not Props.get('separate'):
1087             outfile.end()
1088     else:
1089         help()
1090         sys.exit('ExitBadArgs','No files specified')
1091
1092 #
1093 # Exit values
1094 #
1095 ExitTable = {
1096     'ExitInterupt'         : ['Ouch!', 1 ],
1097     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1098     'ExitNotFound'         : ['File not found', 3 ],
1099     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1100     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1101     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1102     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1103     'ExitNoWrite'          : ['Permission denied', 8 ],
1104     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1105     'ExitBadLily'          : ['Lilypond failed', 10 ],
1106     'ExitBadLatex'         : ['Latex failed', 11 ],
1107     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1108     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1109     }
1110
1111 def cleanup():
1112     lilyfiles = []
1113     tmpfiles = []
1114     if not Props.get('keeplilypond'):
1115         lilyfiles = Props.get('lilyOutputFiles')
1116     if not Props.get('keeply2dvi'):
1117         tmpfiles = glob.glob('*.' + str(os.getpid()) + '.*' )
1118     for file in lilyfiles + tmpfiles:
1119         if os.path.isfile(file):
1120             os.remove(file)
1121
1122
1123 identify()
1124 Props = Properties()
1125
1126 try:
1127     main()
1128
1129 except KeyboardInterrupt:
1130     print ExitTable['ExitInterupt'][0]
1131     cleanup()
1132     sys.exit(ExitTable['ExitInterupt'][1])
1133
1134 except SystemExit, errno:
1135     if ExitTable.has_key(errno.args[0]):
1136         msg = ExitTable[errno.args[0]]
1137     else:
1138         msg = ExitTable['ExitUnknown']
1139     if len(errno.args) > 1:  
1140         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1141     else:
1142         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1143     if Props.get('debug'):
1144         Props.printProps()
1145     cleanup()
1146     sys.exit(msg[1])
1147 else:
1148     cleanup()