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