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