]> git.lyx.org Git - lyx.git/blob - lib/scripts/layout2layout.py
Split osf options to families
[lyx.git] / lib / scripts / layout2layout.py
1 # -*- coding: utf-8 -*-
2
3 # file layout2layout.py
4 # This file is part of LyX, the document processor.
5 # Licence details can be found in the file COPYING.
6
7 # author Georg Baum
8
9 # Full author contact details are available in file CREDITS
10
11 # This script will update a .layout file to current format
12
13 # The latest layout format is also defined in src/TextClass.cpp
14 currentFormat = 76
15
16
17 # Incremented to format 4, 6 April 2007, lasgouttes
18 # Introduction of generic "Provides" declaration
19
20 # Incremented to format 5, 22 August 2007 by vermeer
21 # InsetLayout material
22
23 # Incremented to format 6, 7 January 2008 by spitz
24 # Requires tag added to layout files
25
26 # Incremented to format 7, 24 March 2008 by rgh
27 # AddToPreamble tag added to layout files
28
29 # Incremented to format 8, 25 July 2008 by rgh
30 # UseModule tag added to layout files
31 # CopyStyle added to InsetLayout
32
33 # Incremented to format 9, 5 October 2008 by rgh
34 # ForcePlain and CustomPars tags added to InsetLayout
35
36 # Incremented to format 10, 6 October 2008 by rgh
37 # Change format of counters
38
39 # Incremented to format 11, 14 October 2008 by rgh
40 # Add ProvidesModule, ExcludesModule tags
41
42 # Incremented to format 12, 10 January 2009 by gb
43 # Add I18NPreamble tag
44
45 # Incremented to format 13, 5 February 2009 by rgh
46 # Add InToc tag for InsetLayout
47
48 # Incremented to format 14, 14 February 2009 by gb
49 # Rename I18NPreamble to BabelPreamble and add LangPreamble
50
51 # Incremented to format 15, 28 May 2009 by lasgouttes
52 # Add new tag OutputFormat; modules can be conditioned on feature
53 # "from->to".
54
55 # Incremented to format 16, 5 June 2009 by rgh
56 # Add new tags for Text Class:
57 #   HTMLPreamble, HTMLAddToPreamble
58 # For Layout:
59 #   HTMLTag, HTMLAttr, HTMLLabel, HTMLLabelAttr, HTMLItem, HTMLItemAttr
60 #   HTMLStyle, and HTMLPreamble
61 # For InsetLayout:
62 #   HTMLTag, HTMLAttr, HTMLStyle, and HTMLPreamble
63 # For Floats:
64 #   HTMLType, HTMLClass, HTMLStyle
65
66 # Incremented to format 17, 12 August 2009 by rgh
67 # Add IfStyle and IfCounter tags for layout.
68
69 # Incremented to format 18, 27 October 2009 by rgh
70 # Added some new tags for HTML output.
71
72 # Incremented to format 19, 17 November 2009 by rgh
73 # Added InPreamble tag.
74
75 # Incremented to format 20, 17 December 2009 by rgh
76 # Added ContentAsLabel tag.
77
78 # Incremented to format 21, 12 January 2010 by rgh
79 # Added HTMLTocLayout and HTMLTitle tags.
80
81 # Incremented to format 22, 20 January 2010 by rgh
82 # Added HTMLFormat tag to Counters.
83
84 # Incremented to format 23, 13 February 2010 by spitz
85 # Added Spellcheck tag.
86
87 # Incremented to format 24, 5 March 2010 by rgh
88 # Changed LaTeXBuiltin tag to NeedsFloatPkg and
89 # added new tag ListCommand.
90
91 # Incremented to format 25, 12 March 2010 by rgh
92 # Added RefPrefix tag for layouts and floats.
93
94 # Incremented to format 26, 29 March 2010 by rgh
95 # Added CiteFormat.
96
97 # Incremented to format 27, 4 June 2010 by rgh
98 # Added RequiredArgs tag.
99
100 # Incremented to format 28, 6 August 2010 by lasgouttes
101 # Added ParbreakIsNewline tag for Layout and InsetLayout.
102
103 # Incremented to format 29, 10 August 2010 by rgh
104 # Changed Custom:Style, CharStyle:Style, and Element:Style
105 # uniformly to Flex:Style.
106
107 # Incremented to format 30, 13 August 2010 by rgh
108 # Introduced ResetsFont tag for InsetLayout.
109
110 # Incremented to format 31, 12 January 2011 by rgh
111 # Introducted NoCounter tag.
112
113 # Incremented to format 32, 30 January 2011 by forenr
114 # Added Display tag for InsetLayout
115
116 # Incremented to format 33, 2 February 2011 by rgh
117 # Changed NeedsFloatPkg to UsesFloatPkg
118
119 # Incremented to format 34, 28 March 2011 by rgh
120 # Remove obsolete Fill_(Top|Bottom) tags
121
122 # Incremented to format 35, 28 March 2011 by rgh
123 # Try to add "Flex:" to any flex insets that don't have it.
124
125 # Incremented to format 36, 7 December 2011, by rgh
126 # Added HTMLStyles and AddToHTMLStyles tags.
127
128 # Incremented to format 37, 29 February 2012 by jrioux
129 # Implement the citation engine machinery in layouts.
130 # Change CiteFormat to CiteFormat (default|authoryear|numerical).
131
132 # Incremented to format 38, 08 April 2012 by gb
133 # Introduce LangPreamble and BabelPreamble for InsetLayout.
134
135 # Incremented to format 39, 15 April 2012 by sanda
136 # Introduce styling of branches via "InsetLayout Branch:".
137
138 # Incremented to format 40, 10 October 2012 by rgh
139 # Re-do layout names for layout categories
140
141 # Incremented to format 41, 20 November 2012 by spitz
142 # New Argument syntax
143
144 # Incremented to format 42, 22 December 2012 by spitz
145 # New Style tag "ItemCommand"
146
147 # Incremented to format 43, 30 December 2012 by spitz
148 # Extended InsetCaption format
149
150 # Incremented to format 44, 9 February 2013 by rgh
151 # Remove COUNTER label style; rename as STATIC
152 # Rename TOP_ENVIRONMENT to ABOVE and CENTERED_TOP_ENVIRONMENT to CENTERED
153
154 # Incremented to format 45, 12 February 2013 by rgh
155 # New Tag "NoInsetLayout"
156
157 # Incremented to format 46, 15 May 2013 by gb
158 # New Tag "ForceLocal"
159
160 # Incremented to format 47, 23 May 2013 by rgh
161 # Add PackageOptions tag
162
163 # Incremented to format 48, 31 May 2013 by rgh
164 # Add InitialValue tag for counters
165
166 # Incremented to format 49, 10 Feb 2014 by gb
167 # Change default of "ResetsFont" tag to false
168
169 # Incremented to format 50, 9 May 2014 by forenr
170 # Removal of "Separator" layouts
171
172 # Incremented to format 51, 29 May 2014 by spitz
173 # New Style tag "ToggleIndent"
174
175 # Incremented to format 52, 1 December 2014 by spitz
176 # New InsetLayout tag "ForceOwnlines"
177
178 # Incremented to format 53, 7 December 2014 by spitz
179 # New InsetLayout tag "ObsoletedBy"
180
181 # Incremented to format 54, 11 Jan 2014 by gb
182 # New InsetLayout tag "FixedWidthPreambleEncoding"
183
184 # Incremented to format 55, 20 April 2015 by spitz
185 # New InsetLayout and Layout tags "PassThruChars"
186
187 # Incremented to format 56, 20 May 2015 by spitz
188 # New Float tags "AllowedPlacement", "AllowsWide", "AllowsSideways"
189
190 # Incremented to format 57, 30 May 2015 by spitz
191 # New Layout tag "ParagraphGroup"
192
193 # Incremented to format 58, 5 December 2015, by rgh
194 # New Layout tag "ProvideStyle"
195 # Change "IfStyle" to "ModifyStyle"
196
197 # Incremented to format 59, 22 November 2015 by gm
198 # New Tag "OutlinerName"
199 # New Layout tags "AddToToc", "IsTocCaption"
200 # New Layout argument tag "IsTocCaption"
201
202 # Incremented to format 60, 25 March 2016 by lasgouttes
203 # Rename caption subtype LongTableNoNumber to Unnumbered
204
205 # Incremented to format 61, 14 October 2016 by spitz
206 # New Layout tags "ResumeCounter", "StepMasterCounter"
207
208 # Incremented to format 62, 21 October 2016 by spitz
209 # New Layout argument tag "PassThru"
210
211 # Incremented to format 63, 7 January 2017 by spitz
212 # - New textclass tags CiteFramework, MaxCiteNames (for cite engines)
213 # - Extended InsetCite syntax.
214
215 # Incremented to format 64, 30 August 2017 by rgh
216 # Strip leading and trailing spaces from LabelString,
217 # LabelStringAppendix, and EndLabelString, and LabelCounter,
218 # to conform to what we used to do.
219
220 # Incremented to format 65, 16 October 2017 by spitz
221 # Color collapsable -> collapsible
222
223 # Incremented to format 66, 28 December 2017 by spitz
224 # New Layout tags "AutoNests ... EndAutoNests" and
225 # "IsAutoNestedBy ... EndIsAutoNestedBy"
226
227 # Incremented to format 67, 14 April 2018 by spitz
228 # New Layout tag "NeedsCProtect"
229
230 # Incremented to format 68, 21 May 2018 by spitz
231 # New Layout tag "AddToCiteEngine"
232
233 # Incremented to format 69, 16 August 2018 by spitz
234 # New argument type "listpreamble"
235
236 # Incremented to format 70, 5 June 2018 by rkh
237 # New InsetLayout tag EditExternal
238
239 # Incremented to format 71, 12 March 2019 by spitz
240 # New [Inset]Layout tag NeedMBoxProtect
241
242 # Incremented to format 72, 26 March 2019 by spitz
243 # New TextClass tag TableStyle
244
245 # Incremented to format 73, 18 April 2019 by spitz
246 # New InsetLayout tag MenuString
247
248 # Incremented to format 74, 18 April 2019 by spitz
249 # New InsetLayout and Argument tag NewlineCmd
250
251 # Incremented to format 75, 2 June 2019 by spitz
252 # New Argument tags FreeSpacing, InsertOnNewline
253 # New InsetLayout tag ParbreakIgnored
254
255 # Incremented to format 76, 8 July 2019 by spitz
256 # New textclass tag BibInToc
257
258 # Do not forget to document format change in Customization
259 # Manual (section "Declaring a new text class").
260
261 # You might also want to consider running the
262 # development/tools/updatelayouts.py script to update all
263 # layout files to the new format.
264
265
266 import os, re, sys
267 import argparse
268
269 # Provide support for both python 2 and 3
270 # (copied from lyx2lyx)
271 PY2 = sys.version_info[0] == 2
272 if PY2:
273     # argparse returns strings in the commandline encoding, we need to convert.
274     # sys.getdefaultencoding() would not always be correct, see
275     # http://legacy.python.org/dev/peps/pep-0383/
276     def cmd_arg(arg):
277         return arg.decode(sys.getfilesystemencoding())
278 else:
279     cmd_arg = str
280 # End of code to support for both python 2 and 3
281
282
283 def error(message):
284     sys.stderr.write(message + '\n')
285     sys.exit(1)
286
287
288 def trim_bom(line):
289     " Remove byte order mark."
290     if line[0:3] == "\357\273\277":
291         return line[3:]
292     else:
293         return line
294
295
296 def read(source):
297     " Read input file and strip lineendings."
298     lines = source.read().splitlines() or ['']
299     lines[0] = trim_bom(lines[0])
300     return lines
301
302
303 def write(output, lines):
304     " Write output file with native lineendings."
305     output.write(os.linesep.encode('ascii').join(lines)
306                  + os.linesep.encode('ascii'))
307
308
309 # Concatenates old and new in an intelligent way:
310 # If old is wrapped in ", they are stripped. The result is wrapped in ".
311 def concatenate_label(old, new):
312     # Don't use strip as long as we support python 1.5.2
313     if old[0] == b'"':
314         return old[0:-1] + new + b'"'
315     else:
316         return b'"' + old + new + b'"'
317
318 # appends a string to a list unless it's already there
319 def addstring(s, l):
320     if l.count(s) > 0:
321         return
322     l.append(s)
323
324
325 def convert(lines, end_format):
326     " Convert to new format."
327     re_Comment = re.compile(b'^(\\s*)#')
328     re_Counter = re.compile(b'\\s*Counter\\s*', re.IGNORECASE)
329     re_Name = re.compile(b'\\s*Name\\s+(\\S+)\\s*', re.IGNORECASE)
330     re_UseMod = re.compile(b'^\\s*UseModule\\s+(.*)', re.IGNORECASE)
331     re_Empty = re.compile(b'^(\\s*)$')
332     re_Format = re.compile(b'^(\\s*)(Format)(\\s+)(\\S+)', re.IGNORECASE)
333     re_Preamble = re.compile(b'^(\\s*)Preamble', re.IGNORECASE)
334     re_EndPreamble = re.compile(b'^(\\s*)EndPreamble', re.IGNORECASE)
335     re_LangPreamble = re.compile(b'^(\\s*)LangPreamble', re.IGNORECASE)
336     re_EndLangPreamble = re.compile(b'^(\\s*)EndLangPreamble', re.IGNORECASE)
337     re_BabelPreamble = re.compile(b'^(\\s*)BabelPreamble', re.IGNORECASE)
338     re_EndBabelPreamble = re.compile(b'^(\\s*)EndBabelPreamble', re.IGNORECASE)
339     re_MaxCounter = re.compile(b'^(\\s*)(MaxCounter)(\\s+)(\\S+)', re.IGNORECASE)
340     re_LabelType = re.compile(b'^(\\s*)(LabelType)(\\s+)(\\S+)', re.IGNORECASE)
341     re_LabelString = re.compile(b'^(\\s*)(LabelString)(\\s+)(("[^"]+")|(\\S+))', re.IGNORECASE)
342     re_LabelStringAppendix = re.compile(b'^(\\s*)(LabelStringAppendix)(\\s+)(("[^"]+")|(\\S+))', re.IGNORECASE)
343     re_LatexType = re.compile(b'^(\\s*)(LatexType)(\\s+)(\\S+)', re.IGNORECASE)
344     re_Style = re.compile(b'^(\\s*)(Style)(\\s+)(\\S+)', re.IGNORECASE)
345     re_IfStyle = re.compile(b'^(\\s*)IfStyle(\\s+\\S+)', re.IGNORECASE)
346     re_CopyStyle = re.compile(b'^(\\s*)(CopyStyle)(\\s+)(\\S+)', re.IGNORECASE)
347     re_NoStyle = re.compile(b'^(\\s*)(NoStyle)(\\s+)(\\S+)', re.IGNORECASE)
348     re_End = re.compile(b'^(\\s*)(End)(\\s*)$', re.IGNORECASE)
349     re_Provides = re.compile(b'^(\\s*)Provides(\\S+)(\\s+)(\\S+)', re.IGNORECASE)
350     re_CharStyle = re.compile(b'^(\\s*)CharStyle(\\s+)(\\S+)$', re.IGNORECASE)
351     re_CiteFormat = re.compile(b'^(\\s*)(CiteFormat)(?:(\\s*)()|(\\s+)(default|authoryear|numerical))', re.IGNORECASE)
352     re_AMSMaths = re.compile(b'^\\s*Input ams(?:math|def)s.inc\\s*')
353     re_AMSMathsPlain = re.compile(b'^\\s*Input amsmaths-plain.inc\\s*')
354     re_AMSMathsSeq = re.compile(b'^\\s*Input amsmaths-seq.inc\\s*')
355     re_TocLevel = re.compile(b'^(\\s*)(TocLevel)(\\s+)(\\S+)', re.IGNORECASE)
356     re_I18nPreamble = re.compile(b'^(\\s*)I18nPreamble', re.IGNORECASE)
357     re_EndI18nPreamble = re.compile(b'^(\\s*)EndI18nPreamble', re.IGNORECASE)
358     re_Float = re.compile(b'^\\s*Float\\s*$', re.IGNORECASE)
359     re_Type = re.compile(b'\\s*Type\\s+(\\w+)', re.IGNORECASE)
360     re_Builtin = re.compile(b'^(\\s*)LaTeXBuiltin\\s+(\\w*)', re.IGNORECASE)
361     re_True = re.compile(b'^\\s*(?:true|1)\\s*$', re.IGNORECASE)
362     re_InsetLayout = re.compile(b'^\\s*InsetLayout\\s+(?:Custom|CharStyle|Element):(\\S+)\\s*$', re.IGNORECASE)
363     re_ResetsFont = re.compile(b'^(\\s*)ResetsFont(\\s+)(\\S+)$', re.IGNORECASE)
364     # with quotes
365     re_QInsetLayout = re.compile(b'^\\s*InsetLayout\\s+"(?:Custom|CharStyle|Element):([^"]+)"\\s*$', re.IGNORECASE)
366     re_InsetLayout_CopyStyle = re.compile(b'^\\s*CopyStyle\\s+(?:Custom|CharStyle|Element):(\\S+)\\s*$', re.IGNORECASE)
367     re_QInsetLayout_CopyStyle = re.compile(b'^\\s*CopyStyle\\s+"(?:Custom|CharStyle|Element):([^"]+)"\\s*$', re.IGNORECASE)
368     re_NeedsFloatPkg = re.compile(b'^(\\s*)NeedsFloatPkg\\s+(\\w+)\\s*$', re.IGNORECASE)
369     re_Fill = re.compile(b'^\\s*Fill_(?:Top|Bottom).*$', re.IGNORECASE)
370     re_InsetLayout2 = re.compile(b'^\\s*InsetLayout\\s+(\\S+)\\s*$', re.IGNORECASE)
371     # with quotes
372     re_QInsetLayout2 = re.compile(b'^\\s*InsetLayout\\s+"([^"]+)"\\s*$', re.IGNORECASE)
373     re_IsFlex = re.compile(b'\\s*LyXType.*$', re.IGNORECASE)
374     re_CopyStyle2 = re.compile(b'(\\s*CopyStyle\\s+)"?([^"]+)"?\\s*$')
375     re_Separator = re.compile(b'^(?:(-*)|(\\s*))(Separator|EndOfSlide)(?:(-*)|(\\s*))$', re.IGNORECASE)
376     # for categories
377     re_Declaration = re.compile(b'^#\\s*\\Declare\\w+Class.*$')
378     re_ExtractCategory = re.compile(b'^(#\\s*\\Declare\\w+Class(?:\\[[^]]*?\\])?){([^(]+?)\\s+\\(([^)]+?)\\)\\s*}\\s*$')
379     ConvDict = {"article": "Articles", "book" : "Books", "letter" : "Letters", "report": "Reports", \
380                 "presentation" : "Presentations", "curriculum vitae" : "Curricula Vitae", "handout" : "Handouts"}
381     # Arguments
382     re_OptArgs = re.compile(b'^(\\s*)OptionalArgs(\\s+)(\\d+)\\D*$', re.IGNORECASE)
383     re_ReqArgs = re.compile(b'^(\\s*)RequiredArgs(\\s+)(\\d+)\\D*$', re.IGNORECASE)
384
385     # various changes associated with changing how chapters are handled
386     re_LabelTypeIsCounter = re.compile(b'^(\\s*)LabelType(\\s*)Counter\\s*$', re.IGNORECASE)
387     re_TopEnvironment = re.compile(b'^(\\s*)LabelType(\\s+)Top_Environment\\s*$', re.IGNORECASE)
388     re_CenteredEnvironment = re.compile(b'^(\\s*)LabelType(\\s+)Centered_Top_Environment\\s*$', re.IGNORECASE)
389     re_ChapterStyle = re.compile(b'^\\s*Style\\s+Chapter\\s*$', re.IGNORECASE)
390     re_InsetLayout_CaptionLTNN = re.compile(b'^(\\s*InsetLayout\\s+)(Caption:LongTableNonumber)', re.IGNORECASE)
391     # for format 64
392     re_trimLabelString = re.compile(b'^(\\s*LabelString\s+)"\\s*(.*?)\\s*"\\s*$')
393     re_trimLabelStringAppendix  = re.compile(b'^(\\s*LabelStringAppendix\s+)"\\s*(.*?)\\s*"\\s*$')
394     re_trimEndLabelString = re.compile(b'^(\\s*EndLabelString\s+)"\\s*(.*?)\\s*"\\s*$')
395     re_trimLabelCounter = re.compile(b'^(\\s*LabelCounter\s+)"\\s*(.*?)\\s*"\\s*$')
396
397
398     # counters for sectioning styles (hardcoded in 1.3)
399     counters = {b"part"          : b"\\Roman{part}",
400                 b"chapter"       : b"\\arabic{chapter}",
401                 b"section"       : b"\\arabic{section}",
402                 b"subsection"    : b"\\arabic{section}.\\arabic{subsection}",
403                 b"subsubsection" : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}",
404                 b"paragraph"     : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}",
405                 b"subparagraph"  : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}.\\arabic{subparagraph}"}
406
407     # counters for sectioning styles in appendix (hardcoded in 1.3)
408     appendixcounters = {b"chapter"       : b"\\Alph{chapter}",
409                         b"section"       : b"\\Alph{section}",
410                         b"subsection"    : b"\\arabic{section}.\\arabic{subsection}",
411                         b"subsubsection" : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}",
412                         b"paragraph"     : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}",
413                         b"subparagraph"  : b"\\arabic{section}.\\arabic{subsection}.\\arabic{subsubsection}.\\arabic{paragraph}.\\arabic{subparagraph}"}
414
415     # Value of TocLevel for sectioning styles
416     toclevels = {b"part"          : -1,
417                  b"chapter"       : 0,
418                  b"section"       : 1,
419                  b"subsection"    : 2,
420                  b"subsubsection" : 3,
421                  b"paragraph"     : 4,
422                  b"subparagraph"  : 5}
423
424     i = 0
425     only_comment = 1
426     counter = b""
427     toclevel = b""
428     label = b""
429     labelstring = b""
430     labelstringappendix = b""
431     space1 = b""
432     labelstring_line = -1
433     labelstringappendix_line = -1
434     labeltype_line = -1
435     latextype = b""
436     latextype_line = -1
437     style = b""
438     maxcounter = 0
439     format = 1
440     formatline = 0
441     usemodules = []
442     flexstyles = []
443     opts = 0
444     reqs = 0
445     inchapter = False
446     isflexlayout = False         # only used for 48 -> 49
447     # Whether a style is inherited (works only for CopyStyle currently,
448     # not for true inherited styles, see bug 8920
449     inherited = False        # only used for 48 -> 49
450     resetsfont_found = False # only used for 48 -> 49
451
452     while i < len(lines):
453         # Skip comments and empty lines
454         if (re_Comment.match(lines[i]) or re_Empty.match(lines[i])):
455           # We need to deal with this conversion here, because it happens
456           # inside the initial comment block.
457           if only_comment and format == 39:
458               match = re_ExtractCategory.match(lines[i])
459               if match:
460                   lpre = match.group(1)
461                   lcat = match.group(2)
462                   lnam = match.group(3)
463                   if lcat in ConvDict:
464                       lcat = ConvDict[lcat]
465                   lines[i] = lpre + b"{" + lnam + b"}"
466                   lines.insert(i+1, b"#  \\DeclareCategory{" + lcat + b"}")
467                   i += 1
468           i += 1
469           continue
470
471         # insert file format if not already there
472         if (only_comment):
473             match = re_Format.match(lines[i])
474             if match:
475                 formatline = i
476                 format = int(match.group(4))
477                 if format > 1 and format < end_format:
478                     lines[i] = b"Format %d" % (format + 1)
479                     only_comment = 0
480                 elif format == end_format:
481                     # nothing to do
482                     return format
483                 else:
484                     error('Cannot convert file format %s to %s' % (format, end_format))
485             else:
486                 lines.insert(i, b"Format 2")
487                 only_comment = 0
488                 continue
489
490         # Don't get confused by LaTeX code
491         if re_Preamble.match(lines[i]):
492             i += 1
493             while i < len(lines) and not re_EndPreamble.match(lines[i]):
494                 i += 1
495             continue
496         if re_LangPreamble.match(lines[i]):
497             i += 1
498             while i < len(lines) and not re_EndLangPreamble.match(lines[i]):
499                 i += 1
500             continue
501         if re_BabelPreamble.match(lines[i]):
502             i += 1
503             while i < len(lines) and not re_EndBabelPreamble.match(lines[i]):
504                 i += 1
505             continue
506
507         if format >= 65 and format <= 75:
508             # nothing to do.
509             i += 1
510             continue
511
512         if format == 64:
513             match = re.compile(b'(\\s*Color\\s+)(\\w+)', re.IGNORECASE).match(lines[i])
514             if not match:
515                 i += 1
516                 continue
517             col  = match.group(2)
518             if col == "collapsable":
519                 lines[i] = match.group(1) + "collapsible"
520             i += 1
521             continue
522
523         if format == 63:
524             for r in (re_trimLabelString, re_trimLabelStringAppendix,\
525               re_trimEndLabelString, re_trimLabelCounter):
526                 m = r.match(lines[i])
527                 if m:
528                     lines[i] = m.group(1) + b'"' + m.group(2) + b'"'
529             i += 1
530             continue
531
532         if format >= 60 and format <= 62:
533             # nothing to do.
534             i += 1
535             continue
536
537         if format == 59:
538             match = re_InsetLayout_CaptionLTNN.match(lines[i])
539             if not match:
540                 i += 1
541                 continue
542             # '^(\s*InsetLayout\s+)(Caption:LongTableNonumber)'
543             lead  = match.group(1)
544             lines[i] = lead + b"Caption:Unnumbered"
545             i += 1
546             continue
547
548         if format == 58:
549             # nothing to do.
550             i += 1
551             continue
552
553         if format == 57:
554             match = re_IfStyle.match(lines[i])
555             if not match:
556                 i += 1
557                 continue
558             # b'^(\\s*)IfStyle(\\s+\\S+)
559             lead  = match.group(1)
560             trail = match.group(2)
561             lines[i] = lead + b"ModifyStyle" + trail
562             i += 1
563             continue
564
565         if format >= 50 and format <= 56:
566             # nothing to do.
567             i += 1
568             continue
569
570         if format == 49:
571             separator = []
572
573             # delete separator styles
574             match = re_Style.match(lines[i])
575             if match:
576                 style = match.group(4).lower()
577                 if re_Separator.match(style):
578                     del lines[i]
579                     while i < len(lines) and not re_End.match(lines[i]):
580                         separator.append(lines[i])
581                         del lines[i]
582                     if i == len(lines):
583                         error('Incomplete separator style.')
584                     else:
585                         del lines[i]
586                         continue
587
588             # delete undefinition of separator styles
589             match = re_NoStyle.match(lines[i])
590             if match:
591                 style = match.group(4).lower()
592                 if re_Separator.match(style):
593                     del lines[i]
594                     continue
595
596             # replace the CopyStyle statement with the definition of the real
597             # style. This may result in duplicate statements, but that is OK
598             # since the second one will overwrite the first one.
599             match = re_CopyStyle.match(lines[i])
600             if match:
601                 style = match.group(4).lower()
602                 if re_Separator.match(style):
603                     if len(separator) > 0:
604                         lines[i:i+1] = separator
605                     else:
606                         # FIXME: If this style was redefined in an include file,
607                         # we should replace the real style and not this default.
608                         lines[i:i+1] = [b'      Category              MainText',
609                                         b'      KeepEmpty             1',
610                                         b'      Margin                Dynamic',
611                                         b'      LatexType             Paragraph',
612                                         b'      LatexName             dummy',
613                                         b'      ParIndent             MM',
614                                         b'      Align                 Block',
615                                         b'      LabelType             Static',
616                                         b'      LabelString           "--- Separate Environment ---"',
617                                         b'      LabelFont',
618                                         b'        Family              Roman',
619                                         b'        Series              Medium',
620                                         b'        Size                Normal',
621                                         b'        Color               Blue',
622                                         b'      EndFont',
623                                         b'      HTMLLabel             NONE']
624             i += 1
625             continue
626
627         if format == 48:
628             # The default of ResetsFont in LyX changed from true to false,
629             # because it is now used for all InsetLayouts, not only flex ones.
630             # Therefore we need to set it to true for all flex insets which do
631             # do not already have a ResetsFont.
632             match = re_InsetLayout2.match(lines[i])
633             if not match:
634                 i += 1
635                 continue
636
637             name = match.group(1).lower()
638             if name != b"flex" and name != b"\"flex\"" and name[0:5] != b"flex:" and name [0:6] != b"\"flex:":
639                 i += 1
640                 continue
641
642             resetsfont_found = False
643             inherited = False
644             notdone = True
645             while i < len(lines):
646               match = re_ResetsFont.match(lines[i])
647               if match:
648                   resetsfont_found = True
649               else:
650                 match = re_CopyStyle.match(lines[i])
651                 if match:
652                   inherited = True
653                 else:
654                   match = re_End.match(lines[i])
655                   if match:
656                     break
657               i += 1
658             if not resetsfont_found and not inherited:
659               lines.insert(i, b"\tResetsFont true")
660
661             continue
662
663         if format >= 44 and format <= 47:
664             # nothing to do.
665             i += 1
666             continue
667
668         if format == 43:
669           match = re_LabelTypeIsCounter.match(lines[i])
670           if match:
671             if inchapter:
672              lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Above"
673             else:
674               lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Static"
675
676           match = re_TopEnvironment.match(lines[i])
677           if match:
678             lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Above"
679
680           match = re_CenteredEnvironment.match(lines[i])
681           if match:
682             lines[i] = match.group(1) + b"LabelType" + match.group(2) + b"Centered"
683
684           if inchapter:
685             match = re_Style.match(lines[i])
686             if match:
687               inchapter = False
688           else:
689             match = re_ChapterStyle.match(lines[i])
690             if match:
691               inchapter = True
692
693           i += 1
694           continue
695
696         if format == 42:
697           if lines[i] == b"InsetLayout Caption":
698             lines[i] = b"InsetLayout Caption:Standard"
699           i += 1
700           continue
701
702         if format == 41:
703             # nothing to do.
704             i += 1
705             continue
706
707         if format == 40:
708             # reset counters on Style beginning
709             match = re_Style.match(lines[i])
710             if match:
711                 opts = 0
712                 reqs = 0
713                 i += 1
714                 continue
715             match = re_OptArgs.match(lines[i])
716             if match:
717                 # Save number of optional arguments
718                 space1 = match.group(1)
719                 opts = int(match.group(3))
720                 # OptionalArgs 0 > ResetArgs 1
721                 if opts == 0:
722                     lines[i] = space1 + b"ResetArgs\t1"
723                     i += 1
724                 else:
725                     del lines[i]
726                 continue
727             match = re_ReqArgs.match(lines[i])
728             if match:
729                 # Save number of required arguments
730                 space1 = match.group(1)
731                 reqs = int(match.group(3))
732                 del lines[i]
733                 continue
734             # Insert the required number of arguments at the end of the style definition
735             match = re_End.match(lines[i])
736             if match:
737                 newarg = ['']
738                 # First the optionals (this is the required order pre 2.1)
739                 if opts > 0:
740                     if opts == 1:
741                         newarg = [ b'%sArgument 1' % (space1),
742                                    b'%s\tLabelString\t\"Optional Layout Argument\"' % (space1),
743                                    b'%sEndArgument' % (space1)]
744                     elif opts > 1:
745                         actopt = 1
746                         while actopt < (opts + 1):
747                             newarg += [ b'%sArgument %d' % (space1, actopt),
748                                b'%s\tLabelString\t\"Optional Layout Argument %d\"' % (space1, actopt),
749                                b'%sEndArgument' % (space1)]
750                             actopt += 1
751                 # Now the mandatories
752                 if reqs > 0:
753                     actopt = opts + 1
754                     while actopt < (opts +  reqs + 1):
755                         newarg += [ b'%sArgument %d' % (space1, actopt),
756                            b'%s\tLabelString\t"Required Layout Argument %d"' % (space1, actopt - opts),
757                            b'%s\tMandatory\t1' % (space1),
758                            b'%sEndArgument' % (space1)]
759                         actopt += 1
760                 # Since we replace the "End" line, re-add this line
761                 if len(newarg) > 1:
762                     newarg += [b'End']
763                     lines[i:i+1] = newarg
764                     i += len(newarg)
765                 # Reset the counters
766                 opts = 0
767                 reqs = 0
768             i += 1
769             continue
770
771         if format == 39:
772             # There is a conversion with format 40, but it is done within the
773             # initial comment block and so is above.
774             i += 1
775             continue
776
777         if format == 37 or format == 38:
778             i += 1
779             continue
780
781         if format == 36:
782             match = re_CiteFormat.match(lines[i]);
783             if match and match.group(4) == b"":
784                 lines[i] = match.group(0) + b" default"
785             i += 1
786             continue
787
788         if format == 35:
789           i += 1
790           continue
791
792         if format == 34:
793           match = re_QInsetLayout2.match(lines[i])
794           if not match:
795             match = re_InsetLayout2.match(lines[i])
796           if not match:
797             match = re_CopyStyle2.match(lines[i])
798             if not match:
799               i += 1
800               continue
801             style = match.group(2)
802
803             if flexstyles.count(style):
804               lines[i] = match.group(1) + b"\"Flex:" + style + b"\""
805             i += 1
806             continue
807
808           name = match.group(1)
809           names = name.split(b":", 1)
810           if len(names) > 1 and names[0] == b"Flex":
811             i += 1
812             continue
813
814           isflex = False
815           for j in range(i + 1, len(lines)):
816             if re_IsFlex.match(lines[j]):
817               isflex = True
818               break
819             if re_End.match(lines[j]):
820               break
821
822           if isflex:
823             flexstyles.append(name)
824             lines[i] = b"InsetLayout \"Flex:" + name + b"\""
825
826           i += 1
827           continue
828
829         if format == 33:
830           m = re_Fill.match(lines[i])
831           if m:
832             lines[i] = b""
833           i += 1
834           continue
835
836         if format == 32:
837           match = re_NeedsFloatPkg.match(lines[i])
838           if match:
839             space = match.group(1)
840             val = match.group(2)
841             lines[i] = space + b"UsesFloatPkg " + val
842             newval = b'true'
843             if val == b'1' or val.lower() == b'true':
844               newval = b'false'
845             lines.insert(i, space + b"IsPredefined " + newval)
846             i += 1
847           i += 1
848           continue
849
850         # Only new features
851         if format >= 29 and format <= 31:
852           i += 1
853           continue
854
855         if format == 28:
856           match = re_InsetLayout.match(lines[i])
857           if match:
858             lines[i] = b"InsetLayout Flex:" + match.group(1)
859           else:
860             match = re_QInsetLayout.match(lines[i])
861             if match:
862               lines[i] = b"InsetLayout \"Flex:" + match.group(1) + b"\""
863             else:
864               match = re_InsetLayout_CopyStyle.match(lines[i])
865               if match:
866                 lines[i] = b"\tCopyStyle Flex:" + match.group(1)
867               else:
868                 match = re_QInsetLayout_CopyStyle.match(lines[i])
869                 if match:
870                   lines[i] = b"\tCopyStyle \"Flex:" + match.group(1) + b"\""
871           i += 1
872           continue
873
874         # Only new features
875         if format >= 24 and format <= 27:
876           i += 1
877           continue
878
879         if format == 23:
880           match = re_Float.match(lines[i])
881           i += 1
882           if not match:
883             continue
884           # we need to do two things:
885           # (i)  Convert Builtin to NeedsFloatPkg
886           # (ii) Write ListCommand lines for the builtin floats table and figure
887           builtin = False
888           cmd = b""
889           while True and i < len(lines):
890             m1 = re_End.match(lines[i])
891             if m1:
892               if builtin and cmd:
893                 line = b"    ListCommand " + cmd
894                 lines.insert(i, line)
895                 i += 1
896               break
897             m2 = re_Builtin.match(lines[i])
898             if m2:
899               builtin = True
900               ws1 = m2.group(1)
901               arg = m2.group(2)
902               newarg = b""
903               if re_True.match(arg):
904                 newarg = b"false"
905               else:
906                 newarg = b"true"
907               lines[i] = ws1 + b"NeedsFloatPkg " + newarg
908             m3 = re_Type.match(lines[i])
909             if m3:
910               fltype = m3.group(1)
911               fltype = fltype.lower()
912               if fltype == b"table":
913                 cmd = b"listoftables"
914               elif fltype == b"figure":
915                 cmd = b"listoffigures"
916               # else unknown, which is why we're doing this
917             i += 1
918           continue
919
920         # This just involved new features, not any changes to old ones
921         if format >= 14 and format <= 22:
922           i += 1
923           continue
924
925         # Rename I18NPreamble to BabelPreamble
926         if format == 13:
927             match = re_I18nPreamble.match(lines[i])
928             if match:
929                 lines[i] = match.group(1) + b"BabelPreamble"
930                 i += 1
931                 match = re_EndI18nPreamble.match(lines[i])
932                 while i < len(lines) and not match:
933                     i += 1
934                     match = re_EndI18nPreamble.match(lines[i])
935                 lines[i] = match.group(1) + b"EndBabelPreamble"
936                 i += 1
937                 continue
938
939         # These just involved new features, not any changes to old ones
940         if format == 11 or format == 12:
941           i += 1
942           continue
943
944         if format == 10:
945             match = re_UseMod.match(lines[i])
946             if match:
947                 module = match.group(1)
948                 lines[i] = b"DefaultModule " + module
949             i += 1
950             continue
951
952         if format == 9:
953             match = re_Counter.match(lines[i])
954             if match:
955                 counterline = i
956                 i += 1
957                 while i < len(lines):
958                     namem = re_Name.match(lines[i])
959                     if namem:
960                         name = namem.group(1)
961                         lines.pop(i)
962                         lines[counterline] = b"Counter %s" % name
963                         # we don't need to increment i
964                         continue
965                     endem = re_End.match(lines[i])
966                     if endem:
967                         i += 1
968                         break
969                     i += 1
970             i += 1
971             continue
972
973         if format == 8:
974             # We want to scan for ams-type includes and, if we find them,
975             # add corresponding UseModule tags to the layout.
976             match = re_AMSMaths.match(lines[i])
977             if match:
978                 addstring(b"theorems-ams", usemodules)
979                 addstring(b"theorems-ams-extended", usemodules)
980                 addstring(b"theorems-sec", usemodules)
981                 lines.pop(i)
982                 continue
983             match = re_AMSMathsPlain.match(lines[i])
984             if match:
985                 addstring(b"theorems-starred", usemodules)
986                 lines.pop(i)
987                 continue
988             match = re_AMSMathsSeq.match(lines[i])
989             if match:
990                 addstring(b"theorems-ams", usemodules)
991                 addstring(b"theorems-ams-extended", usemodules)
992                 lines.pop(i)
993                 continue
994             i += 1
995             continue
996
997         # These just involved new features, not any changes to old ones
998         if format >= 5 and format <= 7:
999           i += 1
1000           continue
1001
1002         if format == 4:
1003             # Handle conversion to long CharStyle names
1004             match = re_CharStyle.match(lines[i])
1005             if match:
1006                 lines[i] = b"InsetLayout CharStyle:%s" % (match.group(3))
1007                 i += 1
1008                 lines.insert(i, b"\tLyXType charstyle")
1009                 i += 1
1010                 lines.insert(i, b"")
1011                 lines[i] = b"\tLabelString %s" % (match.group(3))
1012             i += 1
1013             continue
1014
1015         if format == 3:
1016             # convert 'providesamsmath x',  'providesmakeidx x',  'providesnatbib x',  'providesurl x' to
1017             #         'provides amsmath x', 'provides makeidx x', 'provides natbib x', 'provides url x'
1018             # x is either 0 or 1
1019             match = re_Provides.match(lines[i])
1020             if match:
1021                 lines[i] = b"%sProvides %s%s%s" % (match.group(1), match.group(2).lower(),
1022                                                   match.group(3), match.group(4))
1023             i += 1
1024             continue
1025
1026         if format == 2:
1027             caption = []
1028
1029             # delete caption styles
1030             match = re_Style.match(lines[i])
1031             if match:
1032                 style = match.group(4).lower()
1033                 if style == b"caption":
1034                     del lines[i]
1035                     while i < len(lines) and not re_End.match(lines[i]):
1036                         caption.append(lines[i])
1037                         del lines[i]
1038                     if i == len(lines):
1039                         error('Incomplete caption style.')
1040                     else:
1041                         del lines[i]
1042                         continue
1043
1044             # delete undefinition of caption styles
1045             match = re_NoStyle.match(lines[i])
1046             if match:
1047                 style = match.group(4).lower()
1048                 if style == b"caption":
1049                     del lines[i]
1050                     continue
1051
1052             # replace the CopyStyle statement with the definition of the real
1053             # style. This may result in duplicate statements, but that is OK
1054             # since the second one will overwrite the first one.
1055             match = re_CopyStyle.match(lines[i])
1056             if match:
1057                 style = match.group(4).lower()
1058                 if style == b"caption":
1059                     if len(caption) > 0:
1060                         lines[i:i+1] = caption
1061                     else:
1062                         # FIXME: This style comes from an include file, we
1063                         # should replace the real style and not this default.
1064                         lines[i:i+1] = [b'      Margin                First_Dynamic',
1065                                         b'      LatexType             Command',
1066                                         b'      LatexName             caption',
1067                                         b'      NeedProtect           1',
1068                                         b'      LabelSep              xx',
1069                                         b'      ParSkip               0.4',
1070                                         b'      TopSep                0.5',
1071                                         b'      Align                 Center',
1072                                         b'      AlignPossible         Center',
1073                                         b'      LabelType             Sensitive',
1074                                         b'      LabelString           "Senseless!"',
1075                                         b'      OptionalArgs          1',
1076                                         b'      LabelFont',
1077                                         b'        Series              Bold',
1078                                         b'      EndFont']
1079
1080             i += 1
1081             continue
1082
1083         # Delete MaxCounter and remember the value of it
1084         match = re_MaxCounter.match(lines[i])
1085         if match:
1086             level = match.group(4).lower()
1087             if level == b"counter_chapter":
1088                 maxcounter = 0
1089             elif level == b"counter_section":
1090                 maxcounter = 1
1091             elif level == b"counter_subsection":
1092                 maxcounter = 2
1093             elif level == b"counter_subsubsection":
1094                 maxcounter = 3
1095             elif level == b"counter_paragraph":
1096                 maxcounter = 4
1097             elif level == b"counter_subparagraph":
1098                 maxcounter = 5
1099             elif level == b"counter_enumi":
1100                 maxcounter = 6
1101             elif level == b"counter_enumii":
1102                 maxcounter = 7
1103             elif level == b"counter_enumiii":
1104                 maxcounter = 8
1105             del lines[i]
1106             continue
1107
1108         # Replace line
1109         #
1110         # LabelType Counter_EnumI
1111         #
1112         # with two lines
1113         #
1114         # LabelType Counter
1115         # LabelCounter EnumI
1116         #
1117         match = re_LabelType.match(lines[i])
1118         if match:
1119             label = match.group(4)
1120             # Remember indenting space for later reuse in added lines
1121             space1 = match.group(1)
1122             # Remember the line for adding the LabelCounter later.
1123             # We can't do it here because it could shift latextype_line etc.
1124             labeltype_line = i
1125             if label[:8].lower() == b"counter_":
1126                 counter = label[8:].lower()
1127                 lines[i] = re_LabelType.sub(b'\\1\\2\\3Counter', lines[i])
1128
1129         # Remember the LabelString line
1130         match = re_LabelString.match(lines[i])
1131         if match:
1132             labelstring = match.group(4)
1133             labelstring_line = i
1134
1135         # Remember the LabelStringAppendix line
1136         match = re_LabelStringAppendix.match(lines[i])
1137         if match:
1138             labelstringappendix = match.group(4)
1139             labelstringappendix_line = i
1140
1141         # Remember the LatexType line
1142         match = re_LatexType.match(lines[i])
1143         if match:
1144             latextype = match.group(4).lower()
1145             latextype_line = i
1146
1147         # Remember the TocLevel line
1148         match = re_TocLevel.match(lines[i])
1149         if match:
1150             toclevel = match.group(4).lower()
1151
1152         # Reset variables at the beginning of a style definition
1153         match = re_Style.match(lines[i])
1154         if match:
1155             style = match.group(4).lower()
1156             counter = b""
1157             toclevel = b""
1158             label = b""
1159             space1 = b""
1160             labelstring = b""
1161             labelstringappendix = b""
1162             labelstring_line = -1
1163             labelstringappendix_line = -1
1164             labeltype_line = -1
1165             latextype = b""
1166             latextype_line = -1
1167
1168         if re_End.match(lines[i]):
1169
1170             # Add a line "LatexType Bib_Environment" if LabelType is Bibliography
1171             # (or change the existing LatexType)
1172             if label.lower() == b"bibliography":
1173                 if (latextype_line < 0):
1174                     lines.insert(i, b"%sLatexType Bib_Environment" % space1)
1175                     i += 1
1176                 else:
1177                     lines[latextype_line] = re_LatexType.sub(b'\\1\\2\\3Bib_Environment', lines[latextype_line])
1178
1179             # Change "LabelType Static" to "LabelType Itemize" for itemize environments
1180             if latextype == b"item_environment" and label.lower() == b"static":
1181                 lines[labeltype_line] = re_LabelType.sub(b'\\1\\2\\3Itemize', lines[labeltype_line])
1182
1183             # Change "LabelType Counter_EnumI" to "LabelType Enumerate" for enumerate environments
1184             if latextype == b"item_environment" and label.lower() == b"counter_enumi":
1185                 lines[labeltype_line] = re_LabelType.sub(b'\\1\\2\\3Enumerate', lines[labeltype_line])
1186                 # Don't add the LabelCounter line later
1187                 counter = ""
1188
1189             # Replace
1190             #
1191             # LabelString "Chapter"
1192             #
1193             # with
1194             #
1195             # LabelString "Chapter \arabic{chapter}"
1196             #
1197             # if this style has a counter. Ditto for LabelStringAppendix.
1198             # This emulates the hardcoded article style numbering of 1.3
1199             #
1200             if counter != b"":
1201                 if style in counters:
1202                     if labelstring_line < 0:
1203                         lines.insert(i, b'%sLabelString "%s"' % (space1, counters[style]))
1204                         i += 1
1205                     else:
1206                         new_labelstring = concatenate_label(labelstring, counters[style])
1207                         lines[labelstring_line] = re_LabelString.sub(
1208                                 b'\\1\\2\\3%s' % new_labelstring.replace(b"\\", b"\\\\"),
1209                                 lines[labelstring_line])
1210                 if style in appendixcounters:
1211                     if labelstringappendix_line < 0:
1212                         lines.insert(i, b'%sLabelStringAppendix "%s"' % (space1, appendixcounters[style]))
1213                         i += 1
1214                     else:
1215                         new_labelstring = concatenate_label(labelstring, appendixcounters[style])
1216                         lines[labelstringappendix_line] = re_LabelStringAppendix.sub(
1217                                 b'\\1\\2\\3%s' % new_labelstring.replace(b"\\", b"\\\\"),
1218                                 lines[labelstringappendix_line])
1219
1220                 # Now we can safely add the LabelCounter line
1221                 lines.insert(labeltype_line + 1, b"%sLabelCounter %s" % (space1, counter))
1222                 i += 1
1223
1224             # Add the TocLevel setting for sectioning styles
1225             if toclevel == b"" and style in toclevels and maxcounter <= toclevels[style]:
1226                 lines.insert(i, b'%s\tTocLevel %d' % (space1, toclevels[style]))
1227                 i += 1
1228
1229         i += 1
1230
1231     if only_comment:
1232         lines.insert(i, b"Format 2")
1233     if usemodules:
1234         i = formatline + 1
1235         for mod in usemodules:
1236             lines.insert(i, b"UseModule " + mod)
1237             i += 1
1238
1239     return format + 1
1240
1241
1242 def main(argv):
1243     args = {}
1244     args["description"] = "Convert layout file <inputfile> to a newer format."
1245
1246     parser = argparse.ArgumentParser(**args)
1247
1248     parser.add_argument("-t", "--to", type=int, dest="format", default= currentFormat,
1249                         help=("destination layout format, default %i (latest)") % currentFormat)
1250     parser.add_argument("input_file", nargs='?', type=cmd_arg, default=None,
1251                         help="input file (default stdin)")
1252     parser.add_argument("output_file", nargs='?', type=cmd_arg, default=None,
1253                         help="output file (default stdout)")
1254
1255     options = parser.parse_args(argv[1:])
1256
1257     # Open files
1258     if options.input_file:
1259         source = open(options.input_file, 'rb')
1260     elif PY2:
1261         source = sys.stdin
1262     else:
1263         source = sys.stdin.buffer
1264
1265     if options.output_file:
1266         output = open(options.output_file, 'wb')
1267     elif PY2:
1268         output = sys.stdout
1269     else:
1270         output = sys.stdout.buffer
1271
1272     if options.format > currentFormat:
1273         error("Format %i does not exist" % options.format);
1274
1275     # Do the real work
1276     lines = read(source)
1277     format = 1
1278     while (format < options.format):
1279         format = convert(lines, options.format)
1280     write(output, lines)
1281
1282     # Close files
1283     if options.input_file:
1284         source.close()
1285     if options.output_file:
1286         output.close()
1287
1288     return 0
1289
1290
1291 if __name__ == "__main__":
1292     main(sys.argv)