'parse a format string and extract the field info
if no string given, reverts to default for class
will set self.fields to be a list of (name,type,spec) tuples
self.fieldnames to a list of fieldnames
and self.fieldmap to a list of key=in value=out mappings
format string can also map in field to out field with %(in:out)spectype
or specify an explicit out type with %(in:out)specintype:outtype
(note this breaks compatibility with text formatting,
but useful for db or other output modules)
a typemap of [intype]=outtype (or [in]=(newintype,outtype)
can be used to map and replace types'

def setformat(self, formatstr=None, typemap=None):

    if formatstr:
        self.format = (formatstr + '\n')
    else:
        self.format = (self._DEFAULT_FORMAT + '\n')
    self.fields = []
    self.fieldnames = []
    self.fieldmap = {}
    e = 0
    while True:
        s = (self.format.find('%', e) + 1)
        if ((s < 1) or (self.format[s] != '(')):
            break
        e = self.format.find(')', s)
        if (e < 0):
            break
        fname = self.format[(s + 1):e]
        fspec = ''
        for i in xrange((e + 1), len(self.format)):
            if (self.format[i] in '1234567890.+-#    lLh'):
                fspec += self.format[i]
            else:
                break
        ftype = self.format[i]
        i += 1
        if ((i < len(self.format)) and (self.format[i] == ':')):
            e = self.format.find('    ', i)
            (ftype, outtype) = self.format[(i - 1):e].split(':')
        else:
            outtype = None
        e = i
        try:
            (fname, fmap) = fname.split(':')
        except:
            fmap = fname
        if (typemap and (ftype in typemap) and (not outtype)):
            try:
                (ftype, outtype) = typemap[ftype]
            except:
                outtype = typemap[ftype]
        self.fields.append((fname, ftype, fspec))
        self.fieldnames.append(fname)
        if outtype:
            self.fieldmap[fname] = (fmap, outtype)
