]> git.donarmstrong.com Git - lilypond.git/blob - scripts/convert-ly.py
2596951399e808cb135864a93c6051601e79e168
[lilypond.git] / scripts / convert-ly.py
1 #!@PYTHON@
2
3 # convert-lilypond.py -- convertor for lilypond versions
4
5 # source file of the GNU LilyPond music typesetter
6
7 # (c) 1998 
8
9 # TODO
10 #   use -f and -t for -s output
11
12 # NEWS
13 # 0.2
14 #  - rewrite in python
15
16 program_name = 'convert-ly'
17 version = '@TOPLEVEL_VERSION@'
18
19 import os
20 import sys
21 import __main__
22 import getopt
23 import  string
24 import re
25 import time
26
27 lilypond_version_re_str = '\\\\version *\"(.*)\"'
28 lilypond_version_re = re.compile(lilypond_version_re_str)
29
30 def program_id ():
31         return '%s (GNU LilyPond) %s' %(program_name,  version);
32
33 def identify ():
34         sys.stderr.write (program_id () + '\n')
35
36 def usage ():
37         sys.stdout.write (
38                 r"""Usage: %s [OPTION]... [FILE]... 
39 Try to convert to newer lilypond-versions.  The version number of the
40 input is guessed by default from \version directive
41
42 Options:
43   -h, --help             print this help
44   -e, --edit             in place edit
45   -f, --from=VERSION     start from version
46   -s, --show-rules       print all rules.
47   -t, --to=VERSION       target version
48       --version          print program version
49
50 Report bugs to bugs-gnu-music@gnu.org
51
52 """ % program_name)
53         
54         
55         sys.exit (0)
56
57 def print_version ():
58         
59         sys.stdout.write (r"""%s
60
61 This is free software.  It is covered by the GNU General Public
62 License, and you are welcome to change it and/or distribute copies of
63 it under certain conditions.  invoke as `%s --warranty' for more
64 information.
65
66 """ % (program_id() , program_name))
67         
68 def gulp_file(f):
69         try:
70                 i = open(f)
71                 i.seek (0, 2)
72                 n = i.tell ()
73                 i.seek (0,0)
74         except:
75                 print 'can\'t open file: ' + f + '\n'
76                 return ''
77         s = i.read (n)
78         if len (s) <= 0:
79                 print 'gulped empty file: ' + f + '\n'
80         i.close ()
81         return s
82
83 def str_to_tuple (s):
84         return tuple (map (string.atoi, string.split (s,'.')))
85
86 def tup_to_str (t):
87         return string.join (map (lambda x: '%s' % x, list (t)), '.')
88
89 def version_cmp (t1, t2):
90         for x in [0,1,2]:
91                 if t1[x] - t2[x]:
92                         return t1[x] - t2[x]
93         return 0
94
95 def guess_lilypond_version(filename):
96         s = gulp_file (filename)
97         m = lilypond_version_re.search (s)
98         if m:
99                 return m.group(1)
100         else:
101                 return ''
102
103 class FatalConversionError:
104         pass
105
106 conversions = []
107
108 def show_rules (file):
109         for x in conversions:
110                 file.write  ('%s: %s\n' % (tup_to_str (x[0]), x[2]))
111
112 ############################
113                 
114 if 1:                                   # need new a namespace
115         def conv (str):
116                 if re.search ('\\\\octave', str):
117                         sys.stderr.write ('\nNot smart enough to convert \\octave')
118                         raise FatalConversionError()
119                 
120                 return str
121
122         conversions.append ((
123                 ((0,1,19), conv, 'deprecated \\octave; can\'t convert automatically')))
124
125
126 if 1:                                   # need new a namespace
127         def conv (str):
128                 str = re.sub ('\\\\textstyle([^;]+);',
129                                          '\\\\property Lyrics . textstyle = \\1', str)
130                 str = re.sub ('\\\\key([^;]+);', '\\\\accidentals \\1;', str)
131                         
132                 return str
133
134         conversions.append ((
135                 ((0,1,20), conv, 'deprecated \\textstyle, new \key syntax')))
136
137
138 if 1:
139         def conv (str):
140                 str = re.sub ('\\\\musical_pitch', '\\\\musicalpitch',str)
141                 str = re.sub ('\\\\meter', '\\\\time',str)
142                         
143                 return str
144
145         conversions.append ((
146                 ((0,1,21), conv, '\\musical_pitch -> \\musicalpitch, '+
147                  '\\meter -> \\time')))
148
149 if 1:
150         def conv (str):
151                 return str
152
153         conversions.append ((
154                 ((1,0,0), conv, '0.1.21 -> 1.0.0 ')))
155
156
157 if 1:
158         def conv (str):
159                 str = re.sub ('\\\\accidentals', '\\\\keysignature',str)
160                 str = re.sub ('specialaccidentals *= *1', 'keyoctaviation = 0',str)
161                 str = re.sub ('specialaccidentals *= *0', 'keyoctaviation = 1',str)
162                         
163                 return str
164
165         conversions.append ((
166                 ((1,0,1), conv, '\\accidentals -> \\keysignature, ' +
167                  'specialaccidentals -> keyoctaviation')))
168
169 if 1:
170         def conv(str):
171                 if re.search ('\\\\header', str):
172                         sys.stderr.write ('\nNot smart enough to convert to new \\header format')
173                 return str
174         
175         conversions.append (((1,0,2), conv, '\\header { key = concat + with + operator }'))
176
177 if 1:
178         def conv(str):
179                 str =  re.sub ('\\\\melodic', '\\\\notes',str)
180                         
181                 return str
182         
183         conversions.append (((1,0,3), conv, '\\melodic -> \\notes'))
184
185 if 1:
186         def conv(str):
187                 str =  re.sub ('default_paper *=', '',str)
188                 str =  re.sub ('default_midi *=', '',x)                 
189                         
190                 return str
191         
192         conversions.append (((1,0,4), conv, 'default_{paper,midi}'))
193
194 if 1:
195         def conv(str):
196                 str =  re.sub ('ChoireStaff', 'ChoirStaff',str)
197                 str =  re.sub ('\\output', 'output = ',str)
198                         
199                 return str
200         
201         conversions.append (((1,0,5), conv, 'ChoireStaff -> ChoirStaff'))
202
203 if 1:
204         def conv(str):
205                 if re.search ('[a-zA-Z]+ = *\\translator',str):
206                         sys.stderr.write ('\nNot smart enough to change \\translator syntax')
207                         raise FatalConversionError()
208                 return str
209         
210         conversions.append (((1,0,6), conv, 'foo = \\translator {\\type .. } ->\\translator {\\type ..; foo; }'))
211
212
213 if 1:
214         def conv(str):
215                 str =  re.sub ('\\\\lyric', '\\\\lyrics',str)
216                         
217                 return str
218         
219         conversions.append (((1,0,7), conv, '\\lyric -> \\lyrics'))
220
221 if 1:
222         def conv(str):
223                 str =  re.sub ('\\\\\\[/3+', '\\\\times 2/3 { ',str)
224                 str =  re.sub ('\\[/3+', '\\\\times 2/3 { [',str)
225                 str =  re.sub ('\\\\\\[([0-9/]+)', '\\\\times \\1 {',str)
226                 str =  re.sub ('\\[([0-9/]+)', '\\\\times \\1 { [',str)
227                 str =  re.sub ('\\\\\\]([0-9/]+)', '}', str)
228                 str =  re.sub ('\\\\\\]', '}',str)
229                 str =  re.sub ('\\]([0-9/]+)', '] }', str)
230                 return str
231         
232         conversions.append (((1,0,10), conv, '[2/3 ]1/1 -> \\times 2/3 '))
233
234 if 1:
235         def conv(str):
236                 return str
237         conversions.append (((1,0,12), conv, 'Chord syntax stuff'))
238
239
240 if 1:
241         def conv(str):
242                 
243                 
244                 str =  re.sub ('<([^>~]+)~([^>]*)>','<\\1 \\2> ~', str)
245                         
246                 return str
247         
248         conversions.append (((1,0,13), conv, '<a ~ b> c -> <a b> ~ c'))
249
250 if 1:
251         def conv(str):
252                 str =  re.sub ('<\\[','[<', str)
253                 str =  re.sub ('\\]>','>]', str)
254                         
255                 return str
256         
257         conversions.append (((1,0,14), conv, '<[a b> <a b]>c -> [<a b> <a b>]'))
258
259
260 if 1:
261         def conv(str):
262                 str =  re.sub ('\\\\type','\\\\context', str)
263                 str =  re.sub ('textstyle','textStyle', str)
264                         
265                 return str
266         
267         conversions.append (((1,0,16), conv, '\\type -> \\context, textstyle -> textStyle'))
268
269
270 if 1:
271         def conv(str):
272                 if re.search ('\\\\repeat',str):
273                         sys.stderr.write ('\nNot smart enough to convert \\repeat')
274                         raise FatalConversionError()
275                 return str
276         
277         conversions.append (((1,0,18), conv,
278                 '\\repeat NUM Music Alternative -> \\repeat FOLDSTR Music Alternative'))
279
280 if 1:
281         def conv(str):
282                 str =  re.sub ('SkipBars','skipBars', str)
283                 str =  re.sub ('fontsize','fontSize', str)
284                 str =  re.sub ('midi_instrument','midiInstrument', str)                 
285                         
286                 return str
287
288         conversions.append (((1,0,19), conv,
289                 'fontsize -> fontSize, midi_instrument -> midiInstrument, SkipBars -> skipBars'))
290
291
292 if 1:
293         def conv(str):
294                 str =  re.sub ('tieydirection','tieVerticalDirection', str)
295                 str =  re.sub ('slurydirection','slurVerticalDirection', str)
296                 str =  re.sub ('ydirection','verticalDirection', str)                   
297                         
298                 return str
299
300         conversions.append (((1,0,20), conv,
301                 '{,tie,slur}ydirection -> {v,tieV,slurV}erticalDirection'))
302
303
304 if 1:
305         def conv(str):
306                 str =  re.sub ('hshift','horizontalNoteShift', str)
307                         
308                 return str
309
310         conversions.append (((1,0,21), conv,
311                 'hshift -> horizontalNoteShift'))
312
313
314 if 1:
315         def conv(str):
316                 str =  re.sub ('\\\\grouping[^;]*;','', str)
317                         
318                 return str
319
320         conversions.append (((1,1,52), conv,
321                 'deprecate \\grouping'))
322
323
324 if 1:
325         def conv(str):
326                 str =  re.sub ('\\\\wheel','\\\\coda', str)
327                         
328                 return str
329
330         conversions.append (((1,1,55), conv,
331                 '\\wheel -> \\coda'))
332
333 if 1:
334         def conv(str):
335                 str =  re.sub ('keyoctaviation','keyOctaviation', str)
336                 str =  re.sub ('slurdash','slurDash', str)
337                         
338                 return str
339
340         conversions.append (((1,1,65), conv,
341                 'slurdash -> slurDash, keyoctaviation -> keyOctaviation'))
342
343 if 1:
344         def conv(str):
345                 str =  re.sub ('\\\\repeat *\"?semi\"?','\\\\repeat "volta"', str)
346                         
347                 return str
348
349         conversions.append (((1,1,66), conv,
350                 'semi -> volta'))
351
352
353 if 1:
354         def conv(str):
355                 str =  re.sub ('\"?beamAuto\"? *= *\"?0?\"?','noAutoBeaming = "1"', str)
356                         
357                 return str
358
359         conversions.append (((1,1,67), conv,
360                 'beamAuto -> noAutoBeaming'))
361
362 if 1:
363         def conv(str):
364                 str =  re.sub ('automaticMelismas', 'automaticMelismata', str)
365                         
366                 return str
367
368         conversions.append (((1,2,0), conv,
369                 'automaticMelismas -> automaticMelismata'))
370
371 if 1:
372         def conv(str):
373                 str =  re.sub ('dynamicDir\\b', 'dynamicDirection', str)
374                         
375                 return str
376
377         conversions.append (((1,2,1), conv,
378                 'dynamicDir -> dynamicDirection'))
379
380 if 1:
381         def conv(str):
382                 str =  re.sub ('\\\\cadenza *0 *;', '\\\\cadenzaOff', str)
383                 str =  re.sub ('\\\\cadenza *1 *;', '\\\\cadenzaOn', str)               
384                         
385                 return str
386
387         conversions.append (((1,3,4), conv,
388                 '\\cadenza -> \cadenza{On|Off}'))
389
390 if 1:
391         def conv (str):
392                 str = re.sub ('"?beamAuto([^"=]+)"? *= *"([0-9]+)/([0-9]+)" *;*',
393                               'beamAuto\\1 = #(make-moment \\2 \\3)',
394                               str)
395                 return str
396
397         conversions.append (((1,3,5), conv, 'beamAuto moment properties'))
398
399 if 1:
400         def conv (str):
401                 str = re.sub ('stemStyle',
402                               'flagStyle',
403                               str)
404                 return str
405
406         conversions.append (((1,3,17), conv, 'stemStyle -> flagStyle'))
407
408 if 1:
409         def conv (str):
410                 str = re.sub ('staffLineLeading',
411                               'staffSpace',
412                               str)
413                 return str
414
415         conversions.append (((1,3,18), conv, 'staffLineLeading -> staffSpace'))
416
417 if 1:
418         def conv (str):
419                 str = re.sub ('textEmptyDimension *= *##t',
420                               'textNonEmpty = ##f',
421                               str)
422                 str = re.sub ('textEmptyDimension *= *##f',
423                               'textNonEmpty = ##t',
424                               str)
425                 return str
426
427         conversions.append (((1,3,35), conv, 'textEmptyDimension -> textNonEmpty'))
428
429 if 1:
430         def conv (str):
431                 str = re.sub ("([a-z]+)[ \t]*=[ \t]*\\\\musicalpitch *{([- 0-9]+)} *\n",
432                               "(\\1 . (\\2))\n", str)
433                 str = re.sub ("\\\\musicalpitch *{([0-9 -]+)}",
434                               "\\\\musicalpitch #'(\\1)", str)
435                 if re.search ('\\\\notenames',str):
436                         sys.stderr.write ('\nNot smart enough to convert to new \\notenames format')
437                 return str
438
439         conversions.append (((1,3,38), conv, '\musicalpitch { a b c } -> #\'(a b c)'))
440
441 if 1:
442         def conv (str):
443                 def replace (match):
444                         return '\\key %s;' % string.lower (match.group (1))
445                 
446                 str = re.sub ("\\\\key ([^;]+);",  replace, str)
447                 return str
448         
449         conversions.append (((1,3,39), conv, '\\key A ;  ->\\key a;'))
450
451 if 1:
452         def conv (str):
453                 if re.search ('\\[:',str):
454                         sys.stderr.write ('\nNot smart enough to convert to new tremolo format')
455                 return str
456
457         conversions.append (((1,3,41), conv,
458                 '[:16 c4 d4 ] -> \\repeat "tremolo" 2 { c16 d16 }'))
459
460 if 1:
461         def conv (str):
462                 str = re.sub ('Staff_margin_engraver' , 'Instrument_name_engraver', str)
463                 return str
464
465         conversions.append (((1,3,42), conv,
466                 'Staff_margin_engraver deprecated, use Instrument_name_engraver'))
467
468 if 1:
469         def conv (str):
470                 str = re.sub ('note[hH]eadStyle\\s*=\\s*"?(\\w+)"?' , "noteHeadStyle = #'\\1", str)
471                 return str
472
473         conversions.append (((1,3,49), conv,
474                 'noteHeadStyle value: string -> symbol'))
475
476 if 1:
477         def conv (str):
478                 str = re.sub (r"""\\key *([a-z]+) *;""", r"""\\key \1 \major;""",str);
479                 return str
480         conversions.append (((1,3,59), conv,
481                 '\key X ; -> \key X major; ')) 
482
483 if 1:
484         def conv (str):
485                 str = re.sub (r'latexheaders *= *"\\\\input ',
486                               'latexheaders = "',
487                               str)
488                 return str
489         conversions.append (((1,3,68), conv, 'latexheaders = "\\input global" -> latexheaders = "global"'))
490
491
492
493 ################ TODO: lots of other syntax change should be done here as well
494
495
496
497 if 1:
498         def conv (str):
499                 str = re.sub ('basicCollisionProperties', 'NoteCollision', str)
500                 str = re.sub ('basicVoltaSpannerProperties' , "VoltaBracket", str)
501                 str = re.sub ('basicKeyProperties' , "KeySignature", str)
502
503                 str = re.sub ('basicClefItemProperties' ,"Clef", str)
504
505
506                 str = re.sub ('basicLocalKeyProperties' ,"Accidentals", str)
507                 str = re.sub ('basicMarkProperties' ,"Accidentals", str)                                
508                 str = re.sub ('basic([A-Za-z_]+)Properties', '\\1', str)
509
510                 return str
511         
512         conversions.append (((1,3,92), conv, 'basicXXXProperties -> XXX'))
513
514 if 1:
515         def conv (str):
516                 # Ugh, but meaning of \stemup changed too
517                 # maybe we should do \stemup -> \stemUp\slurUp\tieUp ?
518                 str = re.sub ('\\\\stemup', '\\\\stemUp', str)
519                 str = re.sub ('\\\\stemdown', '\\\\stemDown', str)
520                 str = re.sub ('\\\\stemboth', '\\\\stemBoth', str)
521                 
522                 str = re.sub ('\\\\slurup', '\\\\slurUp', str)
523                 str = re.sub ('\\\\slurboth', '\\\\slurBoth', str)
524                 str = re.sub ('\\\\slurdown', '\\\\slurDown', str)
525                 str = re.sub ('\\\\slurdotted', '\\\\slurDotted', str)
526                 str = re.sub ('\\\\slurnormal', '\\\\slurNoDots', str)          
527                 
528                 str = re.sub ('\\\\shiftoff', '\\\\shiftOff', str)
529                 str = re.sub ('\\\\shifton', '\\\\shiftOn', str)
530                 str = re.sub ('\\\\shiftonn', '\\\\shiftOnn', str)
531                 str = re.sub ('\\\\shiftonnn', '\\\\shiftOnnn', str)
532
533                 str = re.sub ('\\\\onevoice', '\\\\oneVoice', str)
534                 str = re.sub ('\\\\voiceone', '\\\\voiceOne', str)
535                 str = re.sub ('\\\\voicetwo', '\\\\voiceTwo', str)
536                 str = re.sub ('\\\\voicethree', '\\\\voiceThree', str)
537                 str = re.sub ('\\\\voicefour', '\\\\voiceFour', str)
538
539                 # I don't know exactly when these happened...
540                 str = re.sub ('\\\\property *[^ ]*verticalDirection[^=]*= *#?(1|(\\\\up))', '\\\\stemUp\\\\slurUp\\\\tieUp', str)
541                 str = re.sub ('\\\\property *[^ ]*verticalDirection[^=]*= *#?((-1)|(\\\\down))', '\\\\stemDown\\\\slurDown\\\\tieDown', str)
542                 str = re.sub ('\\\\property *[^ .]*[.]?([a-z]+)VerticalDirection[^=]*= *#?(1|(\\\\up))', '\\\\\\1Up', str)
543                 str = re.sub ('\\\\property *[^ .]*[.]?([a-z]+)VerticalDirection[^=]*= *#?((-1)|(\\\\down))', '\\\\\\1Down', str)
544
545                 return str
546         
547         conversions.append (((1,3,93), conv,
548                 'property definiton case (eg. onevoice -> oneVoice)'))
549
550
551 if 1:
552         def conv (str):
553                 str = re.sub ('ChordNames*', 'ChordNames', str)
554                 if re.search ('\\\\textscript "[^"]* *"[^"]*"', str):
555                         sys.stderr.write ('\nNot smart enough to convert to new \\textscript markup text')
556
557                 str = re.sub ('\\textscript +("[^"]*")', '\\textscript #\\1', str)
558
559                 return str
560         
561         conversions.append (((1,3,97), conv, 'ChordName -> ChordNames'))
562
563
564 ## TODO: add lots of these
565         
566 if 1:
567         def conv (str):
568                 str = re.sub ('\\\\property *"?Voice"? *[.] *"?textStyle"? *= *"([^"]*)"', '\\\\property Voice.TextScript \\\\set #\'font-style = #\'\\1', str)
569                 str = re.sub ('\\\\property *"?Lyrics"? *[.] *"?textStyle"? *= *"([^"]*)"', '\\\\property Lyrics.LyricText \\\\set #\'font-style = #\'\\1', str)
570
571                 str = re.sub ('\\\\property *"?([^.]+)"? *[.] *"?timeSignatureStyle"? *= *"([^"]*)"', '\\\\property \\1.TimeSignature \\\\override #\'style = #\'\\2', str) 
572
573                 str = re.sub ('"?timeSignatureStyle"? *= *#?""', 'TimeSignature \\\\override #\'style = ##f, str)
574                 
575                 str = re.sub ('"?timeSignatureStyle"? *= *#?"([^"]*)"', 'TimeSignature \\\\override #\'style = #\'\\1', str)
576                 
577                 str = re.sub ('#\'style *= #*"([^"])"', '#\'style = #\'\\1', str)
578                 
579                 str = re.sub ('\\\\property *"?([^.]+)"? *[.] *"?horizontalNoteShift"? *= *"?#?([0-9]+)"?', '\\\\property \\1.NoteColumn \\\\override #\'horizontal-shift = #\\2', str) 
580
581                 # ugh
582                 str = re.sub ('\\\\property *"?([^.]+)"? *[.] *"?flagStyle"? *= *""', '\\\\property \\1.Stem \\\\override #\'flag-style = ##f', str)
583                 
584                 str = re.sub ('\\\\property *"?([^.]+)"? *[.] *"?flagStyle"? *= *"([^"]*)"', '\\\\property \\1.Stem \\\\override #\'flag-style = #\'\\2', str) 
585                 return str
586         
587         conversions.append (((1,3,98), conv, 'CONTEXT.textStyle -> GROB.#font-style '))
588
589 if 1:
590         def conv (str):
591                 str = re.sub ('"?beamAutoEnd_([0-9]*)"? *= *(#\\([^)]*\\))', 'autoBeamSettings \\push #\'(end 1 \\1 * *) = \\2', str)
592                 str = re.sub ('"?beamAutoBegin_([0-9]*)"? *= *(#\\([^)]*\))', 'autoBeamSettings \\push #\'(begin 1 \\1 * *) = \\2', str)
593                 str = re.sub ('"?beamAutoEnd"? *= *(#\\([^)]*\\))', 'autoBeamSettings \\push #\'(end * * * *) = \\1', str)
594                 str = re.sub ('"?beamAutoBegin"? *= *(#\\([^)]*\\))', 'autoBeamSettings \\push #\'(begin * * * *) = \\1', str)
595
596
597                 return str
598         
599         conversions.append (((1,3,102), conv, 'beamAutoEnd -> autoBeamSettings \\push (end * * * *)'))
600
601
602 if 1:
603         def conv (str):
604                 str = re.sub ('\\\\push', '\\\\override', str)
605                 str = re.sub ('\\\\pop', '\\\\revert', str)
606
607                 return str
608         
609         conversions.append (((1,3,111), conv, '\\push -> \\override, \\pop -> \\revert'))
610
611 if 1:
612         def conv (str):
613                 str = re.sub ('LyricVoice', 'LyricsVoice', str)
614                 # old fix
615                 str = re.sub ('Chord[Nn]ames*.Chord[Nn]ames*', 'ChordNames.ChordName', str)
616                 return str
617         
618         conversions.append (((1,3,113), conv, 'LyricVoice -> LyricsVoice'))
619
620 def regularize_id (str):
621         s = ''
622         lastx = ''
623         for x in str:
624                 if x == '_':
625                         lastx = x
626                         continue
627                 elif x in string.digits:
628                         x = chr(ord (x) - ord ('0')  +ord ('A'))
629                 elif x not in string.letters:
630                         x = 'x'
631                 elif x in string.lowercase and lastx == '_':
632                         x = string.upper (x)
633                 s = s + x
634                 lastx = x
635         return s
636
637 if 1:
638         def conv (str):
639                 
640                 def regularize_dollar_reference (match):
641                         return regularize_id (match.group (1))
642                 def regularize_assignment (match):
643                         return '\n' + regularize_id (match.group (1)) + ' = '
644                 str = re.sub ('\$([^\t\n ]+)', regularize_dollar_reference, str)
645                 str = re.sub ('\n([^ \t\n]+) = ', regularize_assignment, str)
646                 return str
647         
648         conversions.append (((1,3,117), conv, 'identifier names: $!foo_bar_123 -> xfooBarABC'))
649
650
651 if 1:
652         def conv (str):
653                 def regularize_paper (match):
654                         return regularize_id (match.group (1))
655                 
656                 str = re.sub ('(paper_[a-z]+)', regularize_paper, str)
657                 str = re.sub ('sustainup', 'sustainUp', str)
658                 str = re.sub ('nobreak', 'noBreak', str)
659                 str = re.sub ('sustaindown', 'sustainDown', str)
660                 str = re.sub ('sostenutoup', 'sostenutoUp', str)
661                 str = re.sub ('sostenutodown', 'sostenutoDown', str)
662                 str = re.sub ('unachorda', 'unaChorda', str)
663                 str = re.sub ('trechorde', 'treChorde', str)
664         
665                 return str
666         
667         conversions.append (((1,3,120), conv, 'paper_xxx -> paperXxxx, pedalup -> pedalUp.'))
668
669 if 1:
670         def conv (str):
671                 str = re.sub ('drarnChords', 'chordChanges', str)
672                 str = re.sub ('\\musicalpitch', '\\pitch', str)
673                 return str
674         
675         conversions.append (((1,3,122), conv, 'drarnChords -> chordChanges, \\musicalpitch -> \\pitch'))
676
677
678 ############################
679         
680
681 def get_conversions (from_version, to_version):
682         def version_b (v, f = from_version, t = to_version):
683                 return version_cmp (v[0], f) > 0 and version_cmp (v[0], t) <= 0
684         return filter (version_b, conversions)
685
686
687 def latest_version ():
688         return conversions[-1][0]
689
690 def do_conversion (infile, from_version, outfile, to_version):
691         conv_list = get_conversions (from_version, to_version)
692
693         sys.stderr.write ('Applying conversions: ')
694         str = infile.read ()
695         last_conversion = ()
696         try:
697                 for x in conv_list:
698                         sys.stderr.write (tup_to_str (x[0])  + ', ')
699                         str = x[1] (str)
700                         last_conversion = x[0]
701
702         except FatalConversionError:
703                 sys.stderr.write ('Error while converting; I won\'t convert any further')
704
705         if last_conversion:
706                 sys.stderr.write ('\n')
707                 new_ver =  '\\version \"%s\"' % tup_to_str (last_conversion)
708                 # JUNKME?
709                 # ugh: this all really doesn't help
710                 # esp. as current conversion rules are soo incomplete
711                 if re.search (lilypond_version_re_str, str):
712                         str = re.sub (lilypond_version_re_str,'\\'+new_ver , str)
713                 #else:
714                 #       str = new_ver + '\n' + str
715
716                 outfile.write(str)
717
718         return last_conversion
719         
720 class UnknownVersion:
721         pass
722
723 def do_one_file (infile_name):
724         sys.stderr.write ('Processing `%s\' ... '% infile_name)
725         outfile_name = ''
726         if __main__.edit:
727                 outfile_name = infile_name + '.NEW'
728         elif __main__.outfile_name:
729                 outfile_name = __main__.outfile_name
730
731         if __main__.from_version:
732                 from_version = __main__.from_version
733         else:
734                 guess = guess_lilypond_version (infile_name)
735                 if not guess:
736                         raise UnknownVersion()
737                 from_version = str_to_tuple (guess)
738
739         if __main__.to_version:
740                 to_version = __main__.to_version
741         else:
742                 to_version = latest_version ()
743
744
745         if infile_name:
746                 infile = open (infile_name,'r')
747         else:
748                 infile = sys.stdin
749
750         if outfile_name:
751                 outfile =  open (outfile_name, 'w')
752         else:
753                 outfile = sys.stdout
754
755         touched = do_conversion (infile, from_version, outfile, to_version)
756
757         if infile_name:
758                 infile.close ()
759
760         if outfile_name:
761                 outfile.close ()
762
763         if __main__.edit and touched:
764                 try:
765                         os.remove(infile_name + '~')
766                 except:
767                         pass
768                 os.rename (infile_name, infile_name + '~')
769                 os.rename (infile_name + '.NEW', infile_name)
770
771         sys.stderr.write ('\n')
772         sys.stderr.flush ()
773
774 edit = 0
775 to_version = ()
776 from_version = ()
777 outfile_name = ''
778
779 (options, files) = getopt.getopt (
780         sys.argv[1:], 'o:f:t:seh', ['version', 'output', 'show-rules', 'help', 'edit', 'from=', 'to='])
781
782 for opt in options:
783         o = opt[0]
784         a = opt[1]
785         if o== '--help' or o == '-h':
786                 usage ()
787                 sys.exit (0)
788         if o == '--version' or o == '-v':
789                 print_version ()
790                 sys.exit (0)
791         elif o== '--from' or o=='-f':
792                 from_version = str_to_tuple (a)
793         elif o== '--to' or o=='-t':
794                 to_version = str_to_tuple (a)
795         elif o== '--edit' or o == '-e':
796                 edit = 1
797         elif o== '--show-rules' or o == '-s':
798                 show_rules (sys.stdout)
799                 sys.exit(0)
800         elif o == '--output' or o == '-o':
801                 outfile_name = a
802         else:
803                 print o
804                 raise getopt.error
805
806 identify ()
807 for f in files:
808         if f == '-':
809                 f = ''
810         try:
811                 do_one_file (f)
812         except UnknownVersion:
813                 pass