import sysclass TokenException(Exception): passclass ParseException(Exception): passclass Tokenizer: def __init__(self, f): """ Init the tokenizer on the given file, may throw an exception. """ self.i = 0 # Read the file, coud be streamed as well self.f = f self.data = file(f).read() self.state = "linestart" def get_next_token(self): """ Returns the next token in the file. A token is one of: < = > name Returns None when the end of file is reached and beyond """ while self.i < len(self.data): char = self.data[self.i] # Skip leading spaces (which are really trailing spaces after a token # Newline is important so special case it if char.isspace() and not char == "\n": self.i += 1 continue if self.state == "linestart" and char == "#": # Eat all the comment line while self.i < len(self.data) and self.data[self.i] != "\n": self.i += 1 self.i += 1 #position on first char after newline continue # At this point, we can only have a token, read it, and return it # The method updates the self.i to point to the new char to read next next_token = self.read_token() # Wait ! We got a newline here, so back to start again.. if next_token == "\n": self.state = "linestart" continue return next_token def read_token(self): """ Return the token, self.i must point to the first char in this token. self.i is updated to point just after the returned token Returned token is one of: < = > \n name """ char = self.data[self.i] if char == "<" or char == "=" or char == ">" or char == "\n": self.i += 1 return char elif char == "\"": # Here we assume proper quoting closing = self.data[self.i+1:].find("\"") if closing == -1: raise TokenException("Couldn't find a proper closing quote in %s" % self.f) token = self.data[self.i+1:self.i+1+closing] self.i += closing+2 #Next char is just *after* the closing " return token else: # Token can just be a string now.. token = "" # We eat all chars until one of "=<>" or whitespace is found while self.i < len(self.data) and not self.data[self.i].isspace() and not self.data[self.i] in "=><": token += self.data[self.i] self.i += 1 return tokenclass MozillaParser: def __init__(self, f): """ Init the parse, no exception here """ self.f = f self.infos = { "search": {}, "input": {}, } def parse(self): """ """ tokenizer = Tokenizer(self.f) n = state = None # We load the two first tokens, whch should be ["<", "search"] tokens = [tokenizer.get_next_token(), tokenizer.get_next_token()] while tokens[0] != None: # Retreive the next state, the number of tokens to read next, and any discarded tokens # by the handler, which should be handled on next iteration state, n, rest = self._handle_token(state, tokens) # If n == None, we finished parsing if n == None: break # Read the requested number of tokens, but count the ones that were rejected first tokens = rest for i in range(n - len(rest)): tokens.append(tokenizer.get_next_token()) def _handle_token(self, state, tokens): if state == None and (tokens == ["<", "search"] or tokens == ["<", "SEARCH"]): return "search", 3, [] elif state == None: raise ParseException("File %s does not begin with <search" % self.f) # Read key=value pairs and store them in the infos["search"] dict if state == "search" and tokens[1] == "=": self.infos["search"][tokens[0]] = tokens[2] return "search", 3, [] # We reached the end of <Search tag, now in theory come the <input> tags if state == "search" or state == "anotherinput" and tokens == [">", "<", "input"]: return "input", 6, [] elif state == "search": raise ParseException("Expecting <input after <search section in file %s" % self.f) # This parses the <input fields, taking each time 6 tokens # First triplet is name=value, second triplet is value=val # Special case for lone "user" second triplets, and store that in infos["input"] dict if state == "input" and tokens[1] == "=": if tokens[3] == "user": self.infos["input"][tokens[2]] = "%s" if tokens[4] != "=": return "anotherinput", 3, tokens[4:] else: self.infos["input"][tokens[2]] = tokens[5] return "anotherinput", 3, [] # Here we stop processing, cause we are no longer in <input sections, and what comes after isn't interesting return None, None, Nonefor f in sys.argv[1:]: moz = MozillaParser(f) print "Reading file: ", f moz.parse() print "Search:" for key, value in moz.infos["search"].items(): print "\t", key, ":", value print "Input:" for key, value in moz.infos["input"].items(): print "\t", key, ":", value