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