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