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