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