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