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