home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_823 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  48.1 KB  |  1,559 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import generators
  5. __author__ = 'Leonard Richardson (leonardr@segfault.org)'
  6. __version__ = '3.0.5'
  7. __copyright__ = 'Copyright (c) 2004-2007 Leonard Richardson'
  8. __license__ = 'New-style BSD'
  9. from calibre.ebooks.sgmllib import SGMLParser, SGMLParseError
  10. import codecs
  11. import types
  12. import re
  13. import calibre.ebooks.sgmllib as sgmllib
  14. from htmlentitydefs import name2codepoint
  15. sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
  16. DEFAULT_OUTPUT_ENCODING = 'utf-8'
  17.  
  18. class PageElement:
  19.     
  20.     def setup(self, parent = None, previous = None):
  21.         self.parent = parent
  22.         self.previous = previous
  23.         self.next = None
  24.         self.previousSibling = None
  25.         self.nextSibling = None
  26.         if self.parent and self.parent.contents:
  27.             self.previousSibling = self.parent.contents[-1]
  28.             self.previousSibling.nextSibling = self
  29.         
  30.  
  31.     
  32.     def replaceWith(self, replaceWith):
  33.         oldParent = self.parent
  34.         myIndex = self.parent.contents.index(self)
  35.         if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:
  36.             index = self.parent.contents.index(replaceWith)
  37.             if index and index < myIndex:
  38.                 myIndex = myIndex - 1
  39.             
  40.         
  41.         self.extract()
  42.         oldParent.insert(myIndex, replaceWith)
  43.  
  44.     
  45.     def extract(self):
  46.         if self.parent:
  47.             idx = None
  48.             for i, x in enumerate(self.parent.contents):
  49.                 if x is self:
  50.                     idx = i
  51.                     break
  52.                     continue
  53.             
  54.             if idx is not None:
  55.                 self.parent.contents.pop(idx)
  56.             
  57.         
  58.         lastChild = self._lastRecursiveChild()
  59.         nextElement = lastChild.next
  60.         if self.previous:
  61.             self.previous.next = nextElement
  62.         
  63.         if nextElement:
  64.             nextElement.previous = self.previous
  65.         
  66.         self.previous = None
  67.         lastChild.next = None
  68.         self.parent = None
  69.         if self.previousSibling:
  70.             self.previousSibling.nextSibling = self.nextSibling
  71.         
  72.         if self.nextSibling:
  73.             self.nextSibling.previousSibling = self.previousSibling
  74.         
  75.         self.previousSibling = None
  76.         self.nextSibling = None
  77.  
  78.     
  79.     def _lastRecursiveChild(self):
  80.         lastChild = self
  81.         while hasattr(lastChild, 'contents') and lastChild.contents:
  82.             lastChild = lastChild.contents[-1]
  83.         return lastChild
  84.  
  85.     
  86.     def insert(self, position, newChild):
  87.         if (isinstance(newChild, basestring) or isinstance(newChild, unicode)) and not isinstance(newChild, NavigableString):
  88.             newChild = NavigableString(newChild)
  89.         
  90.         position = min(position, len(self.contents))
  91.         if hasattr(newChild, 'parent') and newChild.parent != None:
  92.             if newChild.parent == self:
  93.                 index = self.find(newChild)
  94.                 if index and index < position:
  95.                     position = position - 1
  96.                 
  97.             
  98.             newChild.extract()
  99.         
  100.         newChild.parent = self
  101.         previousChild = None
  102.         if position == 0:
  103.             newChild.previousSibling = None
  104.             newChild.previous = self
  105.         else:
  106.             previousChild = self.contents[position - 1]
  107.             newChild.previousSibling = previousChild
  108.             newChild.previousSibling.nextSibling = newChild
  109.             newChild.previous = previousChild._lastRecursiveChild()
  110.         if newChild.previous:
  111.             newChild.previous.next = newChild
  112.         
  113.         newChildsLastElement = newChild._lastRecursiveChild()
  114.         if position >= len(self.contents):
  115.             newChild.nextSibling = None
  116.             parent = self
  117.             parentsNextSibling = None
  118.             while not parentsNextSibling:
  119.                 parentsNextSibling = parent.nextSibling
  120.                 parent = parent.parent
  121.                 if not parent:
  122.                     break
  123.                     continue
  124.             if parentsNextSibling:
  125.                 newChildsLastElement.next = parentsNextSibling
  126.             else:
  127.                 newChildsLastElement.next = None
  128.         else:
  129.             nextChild = self.contents[position]
  130.             newChild.nextSibling = nextChild
  131.             if newChild.nextSibling:
  132.                 newChild.nextSibling.previousSibling = newChild
  133.             
  134.             newChildsLastElement.next = nextChild
  135.         if newChildsLastElement.next:
  136.             newChildsLastElement.next.previous = newChildsLastElement
  137.         
  138.         self.contents.insert(position, newChild)
  139.  
  140.     
  141.     def append(self, tag):
  142.         self.insert(len(self.contents), tag)
  143.  
  144.     
  145.     def findNext(self, name = None, attrs = { }, text = None, **kwargs):
  146.         return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
  147.  
  148.     
  149.     def findAllNext(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  150.         return self._findAll(name, attrs, text, limit, self.nextGenerator)
  151.  
  152.     
  153.     def findNextSibling(self, name = None, attrs = { }, text = None, **kwargs):
  154.         return self._findOne(self.findNextSiblings, name, attrs, text, **kwargs)
  155.  
  156.     
  157.     def findNextSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  158.         return self._findAll(name, attrs, text, limit, self.nextSiblingGenerator, **kwargs)
  159.  
  160.     fetchNextSiblings = findNextSiblings
  161.     
  162.     def findPrevious(self, name = None, attrs = { }, text = None, **kwargs):
  163.         return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
  164.  
  165.     
  166.     def findAllPrevious(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  167.         return self._findAll(name, attrs, text, limit, self.previousGenerator, **kwargs)
  168.  
  169.     fetchPrevious = findAllPrevious
  170.     
  171.     def findPreviousSibling(self, name = None, attrs = { }, text = None, **kwargs):
  172.         return self._findOne(self.findPreviousSiblings, name, attrs, text, **kwargs)
  173.  
  174.     
  175.     def findPreviousSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  176.         return self._findAll(name, attrs, text, limit, self.previousSiblingGenerator, **kwargs)
  177.  
  178.     fetchPreviousSiblings = findPreviousSiblings
  179.     
  180.     def findParent(self, name = None, attrs = { }, **kwargs):
  181.         r = None
  182.         l = self.findParents(name, attrs, 1)
  183.         if l:
  184.             r = l[0]
  185.         
  186.         return r
  187.  
  188.     
  189.     def findParents(self, name = None, attrs = { }, limit = None, **kwargs):
  190.         return self._findAll(name, attrs, None, limit, self.parentGenerator, **kwargs)
  191.  
  192.     fetchParents = findParents
  193.     
  194.     def _findOne(self, method, name, attrs, text, **kwargs):
  195.         r = None
  196.         l = method(name, attrs, text, 1, **kwargs)
  197.         if l:
  198.             r = l[0]
  199.         
  200.         return r
  201.  
  202.     
  203.     def _findAll(self, name, attrs, text, limit, generator, **kwargs):
  204.         if isinstance(name, SoupStrainer):
  205.             strainer = name
  206.         else:
  207.             strainer = SoupStrainer(name, attrs, text, **kwargs)
  208.         results = ResultSet(strainer)
  209.         g = generator()
  210.         while True:
  211.             
  212.             try:
  213.                 i = g.next()
  214.             except StopIteration:
  215.                 break
  216.  
  217.             if i:
  218.                 found = strainer.search(i)
  219.                 if found:
  220.                     results.append(found)
  221.                     if limit and len(results) >= limit:
  222.                         break
  223.                     
  224.                 
  225.             found
  226.         return results
  227.  
  228.     
  229.     def nextGenerator(self):
  230.         i = self
  231.         while i:
  232.             i = i.next
  233.             yield i
  234.  
  235.     
  236.     def nextSiblingGenerator(self):
  237.         i = self
  238.         while i:
  239.             i = i.nextSibling
  240.             yield i
  241.  
  242.     
  243.     def previousGenerator(self):
  244.         i = self
  245.         while i:
  246.             i = i.previous
  247.             yield i
  248.  
  249.     
  250.     def previousSiblingGenerator(self):
  251.         i = self
  252.         while i:
  253.             i = i.previousSibling
  254.             yield i
  255.  
  256.     
  257.     def parentGenerator(self):
  258.         i = self
  259.         while i:
  260.             i = i.parent
  261.             yield i
  262.  
  263.     
  264.     def substituteEncoding(self, str, encoding = None):
  265.         if not encoding:
  266.             pass
  267.         encoding = 'utf-8'
  268.         return str.replace('%SOUP-ENCODING%', encoding)
  269.  
  270.     
  271.     def toEncoding(self, s, encoding = None):
  272.         if isinstance(s, unicode):
  273.             if encoding:
  274.                 s = s.encode(encoding)
  275.             
  276.         elif isinstance(s, str):
  277.             if encoding:
  278.                 s = s.encode(encoding)
  279.             else:
  280.                 s = unicode(s)
  281.         elif encoding:
  282.             s = self.toEncoding(str(s), encoding)
  283.         else:
  284.             s = unicode(s)
  285.         return s
  286.  
  287.  
  288.  
  289. class NavigableString(unicode, PageElement):
  290.     
  291.     def __getnewargs__(self):
  292.         return (NavigableString.__str__(self),)
  293.  
  294.     
  295.     def __getattr__(self, attr):
  296.         if attr == 'string':
  297.             return self
  298.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
  299.  
  300.     
  301.     def __unicode__(self):
  302.         return unicode(str(self), DEFAULT_OUTPUT_ENCODING)
  303.  
  304.     
  305.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  306.         if encoding:
  307.             return self.encode(encoding)
  308.         return self
  309.  
  310.  
  311.  
  312. class CData(NavigableString):
  313.     
  314.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  315.         return '<![CDATA[%s]]>' % NavigableString.__str__(self, encoding)
  316.  
  317.  
  318.  
  319. class ProcessingInstruction(NavigableString):
  320.     
  321.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  322.         output = self
  323.         if '%SOUP-ENCODING%' in output:
  324.             output = self.substituteEncoding(output, encoding)
  325.         
  326.         return '<?%s?>' % self.toEncoding(output, encoding)
  327.  
  328.  
  329.  
  330. class Comment(NavigableString):
  331.     
  332.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  333.         return '<!--%s-->' % NavigableString.__str__(self, encoding)
  334.  
  335.  
  336.  
  337. class Declaration(NavigableString):
  338.     
  339.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  340.         return '<!%s>' % NavigableString.__str__(self, encoding)
  341.  
  342.  
  343.  
  344. class Tag(PageElement):
  345.     
  346.     def _invert(h):
  347.         i = { }
  348.         for k, v in h.items():
  349.             i[v] = k
  350.         
  351.         return i
  352.  
  353.     XML_ENTITIES_TO_SPECIAL_CHARS = {
  354.         'apos': "'",
  355.         'quot': '"',
  356.         'amp': '&',
  357.         'lt': '<',
  358.         'gt': '>' }
  359.     XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)
  360.     
  361.     def _convertEntities(self, match):
  362.         x = match.group(1)
  363.         if self.convertHTMLEntities and x in name2codepoint:
  364.             return unichr(name2codepoint[x])
  365.         if x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
  366.             if self.convertXMLEntities:
  367.                 return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
  368.             return u'&%s;' % x
  369.         x in self.XML_ENTITIES_TO_SPECIAL_CHARS
  370.         if len(x) > 0 and x[0] == '#':
  371.             if len(x) > 1 and x[1] == 'x':
  372.                 return unichr(int(x[2:], 16))
  373.             return unichr(int(x[1:]))
  374.         x[0] == '#'
  375.         if self.escapeUnrecognizedEntities:
  376.             return u'&%s;' % x
  377.         return u'&%s;' % x
  378.  
  379.     
  380.     def __init__(self, parser, name, attrs = None, parent = None, previous = None):
  381.         self.parserClass = parser.__class__
  382.         self.isSelfClosing = parser.isSelfClosingTag(name)
  383.         self.name = name
  384.         if attrs == None:
  385.             attrs = []
  386.         
  387.         self.attrs = attrs
  388.         self.contents = []
  389.         self.setup(parent, previous)
  390.         self.hidden = False
  391.         self.containsSubstitutions = False
  392.         self.convertHTMLEntities = parser.convertHTMLEntities
  393.         self.convertXMLEntities = parser.convertXMLEntities
  394.         self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
  395.         
  396.         convert = lambda .0: (k, val) = .0(k, re.sub('&(#\\d+|#x[0-9a-fA-F]+|\\w+);', self._convertEntities, val))
  397.         self.attrs = map(convert, self.attrs)
  398.  
  399.     
  400.     def get(self, key, default = None):
  401.         return self._getAttrMap().get(key, default)
  402.  
  403.     
  404.     def has_key(self, key):
  405.         return self._getAttrMap().has_key(key)
  406.  
  407.     
  408.     def __getitem__(self, key):
  409.         return self._getAttrMap()[key]
  410.  
  411.     
  412.     def __iter__(self):
  413.         return iter(self.contents)
  414.  
  415.     
  416.     def __len__(self):
  417.         return len(self.contents)
  418.  
  419.     
  420.     def __contains__(self, x):
  421.         return x in self.contents
  422.  
  423.     
  424.     def __nonzero__(self):
  425.         return True
  426.  
  427.     
  428.     def __setitem__(self, key, value):
  429.         self._getAttrMap()
  430.         self.attrMap[key] = value
  431.         found = False
  432.         for i in range(0, len(self.attrs)):
  433.             if self.attrs[i][0] == key:
  434.                 self.attrs[i] = (key, value)
  435.                 found = True
  436.                 continue
  437.         
  438.         if not found:
  439.             self.attrs.append((key, value))
  440.         
  441.         self._getAttrMap()[key] = value
  442.  
  443.     
  444.     def __delitem__(self, key):
  445.         for item in self.attrs:
  446.             if item[0] == key:
  447.                 self.attrs.remove(item)
  448.             
  449.             self._getAttrMap()
  450.             if self.attrMap.has_key(key):
  451.                 del self.attrMap[key]
  452.                 continue
  453.         
  454.  
  455.     
  456.     def __call__(self, *args, **kwargs):
  457.         return apply(self.findAll, args, kwargs)
  458.  
  459.     
  460.     def __getattr__(self, tag):
  461.         if len(tag) > 3 and tag.rfind('Tag') == len(tag) - 3:
  462.             return self.find(tag[:-3])
  463.         if tag.find('__') != 0:
  464.             return self.find(tag)
  465.         raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
  466.  
  467.     
  468.     def __eq__(self, other):
  469.         if not hasattr(other, 'name') and not hasattr(other, 'attrs') and not hasattr(other, 'contents') and self.name != other.name and self.attrs != other.attrs or len(self) != len(other):
  470.             return False
  471.         for i in range(0, len(self.contents)):
  472.             if self.contents[i] != other.contents[i]:
  473.                 return False
  474.         
  475.         return True
  476.  
  477.     
  478.     def __ne__(self, other):
  479.         return not (self == other)
  480.  
  481.     
  482.     def __repr__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  483.         return self.__str__(encoding)
  484.  
  485.     
  486.     def __unicode__(self):
  487.         return self.__str__(None)
  488.  
  489.     BARE_AMPERSAND_OR_BRACKET = re.compile('([<>]|' + '&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)' + ')')
  490.     
  491.     def _sub_entity(self, x):
  492.         return '&' + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ';'
  493.  
  494.     
  495.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  496.         encodedName = self.toEncoding(self.name, encoding)
  497.         attrs = []
  498.         if self.attrs:
  499.             for key, val in self.attrs:
  500.                 fmt = '%s="%s"'
  501.                 if isString(val):
  502.                     if self.containsSubstitutions and '%SOUP-ENCODING%' in val:
  503.                         val = self.substituteEncoding(val, encoding)
  504.                     
  505.                     if '"' in val:
  506.                         fmt = "%s='%s'"
  507.                         if "'" in val:
  508.                             val = val.replace("'", '&squot;')
  509.                         
  510.                     
  511.                     val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)
  512.                 
  513.                 attrs.append(fmt % (self.toEncoding(key, encoding), self.toEncoding(val, encoding)))
  514.             
  515.         
  516.         close = ''
  517.         closeTag = ''
  518.         if self.isSelfClosing:
  519.             close = ' /'
  520.         else:
  521.             closeTag = '</%s>' % encodedName
  522.         (indentTag, indentContents) = (0, 0)
  523.         if prettyPrint:
  524.             indentTag = indentLevel
  525.             space = ' ' * (indentTag - 1)
  526.             indentContents = indentTag + 1
  527.         
  528.         contents = self.renderContents(encoding, prettyPrint, indentContents)
  529.         if self.hidden:
  530.             s = contents
  531.         else:
  532.             s = []
  533.             attributeString = ''
  534.             if attrs:
  535.                 attributeString = ' ' + ' '.join(attrs)
  536.             
  537.             if prettyPrint:
  538.                 s.append(space)
  539.             
  540.             s.append('<%s%s%s>' % (encodedName, attributeString, close))
  541.             if prettyPrint:
  542.                 s.append('\n')
  543.             
  544.             s.append(contents)
  545.             if prettyPrint and contents and contents[-1] != '\n':
  546.                 s.append('\n')
  547.             
  548.             if prettyPrint and closeTag:
  549.                 s.append(space)
  550.             
  551.             s.append(closeTag)
  552.             if prettyPrint and closeTag and self.nextSibling:
  553.                 s.append('\n')
  554.             
  555.             s = ''.join(s)
  556.         return s
  557.  
  558.     
  559.     def prettify(self, encoding = DEFAULT_OUTPUT_ENCODING):
  560.         return self.__str__(encoding, True)
  561.  
  562.     
  563.     def renderContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  564.         s = []
  565.         for c in self:
  566.             text = None
  567.             if isinstance(c, NavigableString):
  568.                 text = c.__str__(encoding)
  569.             elif isinstance(c, Tag):
  570.                 s.append(c.__str__(encoding, prettyPrint, indentLevel))
  571.             
  572.             if text and prettyPrint:
  573.                 text = text.strip()
  574.             
  575.             if text:
  576.                 if prettyPrint:
  577.                     s.append(' ' * (indentLevel - 1))
  578.                 
  579.                 s.append(text)
  580.                 if prettyPrint:
  581.                     s.append('\n')
  582.                 
  583.             prettyPrint
  584.         
  585.         return ''.join(s)
  586.  
  587.     
  588.     def find(self, name = None, attrs = { }, recursive = True, text = None, **kwargs):
  589.         r = None
  590.         l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
  591.         if l:
  592.             r = l[0]
  593.         
  594.         return r
  595.  
  596.     findChild = find
  597.     
  598.     def findAll(self, name = None, attrs = { }, recursive = True, text = None, limit = None, **kwargs):
  599.         generator = self.recursiveChildGenerator
  600.         if not recursive:
  601.             generator = self.childGenerator
  602.         
  603.         return self._findAll(name, attrs, text, limit, generator, **kwargs)
  604.  
  605.     findChildren = findAll
  606.     first = find
  607.     fetch = findAll
  608.     
  609.     def fetchText(self, text = None, recursive = True, limit = None):
  610.         return self.findAll(text = text, recursive = recursive, limit = limit)
  611.  
  612.     
  613.     def firstText(self, text = None, recursive = True):
  614.         return self.find(text = text, recursive = recursive)
  615.  
  616.     
  617.     def _getAttrMap(self):
  618.         if not getattr(self, 'attrMap'):
  619.             self.attrMap = { }
  620.             for key, value in self.attrs:
  621.                 self.attrMap[key] = value
  622.             
  623.         
  624.         return self.attrMap
  625.  
  626.     
  627.     def childGenerator(self):
  628.         for i in range(0, len(self.contents)):
  629.             yield self.contents[i]
  630.         
  631.         raise StopIteration
  632.  
  633.     
  634.     def recursiveChildGenerator(self):
  635.         stack = [
  636.             (self, 0)]
  637.         while stack:
  638.             (tag, start) = stack.pop()
  639.             if isinstance(tag, Tag):
  640.                 for i in range(start, len(tag.contents)):
  641.                     a = tag.contents[i]
  642.                     yield a
  643.                     if isinstance(a, Tag) and tag.contents:
  644.                         if i < len(tag.contents) - 1:
  645.                             stack.append((tag, i + 1))
  646.                         
  647.                         stack.append((a, 0))
  648.                         break
  649.                         continue
  650.                 
  651.         raise StopIteration
  652.  
  653.  
  654.  
  655. class SoupStrainer:
  656.     
  657.     def __init__(self, name = None, attrs = { }, text = None, **kwargs):
  658.         self.name = name
  659.         if isString(attrs):
  660.             kwargs['class'] = attrs
  661.             attrs = None
  662.         
  663.         if kwargs:
  664.             if attrs:
  665.                 attrs = attrs.copy()
  666.                 attrs.update(kwargs)
  667.             else:
  668.                 attrs = kwargs
  669.         
  670.         self.attrs = attrs
  671.         self.text = text
  672.  
  673.     
  674.     def __str__(self):
  675.         if self.text:
  676.             return self.text
  677.         return '%s|%s' % (self.name, self.attrs)
  678.  
  679.     
  680.     def searchTag(self, markupName = None, markupAttrs = { }):
  681.         found = None
  682.         markup = None
  683.         if isinstance(markupName, Tag):
  684.             markup = markupName
  685.             markupAttrs = markup
  686.         
  687.         if callable(self.name):
  688.             pass
  689.         callFunctionWithTagData = not isinstance(markupName, Tag)
  690.         if not not (self.name) and callFunctionWithTagData:
  691.             if (markup or self._matches(markup, self.name) or not markup) and self._matches(markupName, self.name):
  692.                 if callFunctionWithTagData:
  693.                     match = self.name(markupName, markupAttrs)
  694.                 else:
  695.                     match = True
  696.                     markupAttrMap = None
  697.                     for attr, matchAgainst in self.attrs.items():
  698.                         if not markupAttrMap:
  699.                             if hasattr(markupAttrs, 'get'):
  700.                                 markupAttrMap = markupAttrs
  701.                             else:
  702.                                 markupAttrMap = { }
  703.                                 for k, v in markupAttrs:
  704.                                     markupAttrMap[k] = v
  705.                                 
  706.                         
  707.                         attrValue = markupAttrMap.get(attr)
  708.                         if not self._matches(attrValue, matchAgainst):
  709.                             match = False
  710.                             break
  711.                             continue
  712.                     
  713.                 if match:
  714.                     if markup:
  715.                         found = markup
  716.                     else:
  717.                         found = markupName
  718.                 
  719.             
  720.         return found
  721.  
  722.     
  723.     def search(self, markup):
  724.         found = None
  725.         if isList(markup) and not isinstance(markup, Tag):
  726.             for element in markup:
  727.                 if isinstance(element, NavigableString) and self.search(element):
  728.                     found = element
  729.                     break
  730.                     continue
  731.             
  732.         elif isinstance(markup, Tag):
  733.             if not self.text:
  734.                 found = self.searchTag(markup)
  735.             
  736.         elif isinstance(markup, NavigableString) or isString(markup):
  737.             if self._matches(markup, self.text):
  738.                 found = markup
  739.             
  740.         else:
  741.             raise Exception, "I don't know how to match against a %s" % markup.__class__
  742.         return isString(markup)
  743.  
  744.     
  745.     def _matches(self, markup, matchAgainst):
  746.         result = False
  747.         if matchAgainst == True and type(matchAgainst) == types.BooleanType:
  748.             result = markup != None
  749.         elif callable(matchAgainst):
  750.             result = matchAgainst(markup)
  751.         elif isinstance(markup, Tag):
  752.             markup = markup.name
  753.         
  754.         if markup and not isString(markup):
  755.             markup = unicode(markup)
  756.         
  757.         if hasattr(matchAgainst, 'match'):
  758.             if markup:
  759.                 pass
  760.             result = matchAgainst.search(markup)
  761.         elif isList(matchAgainst):
  762.             result = markup in matchAgainst
  763.         elif hasattr(matchAgainst, 'items'):
  764.             result = markup.has_key(matchAgainst)
  765.         elif matchAgainst and isString(markup):
  766.             if isinstance(markup, unicode):
  767.                 matchAgainst = unicode(matchAgainst)
  768.             else:
  769.                 matchAgainst = str(matchAgainst)
  770.         
  771.         if not result:
  772.             result = matchAgainst == markup
  773.         
  774.         return result
  775.  
  776.  
  777.  
  778. class ResultSet(list):
  779.     
  780.     def __init__(self, source):
  781.         list.__init__([])
  782.         self.source = source
  783.  
  784.  
  785.  
  786. def isList(l):
  787.     if not hasattr(l, '__iter__'):
  788.         pass
  789.     return type(l) in (types.ListType, types.TupleType)
  790.  
  791.  
  792. def isString(s):
  793.     
  794.     try:
  795.         if not isinstance(s, unicode):
  796.             pass
  797.         return isinstance(s, basestring)
  798.     except NameError:
  799.         return isinstance(s, str)
  800.  
  801.  
  802.  
  803. def buildTagMap(default, *args):
  804.     built = { }
  805.     for portion in args:
  806.         if hasattr(portion, 'items'):
  807.             for k, v in portion.items():
  808.                 built[k] = v
  809.             
  810.         if isList(portion):
  811.             for k in portion:
  812.                 built[k] = default
  813.             
  814.         built[portion] = default
  815.     
  816.     return built
  817.  
  818.  
  819. class BeautifulStoneSoup(Tag, SGMLParser):
  820.     SELF_CLOSING_TAGS = { }
  821.     NESTABLE_TAGS = { }
  822.     RESET_NESTING_TAGS = { }
  823.     QUOTE_TAGS = { }
  824.     MARKUP_MASSAGE = [
  825.         (re.compile('(<[^<>]*)/>'), (lambda x: x.group(1) + ' />')),
  826.         (re.compile('<!\\s+([^<>]*)>'), (lambda x: '<!' + x.group(1) + '>'))]
  827.     ROOT_TAG_NAME = u'[document]'
  828.     HTML_ENTITIES = 'html'
  829.     XML_ENTITIES = 'xml'
  830.     XHTML_ENTITIES = 'xhtml'
  831.     ALL_ENTITIES = XHTML_ENTITIES
  832.     STRIP_ASCII_SPACES = {
  833.         9: None,
  834.         10: None,
  835.         12: None,
  836.         13: None,
  837.         32: None }
  838.     
  839.     def __init__(self, markup = '', parseOnlyThese = None, fromEncoding = None, markupMassage = True, smartQuotesTo = XML_ENTITIES, convertEntities = None, selfClosingTags = None):
  840.         self.parseOnlyThese = parseOnlyThese
  841.         self.fromEncoding = fromEncoding
  842.         self.smartQuotesTo = smartQuotesTo
  843.         self.convertEntities = convertEntities
  844.         if self.convertEntities:
  845.             self.smartQuotesTo = None
  846.             if convertEntities == self.HTML_ENTITIES:
  847.                 self.convertXMLEntities = False
  848.                 self.convertHTMLEntities = True
  849.                 self.escapeUnrecognizedEntities = True
  850.             elif convertEntities == self.XHTML_ENTITIES:
  851.                 self.convertXMLEntities = True
  852.                 self.convertHTMLEntities = True
  853.                 self.escapeUnrecognizedEntities = False
  854.             elif convertEntities == self.XML_ENTITIES:
  855.                 self.convertXMLEntities = True
  856.                 self.convertHTMLEntities = False
  857.                 self.escapeUnrecognizedEntities = False
  858.             
  859.         else:
  860.             self.convertXMLEntities = False
  861.             self.convertHTMLEntities = False
  862.             self.escapeUnrecognizedEntities = False
  863.         self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
  864.         SGMLParser.__init__(self)
  865.         if hasattr(markup, 'read'):
  866.             markup = markup.read()
  867.         
  868.         self.markup = markup
  869.         self.markupMassage = markupMassage
  870.         
  871.         try:
  872.             self._feed()
  873.         except StopParsing:
  874.             pass
  875.  
  876.         self.markup = None
  877.  
  878.     
  879.     def convert_charref(self, name):
  880.         
  881.         try:
  882.             n = int(name)
  883.         except ValueError:
  884.             return None
  885.  
  886.         if n <= n:
  887.             pass
  888.         elif not n <= 127:
  889.             return None
  890.         return self.convert_codepoint(n)
  891.  
  892.     
  893.     def _feed(self, inDocumentEncoding = None):
  894.         markup = self.markup
  895.         if isinstance(markup, unicode):
  896.             if not hasattr(self, 'originalEncoding'):
  897.                 self.originalEncoding = None
  898.             
  899.         else:
  900.             (markup, self.originalEncoding) = chardet.xml_to_unicode(markup)
  901.         if markup:
  902.             if self.markupMassage:
  903.                 if not isList(self.markupMassage):
  904.                     self.markupMassage = self.MARKUP_MASSAGE
  905.                 
  906.                 for fix, m in self.markupMassage:
  907.                     markup = fix.sub(m, markup)
  908.                 
  909.                 del self.markupMassage
  910.                 self.markup = markup
  911.             
  912.         
  913.         self.reset()
  914.         SGMLParser.feed(self, markup)
  915.         self.endData()
  916.         while self.currentTag.name != self.ROOT_TAG_NAME:
  917.             self.popTag()
  918.  
  919.     
  920.     def __getattr__(self, methodName):
  921.         if methodName.find('start_') == 0 and methodName.find('end_') == 0 or methodName.find('do_') == 0:
  922.             return SGMLParser.__getattr__(self, methodName)
  923.         if methodName.find('__') != 0:
  924.             return Tag.__getattr__(self, methodName)
  925.         raise AttributeError
  926.  
  927.     
  928.     def isSelfClosingTag(self, name):
  929.         if not self.SELF_CLOSING_TAGS.has_key(name):
  930.             pass
  931.         return self.instanceSelfClosingTags.has_key(name)
  932.  
  933.     
  934.     def reset(self):
  935.         Tag.__init__(self, self, self.ROOT_TAG_NAME)
  936.         self.hidden = 1
  937.         SGMLParser.reset(self)
  938.         self.currentData = []
  939.         self.currentTag = None
  940.         self.tagStack = []
  941.         self.quoteStack = []
  942.         self.pushTag(self)
  943.  
  944.     
  945.     def popTag(self):
  946.         self.tagStack.pop()
  947.         if len(self.currentTag.contents) == 1 and isinstance(self.currentTag.contents[0], NavigableString):
  948.             self.currentTag.string = self.currentTag.contents[0]
  949.         
  950.         if self.tagStack:
  951.             self.currentTag = self.tagStack[-1]
  952.         
  953.         return self.currentTag
  954.  
  955.     
  956.     def pushTag(self, tag):
  957.         if self.currentTag:
  958.             self.currentTag.contents.append(tag)
  959.         
  960.         self.tagStack.append(tag)
  961.         self.currentTag = self.tagStack[-1]
  962.  
  963.     
  964.     def endData(self, containerClass = NavigableString):
  965.         if self.currentData:
  966.             currentData = ''.join(self.currentData)
  967.             if not currentData.translate(self.STRIP_ASCII_SPACES):
  968.                 if '\n' in currentData:
  969.                     currentData = '\n'
  970.                 else:
  971.                     currentData = ' '
  972.             
  973.             self.currentData = []
  974.             if self.parseOnlyThese and len(self.tagStack) <= 1:
  975.                 if not (self.parseOnlyThese.text) or not self.parseOnlyThese.search(currentData):
  976.                     return None
  977.                 o = containerClass(currentData)
  978.                 o.setup(self.currentTag, self.previous)
  979.                 if self.previous:
  980.                     self.previous.next = o
  981.                 
  982.             self.previous = o
  983.             self.currentTag.contents.append(o)
  984.         
  985.  
  986.     
  987.     def _popToTag(self, name, inclusivePop = True):
  988.         if name == self.ROOT_TAG_NAME:
  989.             return None
  990.         numPops = 0
  991.         mostRecentTag = None
  992.         for i in range(len(self.tagStack) - 1, 0, -1):
  993.             if name == self.tagStack[i].name:
  994.                 numPops = len(self.tagStack) - i
  995.                 break
  996.                 continue
  997.             name == self.ROOT_TAG_NAME
  998.         
  999.         if not inclusivePop:
  1000.             numPops = numPops - 1
  1001.         
  1002.         for i in range(0, numPops):
  1003.             mostRecentTag = self.popTag()
  1004.         
  1005.         return mostRecentTag
  1006.  
  1007.     
  1008.     def _smartPop(self, name):
  1009.         nestingResetTriggers = self.NESTABLE_TAGS.get(name)
  1010.         isNestable = nestingResetTriggers != None
  1011.         isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
  1012.         popTo = None
  1013.         inclusive = True
  1014.         for i in range(len(self.tagStack) - 1, 0, -1):
  1015.             p = self.tagStack[i]
  1016.             if (not p or p.name == name) and not isNestable:
  1017.                 popTo = name
  1018.                 break
  1019.             
  1020.             if (nestingResetTriggers != None or p.name in nestingResetTriggers or nestingResetTriggers == None) and isResetNesting and self.RESET_NESTING_TAGS.has_key(p.name):
  1021.                 popTo = p.name
  1022.                 inclusive = False
  1023.                 break
  1024.             
  1025.             p = p.parent
  1026.         
  1027.         if popTo:
  1028.             self._popToTag(popTo, inclusive)
  1029.         
  1030.  
  1031.     
  1032.     def unknown_starttag(self, name, attrs, selfClosing = 0):
  1033.         if self.quoteStack:
  1034.             attrs = ''.join(map((lambda .0: (x, y) = .0' %s="%s"' % (x, y)), attrs))
  1035.             self.handle_data('<%s%s>' % (name, attrs))
  1036.             return None
  1037.         self.endData()
  1038.         if not self.isSelfClosingTag(name) and not selfClosing:
  1039.             self._smartPop(name)
  1040.         
  1041.         if self.parseOnlyThese and len(self.tagStack) <= 1:
  1042.             if self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs):
  1043.                 return None
  1044.             tag = Tag(self, name, attrs, self.currentTag, self.previous)
  1045.             if self.previous:
  1046.                 self.previous.next = tag
  1047.             
  1048.         self.previous = tag
  1049.         self.pushTag(tag)
  1050.         if selfClosing or self.isSelfClosingTag(name):
  1051.             self.popTag()
  1052.         
  1053.         if name in self.QUOTE_TAGS:
  1054.             self.quoteStack.append(name)
  1055.             self.literal = 1
  1056.         
  1057.         return tag
  1058.  
  1059.     
  1060.     def unknown_endtag(self, name):
  1061.         if self.quoteStack and self.quoteStack[-1] != name:
  1062.             self.handle_data('</%s>' % name)
  1063.             return None
  1064.         self.endData()
  1065.         self._popToTag(name)
  1066.         if self.quoteStack and self.quoteStack[-1] == name:
  1067.             self.quoteStack.pop()
  1068.             self.literal = len(self.quoteStack) > 0
  1069.         
  1070.  
  1071.     
  1072.     def handle_data(self, data):
  1073.         self.currentData.append(data)
  1074.  
  1075.     
  1076.     def _toStringSubclass(self, text, subclass):
  1077.         self.endData()
  1078.         self.handle_data(text)
  1079.         self.endData(subclass)
  1080.  
  1081.     
  1082.     def handle_pi(self, text):
  1083.         if text[:3] == 'xml':
  1084.             text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
  1085.         
  1086.         self._toStringSubclass(text, ProcessingInstruction)
  1087.  
  1088.     
  1089.     def handle_comment(self, text):
  1090.         self._toStringSubclass(text, Comment)
  1091.  
  1092.     
  1093.     def handle_charref(self, ref):
  1094.         if self.convertEntities:
  1095.             if ref.lower().startswith('x'):
  1096.                 ref = int(ref[1:], 16)
  1097.             
  1098.             
  1099.             try:
  1100.                 data = unichr(int(ref))
  1101.             except ValueError:
  1102.                 data = u''
  1103.             except:
  1104.                 None<EXCEPTION MATCH>ValueError
  1105.             
  1106.  
  1107.         None<EXCEPTION MATCH>ValueError
  1108.         data = '&#%s;' % ref
  1109.         self.handle_data(data)
  1110.  
  1111.     
  1112.     def handle_entityref(self, ref):
  1113.         data = None
  1114.         if self.convertHTMLEntities:
  1115.             
  1116.             try:
  1117.                 data = unichr(name2codepoint[ref])
  1118.             except KeyError:
  1119.                 pass
  1120.             except:
  1121.                 None<EXCEPTION MATCH>KeyError
  1122.             
  1123.  
  1124.         None<EXCEPTION MATCH>KeyError
  1125.         if not data and self.convertXMLEntities:
  1126.             data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
  1127.         
  1128.         if not data and self.convertHTMLEntities and not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
  1129.             data = '&%s' % ref
  1130.         
  1131.         if not data:
  1132.             data = '&%s;' % ref
  1133.         
  1134.         self.handle_data(data)
  1135.  
  1136.     
  1137.     def handle_decl(self, data):
  1138.         self._toStringSubclass(data, Declaration)
  1139.  
  1140.     
  1141.     def parse_declaration(self, i):
  1142.         j = None
  1143.         if self.rawdata[i:i + 9] == '<![CDATA[':
  1144.             k = self.rawdata.find(']]>', i)
  1145.             if k == -1:
  1146.                 k = len(self.rawdata)
  1147.             
  1148.             data = self.rawdata[i + 9:k]
  1149.             j = k + 3
  1150.             self._toStringSubclass(data, CData)
  1151.         else:
  1152.             
  1153.             try:
  1154.                 j = SGMLParser.parse_declaration(self, i)
  1155.             except SGMLParseError:
  1156.                 toHandle = self.rawdata[i:]
  1157.                 self.handle_data(toHandle)
  1158.                 j = i + len(toHandle)
  1159.  
  1160.         return j
  1161.  
  1162.  
  1163.  
  1164. class BeautifulSoup(BeautifulStoneSoup):
  1165.     
  1166.     def __init__(self, *args, **kwargs):
  1167.         if not kwargs.has_key('smartQuotesTo'):
  1168.             kwargs['smartQuotesTo'] = self.HTML_ENTITIES
  1169.         
  1170.         BeautifulStoneSoup.__init__(self, *args, **kwargs)
  1171.  
  1172.     SELF_CLOSING_TAGS = buildTagMap(None, [
  1173.         'br',
  1174.         'hr',
  1175.         'input',
  1176.         'img',
  1177.         'meta',
  1178.         'spacer',
  1179.         'link',
  1180.         'frame',
  1181.         'base'])
  1182.     QUOTE_TAGS = {
  1183.         'script': None,
  1184.         'textarea': None }
  1185.     NESTABLE_INLINE_TAGS = [
  1186.         'span',
  1187.         'font',
  1188.         'q',
  1189.         'object',
  1190.         'bdo',
  1191.         'sub',
  1192.         'sup',
  1193.         'center']
  1194.     NESTABLE_BLOCK_TAGS = [
  1195.         'blockquote',
  1196.         'div',
  1197.         'fieldset',
  1198.         'ins',
  1199.         'del']
  1200.     NESTABLE_LIST_TAGS = {
  1201.         'ol': [],
  1202.         'ul': [],
  1203.         'li': [
  1204.             'ul',
  1205.             'ol'],
  1206.         'dl': [],
  1207.         'dd': [
  1208.             'dl'],
  1209.         'dt': [
  1210.             'dl'] }
  1211.     NESTABLE_TABLE_TAGS = {
  1212.         'table': [],
  1213.         'tr': [
  1214.             'table',
  1215.             'tbody',
  1216.             'tfoot',
  1217.             'thead'],
  1218.         'td': [
  1219.             'tr'],
  1220.         'th': [
  1221.             'tr'],
  1222.         'thead': [
  1223.             'table'],
  1224.         'tbody': [
  1225.             'table'],
  1226.         'tfoot': [
  1227.             'table'] }
  1228.     NON_NESTABLE_BLOCK_TAGS = [
  1229.         'address',
  1230.         'form',
  1231.         'p',
  1232.         'pre']
  1233.     RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', NON_NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1234.     NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1235.     CHARSET_RE = re.compile('((^|;)\\s*charset=)([^;]*)')
  1236.     
  1237.     def start_meta(self, attrs):
  1238.         httpEquiv = None
  1239.         contentType = None
  1240.         contentTypeIndex = None
  1241.         tagNeedsEncodingSubstitution = False
  1242.         for i in range(0, len(attrs)):
  1243.             (key, value) = attrs[i]
  1244.             key = key.lower()
  1245.             if key == 'http-equiv':
  1246.                 httpEquiv = value
  1247.                 continue
  1248.             if key == 'content':
  1249.                 contentType = value
  1250.                 contentTypeIndex = i
  1251.                 continue
  1252.         
  1253.         if httpEquiv and contentType:
  1254.             match = self.CHARSET_RE.search(contentType)
  1255.             if match:
  1256.                 if getattr(self, 'declaredHTMLEncoding') or self.originalEncoding == self.fromEncoding:
  1257.                     newAttr = self.CHARSET_RE.sub((lambda match: match.group(1) + '%SOUP-ENCODING%'), value)
  1258.                     attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], newAttr)
  1259.                     tagNeedsEncodingSubstitution = True
  1260.                 else:
  1261.                     newCharset = match.group(3)
  1262.                     if newCharset and newCharset != self.originalEncoding:
  1263.                         self.declaredHTMLEncoding = newCharset
  1264.                         self._feed(self.declaredHTMLEncoding)
  1265.                         raise StopParsing
  1266.                     newCharset != self.originalEncoding
  1267.             
  1268.         
  1269.         tag = self.unknown_starttag('meta', attrs)
  1270.         if tag and tagNeedsEncodingSubstitution:
  1271.             tag.containsSubstitutions = True
  1272.         
  1273.  
  1274.  
  1275.  
  1276. class StopParsing(Exception):
  1277.     pass
  1278.  
  1279.  
  1280. class ICantBelieveItsBeautifulSoup(BeautifulSoup):
  1281.     I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = [
  1282.         'em',
  1283.         'big',
  1284.         'i',
  1285.         'small',
  1286.         'tt',
  1287.         'abbr',
  1288.         'acronym',
  1289.         'strong',
  1290.         'cite',
  1291.         'code',
  1292.         'dfn',
  1293.         'kbd',
  1294.         'samp',
  1295.         'strong',
  1296.         'var',
  1297.         'b',
  1298.         'big']
  1299.     I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = [
  1300.         'noscript']
  1301.     NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
  1302.  
  1303.  
  1304. class MinimalSoup(BeautifulSoup):
  1305.     RESET_NESTING_TAGS = buildTagMap('noscript')
  1306.     NESTABLE_TAGS = { }
  1307.  
  1308.  
  1309. class BeautifulSOAP(BeautifulStoneSoup):
  1310.     
  1311.     def popTag(self):
  1312.         if len(self.tagStack) > 1:
  1313.             tag = self.tagStack[-1]
  1314.             parent = self.tagStack[-2]
  1315.             parent._getAttrMap()
  1316.             if isinstance(tag, Tag) and len(tag.contents) == 1 and isinstance(tag.contents[0], NavigableString) and not parent.attrMap.has_key(tag.name):
  1317.                 parent[tag.name] = tag.contents[0]
  1318.             
  1319.         
  1320.         BeautifulStoneSoup.popTag(self)
  1321.  
  1322.  
  1323.  
  1324. class RobustXMLParser(BeautifulStoneSoup):
  1325.     pass
  1326.  
  1327.  
  1328. class RobustHTMLParser(BeautifulSoup):
  1329.     pass
  1330.  
  1331.  
  1332. class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
  1333.     pass
  1334.  
  1335.  
  1336. class RobustInsanelyWackAssHTMLParser(MinimalSoup):
  1337.     pass
  1338.  
  1339.  
  1340. class SimplifyingSOAPParser(BeautifulSOAP):
  1341.     pass
  1342.  
  1343. import calibre.ebooks.chardet as chardet
  1344.  
  1345. class UnicodeDammit:
  1346.     CHARSET_ALIASES = {
  1347.         'macintosh': 'mac-roman',
  1348.         'x-sjis': 'shift-jis' }
  1349.     
  1350.     def __init__(self, markup, overrideEncodings = [], smartQuotesTo = 'xml'):
  1351.         (self.markup, documentEncoding, sniffedEncoding) = self._detectEncoding(markup)
  1352.         self.smartQuotesTo = smartQuotesTo
  1353.         self.triedEncodings = []
  1354.         if markup == '' or isinstance(markup, unicode):
  1355.             self.originalEncoding = None
  1356.             self.unicode = unicode(markup)
  1357.             return None
  1358.         u = None
  1359.         for proposedEncoding in overrideEncodings:
  1360.             u = self._convertFrom(proposedEncoding)
  1361.             if u:
  1362.                 break
  1363.                 continue
  1364.             isinstance(markup, unicode)
  1365.         
  1366.         if not u:
  1367.             for proposedEncoding in (documentEncoding, sniffedEncoding):
  1368.                 u = self._convertFrom(proposedEncoding)
  1369.                 if u:
  1370.                     break
  1371.                     continue
  1372.             
  1373.         
  1374.         if not u and chardet and not isinstance(self.markup, unicode):
  1375.             u = self._convertFrom(chardet.detect(self.markup)['encoding'])
  1376.         
  1377.         if not u:
  1378.             for proposed_encoding in ('utf-8', 'windows-1252'):
  1379.                 u = self._convertFrom(proposed_encoding)
  1380.                 if u:
  1381.                     break
  1382.                     continue
  1383.             
  1384.         
  1385.         self.unicode = u
  1386.         if not u:
  1387.             self.originalEncoding = None
  1388.         
  1389.  
  1390.     
  1391.     def _subMSChar(self, orig):
  1392.         sub = self.MS_CHARS.get(orig)
  1393.         if type(sub) == types.TupleType:
  1394.             if self.smartQuotesTo == 'xml':
  1395.                 sub = '&#x%s;' % sub[1]
  1396.             else:
  1397.                 sub = '&%s;' % sub[0]
  1398.         
  1399.         return sub
  1400.  
  1401.     
  1402.     def _convertFrom(self, proposed):
  1403.         proposed = self.find_codec(proposed)
  1404.         if not proposed or proposed in self.triedEncodings:
  1405.             return None
  1406.         self.triedEncodings.append(proposed)
  1407.         markup = self.markup
  1408.         if self.smartQuotesTo and proposed.lower() in ('windows-1252', 'iso-8859-1', 'iso-8859-2'):
  1409.             markup = (re.compile('([\x80-\x9f])').sub,)((lambda x: self._subMSChar(x.group(1))), markup)
  1410.         
  1411.         
  1412.         try:
  1413.             u = self._toUnicode(markup, proposed)
  1414.             self.markup = u
  1415.             self.originalEncoding = proposed
  1416.         except Exception:
  1417.             return None
  1418.  
  1419.         return self.markup
  1420.  
  1421.     
  1422.     def _toUnicode(self, data, encoding):
  1423.         if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  1424.             encoding = 'utf-16be'
  1425.             data = data[2:]
  1426.         elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  1427.             encoding = 'utf-16le'
  1428.             data = data[2:]
  1429.         elif data[:3] == '\xef\xbb\xbf':
  1430.             encoding = 'utf-8'
  1431.             data = data[3:]
  1432.         elif data[:4] == '\x00\x00\xfe\xff':
  1433.             encoding = 'utf-32be'
  1434.             data = data[4:]
  1435.         elif data[:4] == '\xff\xfe\x00\x00':
  1436.             encoding = 'utf-32le'
  1437.             data = data[4:]
  1438.         
  1439.         newdata = unicode(data, encoding)
  1440.         return newdata
  1441.  
  1442.     
  1443.     def _detectEncoding(self, xml_data):
  1444.         xml_encoding = None
  1445.         sniffed_xml_encoding = None
  1446.         
  1447.         try:
  1448.             if xml_data[:4] == 'Lo\xa7\x94':
  1449.                 xml_data = self._ebcdic_to_ascii(xml_data)
  1450.             elif xml_data[:4] == '\x00<\x00?':
  1451.                 sniffed_xml_encoding = 'utf-16be'
  1452.             elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  1453.                 sniffed_xml_encoding = 'utf-16be'
  1454.             elif xml_data[:4] == '<\x00?\x00':
  1455.                 sniffed_xml_encoding = 'utf-16le'
  1456.             elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  1457.                 sniffed_xml_encoding = 'utf-16le'
  1458.             elif xml_data[:4] == '\x00\x00\x00<':
  1459.                 sniffed_xml_encoding = 'utf-32be'
  1460.             elif xml_data[:4] == '<\x00\x00\x00':
  1461.                 sniffed_xml_encoding = 'utf-32le'
  1462.             elif xml_data[:4] == '\x00\x00\xfe\xff':
  1463.                 sniffed_xml_encoding = 'utf-32be'
  1464.             elif xml_data[:4] == '\xff\xfe\x00\x00':
  1465.                 sniffed_xml_encoding = 'utf-32le'
  1466.             elif xml_data[:3] == '\xef\xbb\xbf':
  1467.                 sniffed_xml_encoding = 'utf-8'
  1468.             else:
  1469.                 sniffed_xml_encoding = 'ascii'
  1470.             xml_encoding_match = re.compile('^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>').match(xml_data)
  1471.             if xml_encoding_match is None:
  1472.                 xml_encoding_match = re.compile('<meta.*?content=[\\\'"].*?charset=(\\S+).*?[\\\'"]', re.IGNORECASE).search(xml_data)
  1473.         except:
  1474.             xml_encoding_match = None
  1475.  
  1476.         if xml_encoding_match:
  1477.             xml_encoding = xml_encoding_match.groups()[0].lower()
  1478.             if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  1479.                 xml_encoding = sniffed_xml_encoding
  1480.             
  1481.         
  1482.         return (xml_data, xml_encoding, sniffed_xml_encoding)
  1483.  
  1484.     
  1485.     def find_codec(self, charset):
  1486.         if not self._codec(self.CHARSET_ALIASES.get(charset, charset)):
  1487.             if not charset or self._codec(charset.replace('-', '')):
  1488.                 if not charset or self._codec(charset.replace('-', '_')):
  1489.                     pass
  1490.         return charset
  1491.  
  1492.     
  1493.     def _codec(self, charset):
  1494.         if not charset:
  1495.             return charset
  1496.         codec = None
  1497.         
  1498.         try:
  1499.             codecs.lookup(charset)
  1500.             codec = charset
  1501.         except (LookupError, ValueError):
  1502.             charset
  1503.             charset
  1504.         except:
  1505.             charset
  1506.  
  1507.         return codec
  1508.  
  1509.     EBCDIC_TO_ASCII_MAP = None
  1510.     
  1511.     def _ebcdic_to_ascii(self, s):
  1512.         c = self.__class__
  1513.         if not c.EBCDIC_TO_ASCII_MAP:
  1514.             emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  1515.             import string
  1516.             c.EBCDIC_TO_ASCII_MAP = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  1517.         
  1518.         return s.translate(c.EBCDIC_TO_ASCII_MAP)
  1519.  
  1520.     MS_CHARS = {
  1521.         '\x80': ('euro', '20AC'),
  1522.         '\x81': ' ',
  1523.         '\x82': ('sbquo', '201A'),
  1524.         '\x83': ('fnof', '192'),
  1525.         '\x84': ('bdquo', '201E'),
  1526.         '\x85': ('hellip', '2026'),
  1527.         '\x86': ('dagger', '2020'),
  1528.         '\x87': ('Dagger', '2021'),
  1529.         '\x88': ('circ', '2C6'),
  1530.         '\x89': ('permil', '2030'),
  1531.         '\x8a': ('Scaron', '160'),
  1532.         '\x8b': ('lsaquo', '2039'),
  1533.         '\x8c': ('OElig', '152'),
  1534.         '\x8d': '?',
  1535.         '\x8e': ('#x17D', '17D'),
  1536.         '\x8f': '?',
  1537.         '\x90': '?',
  1538.         '\x91': ('lsquo', '2018'),
  1539.         '\x92': ('rsquo', '2019'),
  1540.         '\x93': ('ldquo', '201C'),
  1541.         '\x94': ('rdquo', '201D'),
  1542.         '\x95': ('bull', '2022'),
  1543.         '\x96': ('ndash', '2013'),
  1544.         '\x97': ('mdash', '2014'),
  1545.         '\x98': ('tilde', '2DC'),
  1546.         '\x99': ('trade', '2122'),
  1547.         '\x9a': ('scaron', '161'),
  1548.         '\x9b': ('rsaquo', '203A'),
  1549.         '\x9c': ('oelig', '153'),
  1550.         '\x9d': '?',
  1551.         '\x9e': ('#x17E', '17E'),
  1552.         '\x9f': ('Yuml', '') }
  1553.  
  1554. if __name__ == '__main__':
  1555.     import sys
  1556.     soup = BeautifulSoup(sys.stdin.read())
  1557.     print soup.prettify()
  1558.  
  1559.