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