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 # Option values. 82 83 l = filenames = [] 84 formats = [] 85 input_dir_types = [] 86 input_dirs = [] 87 input_encodings = [] 88 input_page_seps = [] 89 mappings = [] 90 output_dirs = [] 91 output_encodings = [] 92 theme_names = [] 93 pagenames = [] 94 root_pagenames = [] 95 96 # Flags. 97 98 all = False 99 fragment = False 100 macros = False 101 tree = False 102 103 for arg in args: 104 105 # Detect tree output. 106 107 if arg == "--tree": 108 tree = True 109 110 # Detect macro evaluation. 111 112 elif arg == "--macros": 113 macros = True 114 115 # Detect all documents. 116 117 elif arg == "--all": 118 all = True 119 120 # Detect fragment output (if serialising). 121 122 elif arg == "--fragment": 123 fragment = True 124 125 # Switch to collecting formats. 126 127 elif arg == "--format": 128 l = formats 129 continue 130 131 # Switch to collecting input locations. 132 133 elif arg == "--input-dir": 134 l = input_dirs 135 continue 136 137 # Switch to collecting input context types. 138 139 elif arg == "--input-dir-type": 140 l = input_dir_types 141 continue 142 143 # Switch to collecting input encodings. 144 145 elif arg == "--input-encoding": 146 l = input_encodings 147 continue 148 149 # Switch to collecting input page hierarchy separators. 150 151 elif arg == "--input-page-sep": 152 l = input_page_seps 153 continue 154 155 # Switch to collecting mappings. 156 157 elif arg == "--mapping": 158 l = mappings 159 continue 160 161 # Switch to collecting output locations. 162 163 elif arg == "--output-dir": 164 l = output_dirs 165 continue 166 167 # Switch to collecting output encodings. 168 169 elif arg == "--output-encoding": 170 l = output_encodings 171 continue 172 173 # Switch to collecting page names. 174 175 elif arg == "--pagename": 176 l = pagenames 177 continue 178 179 # Switch to collecting root page names. 180 181 elif arg == "--root": 182 l = root_pagenames 183 continue 184 185 # Switch to collecting theme names. 186 187 elif arg == "--theme": 188 l = theme_names 189 continue 190 191 # Collect options and arguments. 192 193 else: 194 l.append(arg) 195 196 # Collect multiple mappings. 197 198 if l is mappings: 199 continue 200 201 # Collect filenames normally. 202 203 l = filenames 204 205 format = formats and formats[0] or "html" 206 input_dir = getvalue(input_dirs) 207 output_dir = getvalue(output_dirs) 208 209 # Define metadata. 210 211 metadata = Metadata({ 212 "input_context" : input_dir and \ 213 getvalue(input_dir_types, "directory") or \ 214 "standalone", 215 "input_encoding" : getvalue(input_encodings), 216 "input_filename" : input_dir, 217 "input_separator" : getvalue(input_page_seps), 218 "link_format" : format, 219 "mapping" : getmapping(mappings), 220 "output_context" : output_dir and "directory" or "standalone", 221 "output_encoding" : getvalue(output_encodings), 222 "output_format" : format, 223 "output_filename" : output_dir, 224 "root_pagename" : getvalue(root_pagenames, "FrontPage"), 225 "theme_name" : not fragment and \ 226 "%s.%s" % (getvalue(theme_names, "default"), format) or None, 227 }) 228 229 # Define the input context and theme. 230 231 input = metadata.get_input() 232 theme = metadata.get_theme() 233 234 # Treat filenames as pagenames if an input directory is indicated and if no 235 # pagenames are explicitly specified. 236 237 if input_dir: 238 if pagenames: 239 print >>sys.stderr, message_explicit_pagenames 240 sys.exit(1) 241 242 if all: 243 if filenames: 244 print >>sys.stderr, message_all_with_filenames 245 sys.exit(1) 246 else: 247 filenames = input.all() 248 249 pagenames = filenames 250 filenames = [] 251 252 # Open each file or page, parse the content, serialise the document. 253 254 for pagename, filename in map(None, pagenames, filenames): 255 256 # Define a pagename if missing. 257 258 pagename = pagename or split(filename)[-1] 259 metadata.set("pagename", pagename) 260 261 # Read either from a filename or using a pagename. 262 263 if filename: 264 pagetext = input.readfile(filename) 265 else: 266 pagetext = input.readpage(pagename) 267 268 # Parse the page content. 269 270 p = make_parser(metadata) 271 d = parse(pagetext, p) 272 273 if macros: 274 p.evaluate_macros() 275 276 # Show a document tree for debugging purposes, if requested. 277 278 if tree: 279 print d.prettyprint() 280 continue 281 282 # Otherwise, serialise the document. 283 284 # Obtain a serialiser using the configuration. 285 286 serialiser = make_serialiser(metadata) 287 outtext = serialise(d, serialiser) 288 289 # With a theme, apply it to the text. 290 291 if theme: 292 outtext = theme.apply(outtext) 293 294 # If reading from a file, show the result. Otherwise, write to the 295 # output context. 296 297 output = metadata.get_output() 298 299 if not output.can_write(): 300 print outtext 301 else: 302 output.writepage(outtext, pagename) 303 print >>sys.stderr, pagename 304 305 # Install any theme resources. 306 307 if theme: 308 theme.install_resources() 309 310 if __name__ == "__main__": 311 main() 312 313 # vim: tabstop=4 expandtab shiftwidth=4