1 #!/usr/bin/env python 2 3 """ 4 Moin wiki format converter. 5 6 Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from moinformat import make_parser, make_serialiser, Metadata, parse, serialise 23 from os.path import split 24 import sys 25 26 # Long messages. 27 28 message_all_with_filenames = """\ 29 Using --all overrides any indicated pagenames. Either --all or the filenames 30 should be omitted.""" 31 32 message_explicit_pagenames = """\ 33 Explicit pagenames (indicated using --pagename) are only to be specified when 34 providing filenames without an input directory (indicated using --input-dir). 35 36 To indicate pagenames within an input directory, omit any --pagename flags.""" 37 38 39 40 # Options management. 41 42 def getmapping(mappings): 43 44 """ 45 Return the given 'mappings' - a collection of key-then-value items - as a 46 dictionary. 47 """ 48 49 mapping = {} 50 key = None 51 52 for arg in mappings: 53 if key is None: 54 key = arg 55 else: 56 mapping[key] = arg 57 key = None 58 59 return mapping 60 61 def getvalue(values, default=None): 62 63 """ 64 Return the first value from 'values' or 'default' if 'values' is empty or 65 the first value tests as false. 66 """ 67 68 return values and values[0] or default 69 70 71 72 # Main program. 73 74 def main(): 75 76 "Interpret program options and perform the conversion." 77 78 dirname, progname = split(sys.argv[0]) 79 args = sys.argv[1:] 80 81 if "--help" in args: 82 show_help(progname) 83 sys.exit(0) 84 85 # Option values. 86 87 l = filenames = [] 88 formats = [] 89 input_dir_types = [] 90 input_dirs = [] 91 input_encodings = [] 92 input_page_seps = [] 93 mappings = [] 94 output_dirs = [] 95 output_encodings = [] 96 theme_names = [] 97 pagenames = [] 98 root_pagenames = [] 99 100 # Flags. 101 102 all = False 103 fragment = False 104 macros = False 105 tree = False 106 107 for arg in args: 108 109 # Detect tree output. 110 111 if arg == "--tree": 112 tree = True 113 114 # Detect macro evaluation. 115 116 elif arg == "--macros": 117 macros = True 118 119 # Detect all documents. 120 121 elif arg == "--all": 122 all = True 123 124 # Detect fragment output (if serialising). 125 126 elif arg == "--fragment": 127 fragment = True 128 129 # Switch to collecting formats. 130 131 elif arg == "--format": 132 l = formats 133 continue 134 135 # Switch to collecting input locations. 136 137 elif arg == "--input-dir": 138 l = input_dirs 139 continue 140 141 # Switch to collecting input context types. 142 143 elif arg == "--input-dir-type": 144 l = input_dir_types 145 continue 146 147 # Switch to collecting input encodings. 148 149 elif arg == "--input-encoding": 150 l = input_encodings 151 continue 152 153 # Switch to collecting input page hierarchy separators. 154 155 elif arg == "--input-page-sep": 156 l = input_page_seps 157 continue 158 159 # Switch to collecting mappings. 160 161 elif arg == "--mapping": 162 l = mappings 163 continue 164 165 # Switch to collecting output locations. 166 167 elif arg == "--output-dir": 168 l = output_dirs 169 continue 170 171 # Switch to collecting output encodings. 172 173 elif arg == "--output-encoding": 174 l = output_encodings 175 continue 176 177 # Switch to collecting page names. 178 179 elif arg == "--pagename": 180 l = pagenames 181 continue 182 183 # Switch to collecting root page names. 184 185 elif arg == "--root": 186 l = root_pagenames 187 continue 188 189 # Switch to collecting theme names. 190 191 elif arg == "--theme": 192 l = theme_names 193 continue 194 195 # Collect options and arguments. 196 197 else: 198 l.append(arg) 199 200 # Collect multiple mappings. 201 202 if l is mappings: 203 continue 204 205 # Collect filenames normally. 206 207 l = filenames 208 209 format = formats and formats[0] or "html" 210 input_dir = getvalue(input_dirs) 211 output_dir = getvalue(output_dirs) 212 213 # Define metadata. 214 215 metadata = Metadata({ 216 "input_context" : input_dir and \ 217 getvalue(input_dir_types, "directory") or \ 218 "standalone", 219 "input_encoding" : getvalue(input_encodings), 220 "input_filename" : input_dir, 221 "input_separator" : getvalue(input_page_seps), 222 "link_format" : format, 223 "mapping" : getmapping(mappings), 224 "output_context" : output_dir and "directory" or "standalone", 225 "output_encoding" : getvalue(output_encodings), 226 "output_format" : format, 227 "output_filename" : output_dir, 228 "root_pagename" : getvalue(root_pagenames, "FrontPage"), 229 "theme_name" : not fragment and \ 230 "%s.%s" % (getvalue(theme_names, "default"), format) or None, 231 }) 232 233 # Define the input context and theme. 234 235 input = metadata.get_input() 236 theme = metadata.get_theme() 237 238 # Treat filenames as pagenames if an input directory is indicated and if no 239 # pagenames are explicitly specified. 240 241 if input_dir: 242 if pagenames: 243 print >>sys.stderr, message_explicit_pagenames 244 sys.exit(1) 245 246 if all: 247 if filenames: 248 print >>sys.stderr, message_all_with_filenames 249 sys.exit(1) 250 else: 251 filenames = input.all() 252 253 pagenames = filenames 254 filenames = [] 255 256 # Open each file or page, parse the content, serialise the document. 257 258 for pagename, filename in map(None, pagenames, filenames): 259 260 # Define a pagename if missing. 261 262 pagename = pagename or split(filename)[-1] 263 metadata.set("pagename", pagename) 264 265 # Read either from a filename or using a pagename. 266 267 if filename: 268 pagetext = input.readfile(filename) 269 else: 270 pagetext = input.readpage(pagename) 271 272 # Parse the page content. 273 274 p = make_parser(metadata) 275 d = parse(pagetext, p) 276 277 if macros: 278 p.evaluate_macros() 279 280 # Show a document tree for debugging purposes, if requested. 281 282 if tree: 283 print d.prettyprint() 284 continue 285 286 # Otherwise, serialise the document. 287 288 # Obtain a serialiser using the configuration. 289 290 serialiser = make_serialiser(metadata) 291 outtext = serialise(d, serialiser) 292 293 # With a theme, apply it to the text. 294 295 if theme: 296 outtext = theme.apply(outtext) 297 298 # If reading from a file, show the result. Otherwise, write to the 299 # output context. 300 301 output = metadata.get_output() 302 303 if not output.can_write(): 304 print outtext 305 else: 306 output.writepage(outtext, pagename) 307 print >>sys.stderr, pagename 308 309 # Install any theme resources. 310 311 if theme: 312 theme.install_resources() 313 314 def show_help(progname): 315 316 "Show the help text." 317 318 print >>sys.stderr, help_text % progname 319 320 help_text = """\ 321 Usage: %s [ <options> ] ( --all | <filename>... ) 322 323 Input options: 324 325 --all Detect all document files in the specified input directory 326 --input-dir Indicate an input directory containing document files 327 --input-dir-type Indicate the type of input directory involved 328 (default: directory) 329 --input-encoding Indicate the character encoding used in document files 330 --input-page-sep Indicate the separator used in filenames to encode 331 hierarchical relationships (subpages and descendant pages) 332 --pagename Indicate the page name corresponding to an indicated 333 filename, with each successive instance of this option 334 corresponding to each successive filename instance 335 336 Output options: 337 338 --format Indicate the format to be used for serialised documents 339 (default: html) 340 --fragment Indicates that an output fragment, not an entire document, 341 is to be generated, skipping any theming activities 342 --output-dir Indicate an output directory to contain serialised document 343 files 344 --output-encoding Indicate the character encoding used in serialised document 345 files 346 --theme Indicate a theme for serialised documents, typically 347 requiring an output directory to be useful 348 --tree Produce a document tree representation on standard output 349 instead of generating output files 350 351 Configuration options: 352 353 --macros Perform macro evaluation/expansion before serialising 354 documents 355 --mapping Indicate a name and corresponding URL to be used to 356 translate interwiki links 357 --root Indicate the root page name to be used 358 (default: FrontPage) 359 """ 360 361 if __name__ == "__main__": 362 main() 363 364 # vim: tabstop=4 expandtab shiftwidth=4