#!/usr/bin/python ######################################################################## # # Copyright 2002, Tero Tilus  # # This software is distributed under the terms of the gnu general # Public License  and WITHOUT # ANY WARRANTY. # ######################################################################## # Splits given html file into separate files on h2 (or optionally on # h3 too). Fixes links from given list of files pointing to file # which is to be split. Run without argumets to get help. import re import sys import sgmllib import string def str_strip(str): return string.strip(string.replace(str, '\012', '')) # Receives data with calls to feed() and splits it along nextPart() # -calls. Originally written for cHTMLSplitter -class. # # If you want pre- and posfix every part with head and foot you gotta # set them with makeHead(prefix) and makeFoot(postfix) and call # renderParts() before accesing parts -list. Optionally you can # access parts with renderPart(index). class cReceiver: def __init__(self, op): self.reset() self.setOutputter(op) self.nextprevlinks = 1 def reset(self): self.onpart = 0 self.parts = [''] self.head = '' self.foot = '' self.rendered = 0 def setOutputter(self, op): self.outputter = op def feed(self, s): self.parts[self.onpart] = self.parts[self.onpart] + s def nextPart(self): self.onpart = self.onpart + 1 self.parts.append('') def getPart(self, i): return self.parts[i] def setPart(self, i, text): self.parts[i] = text def renderPart(self, i): if ( self.nextprevlinks ): prev = i-1 next = i+1 links = '
\n' # Linkki edelliseen sivuun (jos on) if ( prev >= 0 ): links = links + ' [edellinen] ' else: links = links + ' [edellinen] ' # Linkki sisällysluetteloon links = links + ' [sisällys] ' # Linkki seuraavaan sivuun (jos on) if ( next < (self.countParts()-1) ): links = links + ' [seuraava] ' else: links = links + ' [seuraava] ' links = links + '\n
\n' return self.head + links + self.parts[i] + links + self.foot return self.head + self.parts[i] + self.foot def countParts(self): return len(self.parts) # Pre- and postfix non-empty parts with header and footer def renderParts(self): new_parts = [] for i in range(self.countParts()): if (self.parts[i] <> ''): new_parts.append(self.renderPart(i)) self.parts = new_parts def makeHead(self, s=''): if (s == ''): self.head = self.parts[self.onpart] self.parts[self.onpart] = ''; else: self.head = s def makeFoot(self, s=''): if (s == ''): self.foot = self.parts[self.onpart] self.parts[self.onpart] = ''; else: self.foot = s # Writes list of strings to series of files named # originalname.x.originalextension, where x is index. # # Originally written for cHTMLSplitter class cListOutputter: def __init__(self, list=[], origname='', tocname='index.html'): self.setOriginalName(origname) self.setTocName(tocname) self.setList(list) def setOriginalName(self, oname): from string import rfind # Set default original name if (oname == ''): oname = 'file' # Find basename and extension lastdotindex = rfind(oname, '.') if (-1 <> lastdotindex): # Dot found self.basename = oname[:lastdotindex] self.extension = oname[lastdotindex+1:] else: # Dot not found, use origname as basename self.basename = oname self.extension = '' def getOriginalName(self): return self.basename + '.' + self.extension def setTocName(self, tname): self.tocname = tname # get what would be the name of n:th item in outputlist. def getNthName(self, n): return self.basename + '.' + `n` + '.' + self.extension # Set list to output def setList(self, list): self.stringlist = list # Table of contents def setTOC(self, toc): self.toc = toc def putOut(self, TOC): # TOC if ( TOC ): outfile = open(self.basename + '-' + self.tocname, 'w') outfile.write(self.toc) outfile.close() # Content files for i in range(len(self.stringlist)): outfile = open(self.getNthName(i), 'w') outfile.write(self.stringlist[i]) outfile.close() class cAbstractHTMLFilter(sgmllib.SGMLParser): def out(self, s): self.buffer = self.buffer + s def __init__(self): sgmllib.SGMLParser.__init__(self) self.buffer = '' def resetAbstractHTMLFilter(self): sgmllib.SGMLParser.reset(self) self.buffer = '' # Default handlers. # Their job is to (effectively) do nothin. def handle_data(self, data): self.out(data) def unknown_starttag(self, tag, attrs): attrstr = '' for name, value in attrs: attrstr = attrstr + ' ' + name + '="' + value + '"' self.out('<'+tag+attrstr+'>') def unknown_endtag(self, tag): self.out('') def handle_entityref(self, name): self.out('&'+name+';') def handle_charref(self, name): self.out('&#'+name+';') def handle_comment(self, data): self.out('') def getBuffer(self): return self.buffer class cHTMLLinkRewriter(cAbstractHTMLFilter): def __init__(self, anchortable={}, htmlstring=''): cAbstractHTMLFilter.__init__(self) self.setAnchorTable(anchortable) self.feed(htmlstring) self.re_label = re.compile('(.*)\#(.*)') def setAnchorTable(self, at): self.anchortable = at def setSrcName(self, obname): self.source_name = obname def start_a(self, attrs): for index in range(len(attrs)): # Browse variables name, value = attrs[index] if ( name == 'href' ): # Has 'href' -> is a link label = self.re_label.findall(value) if ( len(label) > 0 ): # Uri has a label (sumthn.html#a_label) in it. # Only get the bare filename. This guessing can # fail if there's files with same name in # different directories. htmlfile = string.split(label[0][0],'/')[-1] # Get the label label = label[0][1] # Rewrite perfectly matching links, all having # only label in url and all when source is unknown if ( self.anchortable.has_key(label) and ( (self.source_name == htmlfile) or (htmlfile == '') or (self.source_name == '') ) ): # Relocated label -> must rewrite link attrs[index] = ('href', self.anchortable[label]+'#'+label) # Write corrected tag self.unknown_starttag('a', attrs) def resetHTMLLinkRewriter(self): self.resetAbstractHTMLFilter() class cHTMLSplitter(cAbstractHTMLFilter): def __init__(self, outputter=None, reciever=None): cAbstractHTMLFilter.__init__(self) # Outputter interface if (outputter == None): self.put = cListOutputter() else: self.put = outputter # Reciever interface if (reciever == None): self.rec = cReceiver(self.put) else: self.rec = reciever self.rec.setOutputter(self.put) self.lrw = cHTMLLinkRewriter() self.lrw.setSrcName(self.put.getOriginalName()) self.headerdone = 0 self.footstack = [] self.anchors = {} self.toc = [] self.tmpBuffer = '' self.remData = 0 self.re_idparts = re.compile('([0-9]*)[-_\.]?(.*)') self.outputOn = 1 self.fixfiles = [] def addFixFile(self, fixfilename): self.fixfiles.append(fixfilename) def rememberData(self): self.remData = 1 def forgetData(self): self.tmpBuffer = '' self.remData = 0 def resetHTMLSplitter(self): resetAbstractHTMLFilter(self) self.rec.reset() self.headerdone = 0 self.footstack = [] def handle_data(self, data): self.out(data) # Where's the output directed def out(self, s): if ( self.remData and s[0] <> '<'): self.tmpBuffer = self.tmpBuffer + s if ( self.outputOn ): self.rec.feed(s) # Splitpoint handlers def start_h1(self, attrs): self.handleSplitPoint('h1', attrs) def end_h1(self): self.completeSplitPoint('h1', 0) def start_h2(self, attrs): self.handleSplitPoint('h2', attrs) def end_h2(self): self.completeSplitPoint('h2', 1) def start_h3(self, attrs): if ( self.split_on_h3 ): self.handleSplitPoint('h3', attrs) else: self.unknown_starttag('h3', attrs) def end_h3(self): if ( self.split_on_h3 ): self.completeSplitPoint('h3', 2) else: self.unknown_endtag('h3') def handleSplitPoint(self, tag, attrs): self.rememberData() if not self.headerdone: self.footstack = self.stack self.rec.makeHead() self.headerdone = 1 else: self.rec.nextPart() self.unknown_starttag(tag, attrs) def completeSplitPoint(self, tag, toclevel=-1): if ( toclevel > -1 ): self.toc.append((toclevel, str_strip(self.tmpBuffer), \ self.put.getNthName(self.rec.onpart))) self.forgetData() self.unknown_endtag(tag) # Endpoint handler # Must have start_* to make end_* work. def start_body(self, attrs): self.unknown_starttag('body', attrs) def end_body(self): self.rec.nextPart() if len(self.footstack) > 0: self.footstack.reverse() for tag in self.footstack: self.unknown_endtag(tag) # Replace separator images with
def start_img(self, attrs): for index in range(len(attrs)): if (attrs[index][0] == 'src' and attrs[index][1][-6:] == '-1.gif'): self.unknown_starttag('hr', []) return self.unknown_starttag('img', attrs) # Otherwise stack gets flooded with ''s self.unknown_endtag('img') def end_img(self): # Send ''s to /dev/null pass # When splitting html links to anchors in same file maybe split to # separate files. This requires rewriting of link hrefs. New # locations of anchors must be listed. def start_a(self, attrs): for index in range(len(attrs)): name, value = attrs[index] if (name == 'name'): a_in_file = self.put.getNthName(self.rec.onpart) # Gotta make sure we don't have multiple anchors with # same name. This may occur when combining several # html files to single stream. if ( self.anchors.has_key(value) ): # Duplicate found, resolve sys.stderr.write('Duplicate anchor name "'+value+'"... ') idparts = self.re_idparts.findall(value) prefix_number = int('0'+idparts[0][0]) base_value = idparts[0][1] i = 0 while ( 1 ): fixed_value = `prefix_number+i`+'-'+base_value i = i+1 if ( not self.anchors.has_key(fixed_value) ): attrs[index] = ('name', fixed_value) sys.stderr.write('fixed to "'+fixed_value+'"\012') break if ( i>100000 ): sys.stderr.write('can\'t fix! Links may not work!\012') fixed_value = value break else: # Not a duplicate, may go fixed_value = value # Add to list of named anchors self.anchors[fixed_value] = a_in_file # Output (fixed) link self.unknown_starttag('a', attrs) # Done def close(self): sgmllib.SGMLParser.close(self) self.rec.makeFoot() # Fix links for generated files self.lrw.setAnchorTable(self.anchors) for i in range(self.rec.countParts()): self.lrw.resetHTMLLinkRewriter() self.lrw.feed(self.rec.getPart(i)) self.lrw.close() self.rec.setPart(i, self.lrw.getBuffer()) # Optionally fix links of other files too for fixfname in self.fixfiles: self.lrw.resetHTMLLinkRewriter() # Read and fix fixfile = open(fixfname, 'r') self.lrw.feed(fixfile.read()) self.lrw.close() fixfile.close() # Write fixed fixfile = open(fixfname, 'w') fixfile.write(self.lrw.getBuffer()) fixfile.close() self.rec.renderParts() self.put.setList(self.rec.parts) def getHTMLToc(self): html = '' for entry in self.toc: html = html + '
'+ \ ''+entry[1]+'
\012' return '
\n'+html+'
\n' def putOut(self, TOC): self.put.setTOC(self.rec.head + self.getHTMLToc() + self.rec.foot) self.put.putOut(TOC) def split(html): return p.rec.parts def main(args): fname = '' # Do we split on h3 too h3 = 0 if ( args.count('-h3') ): h3 = 1 args.remove('-h3') # Do we print table of contents TOC = 0 if ( args.count('-t') ): TOC = 1 args.remove('-t') # Do we have enough arguments if ( len(args) > 0 ): fname = args[0] args = args[1:] else: # No, print usage print 'Usage: ' print ' ' + (string.split(sys.argv[0],'/')[-1]) + ' [opts] splitfile.html [fixfile.html ...]' print 'Options:' print ' -h3 Split on h3 too (in addition to h1 and h2)' print ' -t Write TOC to index.html' return 0 # Outputter interface for splitter o = cListOutputter() o.setOriginalName(fname) o.setTocName('index.html') # Splitter p = cHTMLSplitter(o) p.split_on_h3 = h3 for fixfname in args: p.addFixFile(fixfname) # Feed html p.feed(open(fname).read()) p.close() p.putOut(TOC) main(sys.argv[1:])