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