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