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