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