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