#!/usr/bin/env python

"""Parses MARC-format data.  The MARC class has a constructor
which takes binary MARC data.
"""

# This file should be available from
# http://www.pobox.com/~asl2/software/PyZ3950/
# and is licensed under the X Consortium license:
# Copyright (c) 2001, Aaron S. Lav, asl2@pobox.com
# All rights reserved. 

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, and/or sell copies of the Software, and to permit persons
# to whom the Software is furnished to do so, provided that the above
# copyright notice(s) and this permission notice appear in all copies of
# the Software and that both the above copyright notice(s) and this
# permission notice appear in supporting documentation. 

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
# OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
# INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 

# Except as contained in this notice, the name of a copyright holder
# shall not be used in advertising or otherwise to promote the sale, use
# or other dealings in this Software without prior written authorization
# of the copyright holder.


# cheshire3:   Copied in from PyZ3950 for MARC handling without
#              needing Z39.50 support. As I wrote a big chunk and we
#              paid Aaron for a big chunk, I don't feel too bad! :)
#              --azaroth (2008-09-12)


import sys
import string
from xml.sax.saxutils import escape
import re

from cheshire3 import marc_to_unicode

class MarcError (Exception):
    pass

def is_fixed (num):
    return num < 10

fieldsep = '\x1e'
sep = '\x1f'#'\x1f' # XXX or 1D for pseudo-marc output from z3950.c
recsep = '\x1d'

intfixre = re.compile('[^0-9]')

debug = 0

# Attributes for SGML DTD (!!!)  If not present, then I1 I2
attrHash = { 22 : ['ISDSLvl', 'I2'], 
             24 : ['StdNum', 'DiffInd'],    28 : ['PubNmTyp', 'NteAdEnty'],
             33 : ['DateType', 'EventTyp'], 34 : ['ScapeTyp', 'I2'],
             41 : ['TransInd', 'I2'],       45 : ['TimePrd', 'I2'],
             50 : ['InLofC', 'CNSrc'],      55 : ['InNLC', 'CNCLSSrc'],
             60 : ['InNLM', 'CNSrc'],       70 : ['InNAL', 'I2'],
             72 : ['I1', 'CodeSrc'],        82 : ['Edition', 'CNSrc'],
             86 : ['NumbrSrc', 'I2'],      100 : ['NameType', 'I2'],
             110: ['NameType', 'I2'],      111 : ['NameType', 'I2'],
             130: ['NFChars', 'I2'],       150 : ['I1', 'NFChars'],
             151: ['I1', 'NFChars'],       210 : ['AddEnty', 'I2'],
             211: ['AddEnty', 'NFChars'],  212 : ['AddEnty', 'I2'],
             214: ['AddEnty', 'NFChars'],  222 : ['I1', 'NFChars'],
             240: ['PrntDisp', 'NFChars'], 242 : ['AddEnty', 'NFChars'],
             243: ['PrntDisp', 'NFChars'], 245 : ['AddEnty', 'NFChars'],
             246: ['NCAddEty', 'TitleTyp'],247 : ['AddEnty', 'NoteCntl'],
             270: ['Level', 'AddrType'],   355 : ['CntlElmt', 'I2'],
             362: ['DTFormat', 'I2'],      400 : ['NameType', 'Pronoun'],
             410: ['NameType', 'Pronoun'], 411 : ['NameType', 'Pronoun'],
             430: ['I1', 'NFChars'],       440 : ['I1', 'NFChars'],
             450: ['I1', 'NFChars'],       451 : ['I1', 'NFChars'],
             490: ['Traced', 'I2'],        505 : ['DCC', 'CDLevel'],
             510: ['CoverLoc', 'I2'],      511 : ['DCC', 'I2'],
             516: ['DCC', 'I2'],           521 : ['DCC', 'I2'],
             520: ['DCC', 'I2'],           522 : ['DCC', 'I2'],
             524: ['DCC', 'I2'],           535 : ['Holds', 'I2'],
             537: ['DCC', 'I2'],           551 : ['I1', 'NFChars'],
             555: ['DCC', 'I2'],           556 : ['DCC', 'I2'],
             565: ['DCC', 'I2'],           567 : ['DCC', 'I2'],
             581: ['DCC', 'I2'],           582 : ['DCC', 'I2'],
             586: ['DCC', 'I2'],           600 : ['NameType', 'SubjSys'],
             610: ['NameType', 'SubjSys'], 611 : ['NameType', 'SubjSys'],
             630: ['NFChars', 'SubjSys'],  650 : ['SubjLvl', 'SubjSys'],
             651: ['I1', 'SubjSys'],       653 : ['IndexLvl', 'I2'],
             654: ['IndexLvl', 'I2'],      655 : ['Type', 'Source'],
             656: ['I1', 'Source'],        656 : ['I1', 'Source'],
             700: ['NameType','EntryType'],710 : ['NameType','EntryType'],
             711: ['NameType','EntryType'],730 : ['NFChars','EntryType'],
             740: ['NFChars','EntryType'], 760 : ['NoteCntl', 'I2'],
             762: ['NoteCntl', 'I2'],      765 : ['NoteCntl', 'I2'],
             767: ['NoteCntl', 'I2'],      772 : ['NoteCntl', 'I2'],
             773: ['NoteCntl', 'I2'],      775 : ['NoteCntl', 'I2'],
             776: ['NoteCntl', 'I2'],      777 : ['NoteCntl', 'I2'],
             780: ['NoteCntl', 'RelType'], 785 : ['NoteCntl', 'RelType'],
             787: ['NoteCntl', 'I2'],      800 : ['NameType', 'I2'],
             810: ['NameType', 'I2'],      811 : ['NameType', 'I2'],
             830: ['I1', 'NFChars'],       852 : ['Scheme', 'Order'],
             853: ['CmprsExpnd', 'Eval'],  853 : ['CmprsExpnd', 'Eval'],
             856: ['AccsMeth', 'I2'],      863 : ['EncLevel', 'HoldForm'],
             864: ['EncLevel','HoldForm'], 865 : ['EncLevel', 'HoldForm'],
             866: ['EncLevel','Notation'], 867 : ['EncLevel', 'Notation'],
             868: ['EncLevel','Notation'], 886 : ['FldType', 'I2']}

subfieldHash = {'1' : "one", '2' : "two", '3' : "three", '4' : "four", '5' : "five",
                '6' : "six", '7' : "seven", '8' : "eight", '9' : "nine", '0' : "zero"}


# takes text, turns it into tuple of (ind1, ind2, list of (subfield, val))
# where subfield may repeat within the list.
# We need a structure like this in order to correctly parse both records:
# 650  0 $aWorld War, 1939-1945$xCampaigns$zTunisia
# 650  0 $aReal property$zMississippi$zTippah County$xMaps
# (taken from _USMARC Format for Bibliographic Data_, Prepared by Network
# Development and MARC Standards Office, Cataloging Distribution Service,
# Library of Congress, section 650 p. 5, page printed Dec 1991, looseleaf
# binder issued in 1988.

def parse_sub (field):
    if len (field) < 4:
        if field[:2] == '  ':
            # Is this legit?  I've seen it, so handle correctly.
            # specifically for au=Johansen, Arnold S from z3950.bibsys.no:2100
            return (' ', ' ', [])
        elif len(field) == 3:
            return (field[0], field[1], [])
        return None

    if debug and field [2] <> sep:
        print "Bad field [2]", repr (field[2])
    ind1 = field[0]
    ind2 = field[1]
    sublist = []
    splitlist = string.split (field[2:], sep)
    for sub in splitlist:
        if (sub == ''): # we begin w/ sep, so there's an empty prefix
            continue
        sublist.append ((sub[0], string.strip(sub[1:])))
    return (ind1, ind2, sublist)
    
class MARC:
    """Parses data into 'fields' attribute, indexed by field number.
    Each value is a list.  For fixed fields, it's a list of the string data
    (one string for each occurence of the field in the original data).  For
    other fields, each list element is a tuple of (indicator 1, indicator 2,
    subdata), where subdata is a list of tuples of (subfield indicator,
    subfield data).  Yes, this is kinda lame and I really should have
    used structures, but this was some of the first Python code I ever
    wrote.
    """
    hdrbits = [5,6,7,8, 9, 17,18,19]
    # Status, Type, Bib. Level, Type of Ctrl., Enc. Level,
    # Descr. Cat. Form, Linked Rcd Reqt are all part of pseudoentry 0

    def __init__(self, MARC = None):
        self.fields = {}
        self.ok = 0
        self.marc = MARC
        if MARC is None:
            return # we'll write to it later
        reclen = self.extract_int (0,4)
        self.reclen = reclen
        baseaddr = self.extract_int (12, 16)
        zerostr = ""
        for ind in self.hdrbits: zerostr = zerostr + self.marc[ind]
        self.fields [0] = [zerostr]
	if (debug):
	    assert (self.marc[9] == ' ') # 'a' would be UCS/Unicode
	    assert (self.marc[10] == '2' and self.marc[11] == '2')
	    assert (self.marc[20:22] == '45')
        pos = 24
        lastpos = baseaddr
        while pos < baseaddr:
            tag = self.marc[pos:pos+3]
            if tag [0] == '\035' or tag [0] == '\036':
                break
            fieldlen = self.extract_int (pos + 3, pos + 6)
            startpos = self.extract_int (pos + 7, pos + 11)
            pos = pos + 12
            start = baseaddr + startpos
            end   = start + fieldlen
            line = self.marc[start:end]
            lastpos = startpos
            if line [-1] == '\x1E':
                line = line[:-1]
            elif (debug): 
		print "Weird, no hex 1E for", tag, repr(line)
            try:
                field = string.atoi (tag)
            except ValueError:
                # replace all non int chrs
                tag = intfixre.sub('0', tag)
                field = string.atoi(tag)
            if is_fixed (field):
                self.fields[field] = [line]
                # 1-elt list for orthogonality of processing
            else:
                ps = parse_sub (line)
                if ps is None:
                    raise MarcError (repr(line))
                self.fields.setdefault (field, []).append (ps)
        self.ok = 1
        # XXX should do more error-checking
    def __str__ (self):
        k = self.fields.keys ()
        k.sort ()
        lst = []
        for field in k:
            lst.append (self.stringify_field (field))
        return "MARC: \n" + "\n".join (lst)
    def stringify_field (self, k):
        f = self.fields [k]
        if is_fixed (k):
            return str (k) + " " +  f[0]
        else:
            str_l = []
            for l in f:
                def fmt (x):
                    return '$%s%s' % (x[0], x[1])
                sl = map (fmt, l[2])
                str_l.append (str(k) + " " + l[0] + l[1] + " ".join (sl))
            return "\n".join (str_l)
    def extract_int (self, start, end):
        return string.atoi (self.marc[start:end+1])
    def get_MARC (self):
        hdrlist = [' '] * 24
        zerostr = self.fields [0][0]
        for i in range (len (zerostr)):
            hdrlist [self.hdrbits [i]] = zerostr [i]
        hdrlist [10] = '2' # replace these with data map, assert on read
        hdrlist [11] = '2'
        hdrlist [20] = '4'
        hdrlist [21] = '5'
        hdrlist [22] = '0'
        hdrlist [23] = '0'
        # later - 0-4 log. record length, 12-16 base addr of data
        # directory: 3 of tag, 4 of field len, 5 of starting pos (rel.
        # to base address of data, 12-16
        fields = self.fields.keys ()
        fields.sort() # added by CS to make sure control fields come before data fields
        data = ''
        directory = ''
        for field in fields:
            if field == 0: # pseudofield
                continue
            for fielddat in self.fields [field]:
                try:
                    start = len (data.encode('UTF-8'))
                except:
                    start = len(data)
                if is_fixed (field):
                    data += fielddat
                else:
                    sublist = (fielddat [0] + fielddat [1] +
                               "".join (map (lambda s: sep + s[0] + s[1],
                                             fielddat[2])))
                    data += sublist
                data += fieldsep # XXX is this right?
                try:
                    length = len (data.encode('UTF-8')) - start
                except:
                    length = len (data) - start
                directory += "%.03d%.04d%.05d" % (field, length, start)
        def id (x): return x
        directory += '\x1e'
        data += recsep #data += fieldsep + recsep changed from commented code to current code by Cat
        try:
            hdrlist [0:5] = map (id, "%.05d" % (len (hdrlist) + len (directory) +
                                   len (data.encode('UTF-8')),)) #encode bit added by Cat to allow for different byte length of unicode strings
        except:
            hdrlist [0:5] = map (id, "%.05d" % (len (hdrlist) + len (directory) +
                                   len (data),))
        hdrlist [12:17] = map (id,"%.05d" % (len (hdrlist) + len (directory),))
        return "".join (hdrlist) + directory + data

    def toMARCXML(self):
        " Convert record to MarcXML Schema "
        keys = self.fields.keys()
        keys.sort()


        xmllist = ["<record>\n", "  <leader>%s</leader>\n" % (self.get_MARC()[:24])]
        for key in keys:
            if key == 0:
                # XXX Skip?? What are these??
                pass
            elif key < 10:
                xmllist.append("  <controlfield tag=\"00%d\">%s</controlfield>\n" % (key, escape(self.fields[key][0])))
            else:
                for instance in self.fields[key]:
                    if key < 100:
                        keystr = "0" + str(key)
                    else:
                        keystr = str(key)
                    xmllist.append("  <datafield tag=\"%s\" ind1=\"%s\" ind2=\"%s\">\n" % (keystr, instance[0], instance[1]))
                    for sub in instance[2]:
                        xmllist.append("    <subfield code=\"%s\">%s</subfield>\n" % (sub[0], escape(sub[1])))
                    xmllist.append("  </datafield>\n")

        xmllist.append("</record>")
        xml = ''.join(xmllist)
        return xml

    def toOAIMARC(self):
        """Convert record to OAI MARC XML Schema.
        Note Well that OAI-MHP 2.0 recommends using MarcXML"""
        
        keys = self.fields.keys()
        keys.sort()
        marc = self.get_MARC()

        # What should these attributes really be?
        xmllist  = ['<oai_marc type="%s" level="%s">\n' % (marc[6], marc[7])]

        for key in keys:
            if key == 0:
                # Skip?? What are these?
                pass
            elif key < 10:
                xmllist.append("  <fixfield id=\"%d\">%s</fixfield>\n" % (key, self.fields[key][0]))
            else:
                for instance in self.fields[key]:
                    xmllist.append("  <varfield tag=\"%d\" i1=\"%s\" i2=\"%s\">\n" % (key, instance[0], instance[1]))
                    for sub in instance[2]:
                        xmllist.append("    <subfield label=\"%s\">%s</subfield>\n" % (sub[0], escape(sub[1])))
                    xmllist.append("  </varfield>\n")

        xmllist.append("</oai_marc>")
        xml = ''.join(xmllist)
        return xml        

    def sgml_processCode(self, k):
        if attrHash.has_key(k):
            i1 = attrHash[k][0]
            i2 = attrHash[k][1]
        else:
            i1 = "I1"
            i2 = "I2"
        if k < 100:
            keystr = "0%d" % (k)
        else:
            keystr = str(k)

        sgmllist = []
        for instance in self.fields[k]:
            sgmllist.append('        <fld%s %s="%s" %s="%s">\n' % (keystr, i1, instance[0], i2, instance[1]))
            for sub in instance[2]:
                stag = sub[0]
                if subfieldHash.has_key(stag):
                    stag = subfieldHash[stag]
                sgmllist.append('          <%s>%s</%s>\n' % (stag, escape(sub[1]), stag))
            sgmllist.append('        </fld%s>\n' % (keystr))
        sgml = ''.join(sgmllist)
        return sgml
        

    def toSGML(self):
        """ Convert record to USMARC SGML """

        keys = self.fields.keys()
        keys.sort()

        # Extract field ranges
        cflds = []
        numbcode = []
        mainenty = []
        titles = []
        edimprnt = []
        physdesc = []
        series = []
        notes = []
        subjaccs = []
        addenty = []
        linkenty = []
        saddenty = []
        holdaltg = []
        fld9xx = []
        # Ugly
        for k in keys:
            if k == 0:
                pass
            elif k < 10:
                cflds.append(k)
            elif k < 100:
                numbcode.append(k)
            elif k < 200:
                mainenty.append(k)
            elif k < 250:
                titles.append(k)
            elif k < 300:
                edimprnt.append(k)
            elif k < 400:
                physdesc.append(k)
            elif k < 500:
                series.append(k)
            elif k < 600:
                notes.append(k)
            elif k < 700:
                subjaccs.append(k)
            elif k < 760:
                addenty.append(k)
            elif k < 800:
                linkenty.append(k)
            elif k < 840:
                saddenty.append(k)
            elif k < 900:
                holdaltg.append(k)
            else:
                fld9xx.append(k)


                 
        marc = self.get_MARC()

        sgml = ["<usmarc>\n"]
        sgml.append("  <leader>\n")
        sgml.append("    <lrl>%s</lrl>\n" % (marc[:5]))
        sgml.append("    <recstat>%s</recstat>\n" % (marc[5]))
        sgml.append("    <rectype>%s</rectype>\n" % (marc[6]))
        sgml.append("    <biblevel>%s</biblevel>\n" % (marc[7]))
        sgml.append("    <ucp>%s</ucp>\n" % (marc[8:10]))
        sgml.append("    <indcount>%s</indcount>\n" % (marc[10]))
        sgml.append("    <sfcount>%s</sfcount>\n" % (marc[11]))
        sgml.append("    <baseaddr>%s</baseaddr>\n" % (marc[12:17]))
        sgml.append("    <enclevel>%s</enclevel>\n" % (marc[17]))
        sgml.append("    <dsccatfm>%s</dsccatfm>\n" % (marc[18]))
        sgml.append("    <linkrec>%s</linkrec>\n" % (marc[19]))
        sgml.append("    <entrymap>\n")
        sgml.append("      <flength>%s</flength>\n" % (marc[20]))
        sgml.append("      <scharpos>%s</scharpos>\n" % (marc[21]))
        sgml.append("      <idlength>%s</idlength>\n" % (marc[22]))
        sgml.append("      <emucp>%s</emucp>\n" % (marc[23]))
        sgml.append("    </entrymap>\n")
        sgml.append("  </leader>\n")
        sgml.append("  <directry></directry>\n")

        sgml.append("  <varflds>\n")
        sgml.append("    <varcflds>\n")
        for k in cflds:
            sgml.append("      <fld00%d>%s</fld00%s>\n" % (k, self.fields[k][0], k))
        sgml.append("    </varcflds>\n")
        sgml.append("    <vardflds>\n")
        sgml.append("      <numbcode>\n")
        for k in numbcode:
            sgml.append(self.sgml_processCode(k))
        sgml.append("      </numbcode>\n")

        if mainenty:
            sgml.append("      <mainenty>\n")
            for k in mainenty:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </mainenty>\n")
        if titles:
            sgml.append("      <titles>\n")
            for k in titles:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </titles>\n")
        if edimprnt:
            sgml.append("      <edimprnt>\n")
            for k in edimprnt:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </edimprnt>\n")
        if physdesc:
            sgml.append("      <physdesc>\n")
            for k in physdesc:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </physdesc>\n")
        if series:
            sgml.append("      <series>\n")
            for k in series:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </series>\n")
        if notes:
            sgml.append("      <notes>\n")
            for k in notes:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </notes>\n")
        if subjaccs:
            sgml.append("      <subjaccs>\n")
            for k in subjaccs:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </subjaccs>\n")
        if addenty:
            sgml.append("      <addenty>\n")
            for k in addenty:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </addenty>\n")
        if linkenty:
            sgml.append("      <linkenty>\n")
            for k in linkenty:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </linkenty>\n")
        if saddenty:
            sgml.append("      <saddenty>\n")
            for k in saddenty:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </saddenty>\n")
        if holdaltg:
            sgml.append("      <holdaltg>\n")
            for k in holdaltg:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </holdaltg>\n")
        if fld9xx:
            sgml.append("      <fld9xx>\n")
            for k in fld9xx:
                sgml.append(self.sgml_processCode(k))
            sgml.append("      </fld9xx>\n")
        sgml.append("    </vardflds>\n")
        sgml.append("  </varflds>\n")
        sgml.append("</usmarc>")
        return ''.join(sgml)
        

    def toSimpleDC(self):
        """ Convert Marc into DC according to LC Crosswalk """
        xml = ['<dc xmlns="http://www.loc.gov/zing/srw/dcschema/v1.0/">\n']

        # Title -> 245
        if self.fields.has_key(245):
            instance = self.fields[245][0][2]
            a = ''
            b = ''
            for sub in instance:
                if sub[0] == 'a':
                    a = sub[1]
                elif sub[0] == 'b':
                    b = sub[1]
            if a and b and a[-1] in [',', '.', ';', ':']:
                a += " " + b
            elif a and b:
                a += "; " + b
            elif b and not a:
                a = b
            xml.append("  <title>%s</title>\n" % (a))

        # Creator -> 100,110,111,700,710,711
        authorKeys = [100, 110, 111, 700, 710, 711]
        for k in authorKeys:
            if self.fields.has_key(k):
                for instance in self.fields[k]:
                    a = ''
                    h = ''
                    d = ''
                    for sub in instance[2]:
                        if sub[0] == 'a':
                            a = sub[1]
                        elif sub[0] == 'h':
                            h = sub[1]
                        elif sub[0] == 'd':
                            d = sub[1]
                    if h:
                        a += ", " + h
                    if d:
                        a += " (" + d + ")"
                    xml.append("  <creator>%s</creator>\n" % (a))
        
        # Subject -> 600,610, 611, 630, 650, 653
        # Just dump in directly...
        subjectList = [600, 610, 611, 630, 650, 653]
        for s in subjectList:
            if self.fields.has_key(s):
                for instance in self.fields[s]:
                    subject = ''
                    for sub in instance[2]:
                        subject += sub[1] + " -- "
                    subject = subject[:-4]
                    xml.append("  <subject>%s</subject>\n" % (subject))
        

        # Publisher -> 260$a$b
        if self.fields.has_key(260):
            for instance in self.fields[260]:
                a = b = ''
                for sub in instance[2]:
                    if sub[0] == 'a':
                        a = sub[1]
                    elif sub[0] == 'b':
                        b = sub[1]
                        if b[-1] in [',', ';', ':']:
                            b = b[:-1]
                    elif sub[0] == 'c':
                        d = sub[1]
                        if d[-1] == '.':
                            d = d[:-1]
                        xml.append("  <date>%s</date>\n" % (d))
                if b:
                    a += " " + b
                if a:
                    xml.append("  <publisher>%s</publisher>\n" % (a))

        # Type -> 655
        if self.fields.has_key(655):
            for instance in self.fields[655]:
                gf = ''
                for sub in instance[2]:
                    gf  += sub[1] + " -- "
                gf = gf[:-4]
                xml.append("  <type>%s</type>\n" % (gf))

        # Non Standard:  Identifier -> ISSN/ISBN
        for k in [20,22]:
            if self.fields.has_key(k):
                for instance in self.fields[k]:
                    for sub in instance[2]:
                        if sub[0] == 'a':
                            xml.append("  <identifier>%s</identifier>\n" % (sub[1]))

        # Non Standard: Description -> 300
        if self.fields.has_key(300):
            for instance in self.fields[300]:
                desc = ''
                for sub in instance[2]:
                    desc += sub[1] + " "
                desc = desc[:-1]
                xml.append("  <description>%s</description>\n" % (desc))
                
        xml.append("</dc>")
        return ''.join(xml)


    def toMODS(self):
        """ Tranform MARC record into MODS according to CrossWalk """
        xml = ["<mods>\n"]

        # --- TitleInfo Fields ---
        if self.fields.has_key(245):
            instance = self.fields[245][0][2]
            xml.append("  <titleInfo>\n    <title>")
            insubtitle = 0
            for sub in instance:
                if (sub[0] in ['a', 'f', 'g', 'k']):
                    xml.append(escape(sub[1]))
                    xml.append(' ')
                elif (sub[0] == 'b'):
                    xml.append("</title>\n    <subtitle>%s " % (escape(sub[1])))
                    insubtitle = 1
            if (insubtitle):
                xml.append("</subtitle>\n  </titleInfo>\n")
            else:
                xml.append("</title>\n  </titleInfo>\n")

        if self.fields.has_key(210):
            instance = self.fields[210][0][2]
            subf = {}
            for sub in instance:
                subf[sub[0]] = escape(sub[1])
            xml.append('  <titleInfo type="abbreviated">\n    <title>%s</title>\n' % (subf['a']))
            if (subf.has_key('b')):
                xml.append('    <subtitle>%s</subtitle>\n' % (subf['b']))
            xml.append('  </titleInfo>\n')

        if self.fields.has_key(242):
            instance = self.fields[242][0][2]
            subf = {}
            for sub in instance:
                subf[sub[0]] = escape(sub[1])
            if (subf.has_key('i')):
                label = ' displayLabel="%s"' % (subf['i'])
            else:
                label = ''
            xml.append('  <titleInfo type="translated"%s>\n    <title>%s</title>\n' % (label, subf['a']))
            if (subf.has_key('b')):
                xml.append('    <subtitle>%s</subtitle>\n' % (subf['b']))
            if (subf.has_key('n')):
                xml.append('    <partNumber>%s</partNumber>\n' % (subf['n']))
            if (subf.has_key('p')):
                xml.append('    <partName>%s</partName>\n' % (subf['p']))
            xml.append('  </titleInfo>\n')
                

        if self.fields.has_key(246):
            full = self.fields[246][0]
            subfield2 = full[1]
            instance = full[2]
            subf = {}
            for sub in instance:
                subf[sub[0]] = escape(sub[1])
            if (subfield2 == 1):
                xml.append('  <titleInfo type="translated">\n    <title>%s</title>\n' % (subf['a']))
            else:
                xml.append('  <titleInfo type="alternative">\n    <title>%s</title>\n' % (subf['a']))

            if (subf.has_key('b')):
                xml.append('    <subtitle>%s</subtitle>\n' % (subf['b']))
            if (subf.has_key('n')):
                xml.append('    <partNumber>%s</partNumber>\n' % (subf['n']))
            if (subf.has_key('p')):
                xml.append('    <partName>%s</partName>\n' % (subf['p']))
            xml.append('  </titleInfo>\n')

        if self.fields.has_key(130):
            uniform = self.fields[130][0][2]
        elif self.fields.has_key(240):
            uniform = self.fields[240][0][2]
        else:
            uniform = []
        if (uniform):
            subf = {}
            for sub in uniform:
                subf[sub[0]] = escape(sub[1])
            xml.append('  <titleInfo type="uniform">\n    <title>%s</title>\n' % (subf['a']))
            if (subf.has_key('n')):
                xml.append('    <partNumber>%s</partNumber>\n' % (subf['n']))
            if (subf.has_key('p')):
                xml.append('    <partName>%s</partName>\n' % (subf['p']))
            xml.append('  </titleInfo>\n')


        # --- Name Fields ---
        # Creator -> 100,110,111, 700,710,711
        authorKeyTypes = {100 : 'personal',  110 : 'corporate', 111 : 'conference', 700 : 'personal', 710 : 'corporate',  711 : 'conference'}

        for k in authorKeyTypes.keys():
            if self.fields.has_key(k):
                for instance in self.fields[k]:
                    subf = {}
                    for sub in instance[2]:
                        subf[sub[0]] = escape(sub[1])
                    xml.append('  <!-- Marc: %s -->\n' % (k))
                    xml.append('  <name type="%s">\n' % (authorKeyTypes[k]))
                    xml.append('    <role><roleTerm type="text">creator</roleTerm></role>\n')
                    xml.append('    <namePart>%s</namePart>\n' % (subf['a']))
                    if (subf.has_key('d')):
                        xml.append('    <namePart type="date">%s</namePart>\n' % (subf['d']))
                    if (subf.has_key('b')):
                        if (k in [100,700]):
                            xml.append('    <namePart type="termsOfAddress">%s</namePart>\n' % (subf['b']))
                        else:
                            xml.append('    <namePart>%s</namePart>\n' % (subf['b']))
                    if (subf.has_key('e')):
                        xml.append('    <role><roleTerm type="text">%s</roleTerm></role>\n' % (subf['e']))
                    if (subf.has_key('4')):
                        xml.append('    <role><roleTerm type="code">%s</roleTerm></role>\n' % (subf['4']))
                    xml.append('  </name>\n')

        ldr = self.fields[0][0]
        type = ldr[1]
        types = {'a' : 'text', 't' : 'text', 'e' : 'cartographic', 'f' : 'cartographic', 'c' : 'notated music', 'd' : 'notated music', 'i' : 'sound recording - nonmusical', 'j' : 'sound recording - musical', 'k' : 'still image', 'g' : 'moving image', 'r' : 'three dimensional object', 'm' : 'software, multimedia', 'p' : 'mixed material'}
        if (types.has_key(type)):
            xml.append('  <typeOfResource')
            if (ldr[2] == 'c'):
                xml.append(' collection="yes"')
            if (ldr[1] in ['d', 'f', 'p', 't']):
                xml.append(' manuscript="yes"')
            xml.append('>%s</typeOfResource>\n' % (types[type]))


        if (self.fields.has_key(8)):
            instance = self.fields[8][0]
            # XXX LONG set of checks for type and various 008 positions :(
            if (len(instance) > 33 and instance[33] == '0'):
                xml.append('  <genre authority="marcgt">non fiction</genre>\n')
                
        if self.fields.has_key(655):
            for instance in self.fields[655]:
                gf = ''
                for sub in instance[2]:
                    gf  += escape(sub[1]) + " -- "
                gf = gf[:-4]
                xml.append("  <genre>%s</genre>\n" % (gf))

        # PublicationInfo from 260
        f260 = self.fields.get(260, [])
        f44 = self.fields.get(44, [])
        f46 = self.fields.get(46, [])
        f250 = self.fields.get(250, [])
        f310 = self.fields.get(310, [])
        f321 = self.fields.get(321, [])
        f8 = self.fields.get(8, [])

        if f260 or f46 or f250 or f310 or f321:
            xml.append('  <originInfo>\n')

            if (f8 and len(f8[0]) > 18 ):
                loc = f8[0][15:18]
                if (loc <> '   ' and loc <> '|||'): 
                    xml.append('    <place><placeTerm type="code" authority="marccountry">%s</placeTerm></place>\n' % (loc))

            if (f44):
                for s in f44[0][2]:
                    if (s[0] == 'c'):
                        xml.append('    <place><placeTerm type="code" authority="iso3166">%s</placeTerm></place>\n' % (escape(s[1])))
            if (f260):
                instance = self.fields[260][0][2]            
                subf260 = {}
                for sub in instance:
                    subf260[sub[0]] = escape(sub[1])
                if (subf260.has_key('a')):
                    xml.append('    <place><placeTerm type="text">%s</placeTerm></place>\n' % (subf260['a']))
                if (subf260.has_key('b')):
                    xml.append('    <publisher>%s</publisher>\n' % (subf260['b']))
                if (subf260.has_key('c')):
                    xml.append('    <dateIssued>%s</dateIssued>\n' % (subf260['c']))

            if (f8 and len(f8[0]) > 6):
                f8type = f8[0][6]
                if (f8type in ['e', 'p', 'r', 's', 't']):
                    date = f8[0][7:11]
                    if (date <> '    '):
                        xml.append('    <dateIssued encoding="marc">%s</dateIssued>\n' % (date))
                if (f8type in ['c', 'd', 'i', 'k', 'm', 'u', 'q']):
                    if (f8type == 'q'):
                        attrib = ' qualifier="questionable"'
                    else:
                        attrib = ""
                    start = f8[0][7:11]
                    if (start <> '    '):
                        xml.append('    <dateIssued point="start" encoding="marc"%s>%s</dateIssued>\n' % (attrib, start))
                    end = f8[0][11:15]
                    if (end <> '    '):
                        xml.append('    <dateIssued point="end" encoding="marc"%s>%s</dateIssued>\n' % (attrib, end))

            if (f260):
                if subf260.has_key('g'):
                    xml.append('    <dateCreated>%s</dateCreated>\n' % (escape(subf260['g'])))

            if (f46):
                instance = f46[0][2]
                subf46 = {}
                for s in instance:
                    subf46[s[0]] = escape(s[1])
                if (subf46.has_key('k')):
                    xml.append('    <dateCreated point="start">%s</dateCreated>\n' % (subf46['k']))
                if (subf46.has_key('l')):
                    xml.append('    <dateCreated point="end">%s</dateCreated>\n' % (subf46['l']))
                if (subf46.has_key('m')):
                    xml.append('    <dateValid point="start">%s</dateValid>\n' % (subf46['m']))
                if (subf46.has_key('n')):
                    xml.append('    <dateValid point="end">%s</dateValid>\n' % (subf46['n']))
                if (subf46.has_key('j')):
                    xml.append('    <dateModified>%s</dateModified>\n' % (subf46['j']))

            if (f250):
                for s in f250[0][2]:
                    if (s[0] == 'a'):
                        xml.append('    <edition>%s</edition>\n' % (escape(s[1])))
                        break
            
            if (self.fields.has_key(0) and len(self.fields[0][0]) > 2):
                f0type = self.fields[0][0][2]
                if (f0type in ['b', 'i', 's']):
                    xml.append('    <issuance>continuing</issuance>\n')
                elif (f0type in ['a', 'c', 'd', 'm']):
                    xml.append('    <issuance>monographic</issuance>\n')

            if (f310):
                subf310 = {'a' : '', 'b' : ''}
                for s in f310[0][2]:
                    subf310[s[0]] = escape(s[1])
                xml.append('    <frequency>%s %s</frequency>\n' % (subf310['a'], subf310['b']))
            if (f321):
                subf321 = {'a' : '', 'b' : ''}
                for s in f321[0][2]:
                    subf321[s[0]] = escape(s[1])
                xml.append('    <frequency>%s %s</frequency>\n' % (subf321['a'], subf321['b']))
            xml.append('  </originInfo>\n')
                

        # --- Language ---
        if (f8 and len(f8[0]) > 38):
            lang = f8[0][35:38]
            if (lang <> '   '):
                xml.append('  <language><languageTerm type="code" authority="iso639-2b">%s</languageTerm></language>\n' % (lang))
        if self.fields.has_key(41):
            a = two = ''
            for sub in self.fields[41][0][2]:
                if sub[0] == 'a':
                    a = sub[1]
                elif sub[0] == '2':
                    two = sub[1]
                elif sub[0] == 'd' and not a:
                    a = sub[1]
                elif sub[0] == 'e' and not a:
                    a = sub[1]

            if a and not two:
                xml.append('  <language><languageTerm authority="iso639-2b">%s</languageTerm></language>\n' % (escape(a)))
            elif a:
                xml.append('  <language authority="%s">%s</language>\n' % (escape(two), escape(a)))

        # --- Physical Description ---
        # XXX: Better field 008, 242,245,246$h, 256$a
        f300 = self.fields.get(300, [])
        if (f8 and len(f8[0]) > 23):
            f8_23 = self.fields[8][0][23]
        else:
            f8_23 = ' '
        if (f300 or f8_23 == ' '):
            xml.append("  <physicalDescription>\n")
            if (f8_23 == ' '):
                xml.append('    <form authority="marcform">print</form>\n')
            if f300:
                desclist = []
                for s in f300[0][2]:
                    desclist.append(escape(s[1]))
                desc = ' '.join(desclist)
                xml.append("    <extent>%s</extent>\n" % (desc))
            xml.append("  </physicalDescription>\n")

        # Abstract
        if self.fields.has_key(520):
            xml.append('  <abstract>')
            for sub in self.fields[520]:
                if sub[0] == 'a' or sub[0] == 'b':
                    xml.append(escape(sub[1]))
            xml.append("</abstract>\n")

        # --- Table of Contents ---
        if (self.fields.has_key(505)):
            desclist = []
            for s in self.fields[505][0][2]:
                if (s[0] in ['a', 'g', 'r', 't']):
                    desclist.append(escape(s[1]))
            toc = ' '.join(desclist)
            xml.append('  <tableOfContents>%s</tableOfContents>\n' % (toc))

        # XXX TargetAudience (field 8 again)

        # --- Note ---
        if (self.fields.has_key(500)):
            for n in (self.fields[500]):
                xml.append('  <note>');
                for s in n:
                    if (s[0] == 'a'):
                        xml.append(escape(s[1]))
                xml.append('</note>\n')

        # --- Subject ---
        subjectList = [600, 610, 611, 630, 650, 651, 653]
        for s in subjectList:
            if self.fields.has_key(s):
                for instance in self.fields[s]:
                    xml.append("  <subject")
                    auths = {'0' : 'lcsh',
                             '1' : 'lcshac',
                             '2' : 'mesh',
                             '3' : 'csh',
                             '5' : 'nal',
                             '6' : 'rvm'}
                    if (auths.has_key(instance[1])):
                        xml.append(' authority="%s"' % auths[instance[1]])
                    xml.append(">\n")

                    if (s in [600, 610, 611]):
                        stype = {600 : 'personal', 610 : 'corporate', 611 : 'conference'}[s]
                        xml.append('    <name type="%s">\n' % (stype))
                        for sub in instance[2]:
                            val = escape(sub[1])
                            if (sub[0] == 'a'):
                                xml.append('      <namePart>%s</namePart>\n' % (val))
                            elif (sub[0] == 'b'):
                                attrib = ''
                                if (s == 600):
                                    attrib = ' type="termsOfAddress"'
                                xml.append('      <namePart%s>%s</namePart>\n' % (attrib, val))
                            elif (sub[0] == 'd'):
                                xml.append('      <namePart type="date">%s</namePart>\n' % (val))
                            elif (sub[0] == 'e'):
                                xml.append('      <role><roleTerm type="text">%s</roleTerm></role>\n' % (val))
                            elif (sub[0] == '4'):
                                xml.append('      <role><roleTerm type="code">%s</roleTerm></role>\n' % (val))
                            elif (sub[0] == 'u'):
                                xml.append('      <affiliation>%s</affiliation>\n' % (val))
                            elif sub[0] in ['v', 'x']:
                                xml.append('      <topic>%s</topic>\n' % (val))
                            elif sub[0] == 'y':
                                xml.append('      <temporal>%s</temporal>\n' % (val))
                            elif sub[0] == 'z':
                                xml.append('      <geographic>%s</geographic>\n' % (val))
                        xml.append('    </name>\n')
                    elif (s == 630):
                        for sub in instance[2]:
                            val = escape(sub[1])
                            if (sub[0] == 'a'):
                                xml.append('    <title>%s</title>\n' % (val))
                            elif (sub[0] == 'p'):
                                xml.append('    <partName>%s</partName>\n' % (val))
                            elif (sub[0] == 'n'):
                                xml.append('    <partNumber>%s</partNumber>\n' % (val))
                            elif sub[0] in ['v', 'x']:
                                xml.append('    <topic>%s</topic>\n' % (val))
                            elif sub[0] == 'y':
                                xml.append('    <temporal>%s</temporal>\n' % (val))
                            elif sub[0] == 'z':
                                xml.append('    <geographic>%s</geographic>\n' % (val))
                    elif (s in [650, 653]):
                        for sub in instance[2]:
                            val = escape(sub[1])
                            if (sub[0] == 'a'):
                                xml.append('    <topic>%s</topic>\n' % (val))
                            elif sub[0] in ['v', 'x']:
                                xml.append('    <topic>%s</topic>\n' % (val))
                            elif sub[0] == 'y':
                                xml.append('    <temporal>%s</temporal>\n' % (val))
                            elif sub[0] == 'z':
                                xml.append('    <geographic>%s</geographic>\n' % (val))
                    elif (s == 651):
                        for sub in instance[2]:
                            val = escape(sub[1])
                            if (sub[0] == 'a'):
                                xml.append('    <geographic>%s</geographic>\n' % (val))
                            elif sub[0] in ['v', 'x']:
                                xml.append('    <topic>%s</topic>\n' % (val))
                            elif sub[0] == 'y':
                                xml.append('    <temporal>%s</temporal>\n' % (val))
                            elif sub[0] == 'z':
                                xml.append('    <geographic>%s</geographic>\n' % (val))
                                
                    xml.append("  </subject>\n")
        if (self.fields.has_key(45)):
            full = self.fields[45][0]
            if (full[0] in ['0', '1']):
                for x in self.fields[2]:
                    if (x[0] == 'b'):
                        xml.append('  <subject><temporal encoding="iso8601">%s</temporal></subject>\n' % (escape(x[1])))
                        
        if (self.fields.has_key(43)):
            for sub in self.fields[43][0][2]:
                if (sub[0] == 'a'):
                    xml.append('  <subject><geographicCode authority="marcgac">%s</geographicCode></subject>\n' % (escape(sub[1])))
                elif (sub[0] == 'a'):
                    xml.append('  <subject><geographicCode authority="iso3166">%s</geographicCode></subject>\n' % (escape(sub[1])))

        if (self.fields.has_key(752)):
            xml.append('  <subject><hierarchicalGeographic>\n')
            for sub in self.fields[43][0][2]:
                val = escape(sub[1])
                if (sub[0] == 'a'):
                    xml.append('    <country>%s</country>\n' % (val))
                elif (sub[0] == 'b'):
                    xml.append('    <state>%s</state>\n' % (val))
                elif (sub[0] == 'c'):
                    xml.append('    <county>%s</county>\n' % (val))
                elif (sub[0] == 'd'):
                    xml.append('    <city>%s</city>\n' % (val))
            xml.append('  </hierarchicalGeographic></subject>')
            

        if (self.fields.has_key(255)):
            subf = {}
            xml.append('  <subject><cartographics>\n')
            for s in self.fields[255][0][2]:
                subf[s[0]] = escape(s[1])
            if (subf.has_key('c')):
                xml.append('    <coordinates>%s</coordinates>\n' % (subf['c']))
            if (subf.has_key('a')):
                xml.append('    <scale>%s</scale>\n' % (subf['a']))
            if (subf.has_key('b')):
                xml.append('    <projection>%s</projection>\n' % (subf['c']))
            xml.append('  </cartographics></subject>\n')

        if (self.fields.has_key(656)):
            for s in self.fields[656][0][2]:
                if (s[0] == 'a'):
                    xml.append('  <subject><occupation>%s</occupation></subject>\n')

        # XXX:  34

        # XXX:  Classification, 84

        cfields = {50 : 'lcc', 82 : 'ddc', 80 : 'udc', 60 : 'nlm'}
        for k in cfields:
            if (self.fields.has_key(k)):
                for sub in self.fields[k][0][2]:
                    stuff = []
                    if (sub[0] == 'a'):
                        stuff.append(escape(sub[1]))
                    elif (sub[0] == 'b'):
                        stuff.append(escape(sub[1]))
                txt = ' '.join(stuff)
                xml.append('  <classification authority="%s">%s</classification>\n' % (cfields[k], txt))

        if (self.fields.has_key(86)):
            full = self.fields[86][0]
            ind1 = full[0]
            if (ind1 == '0'):
                auth = 'sudocs'
            elif (ind1 == '1'):
                auth = 'candocs'
            else:
                auth = ''
            if (auth):
                for s in full[2]:
                    if (s[0] == 'a'):
                        xml.append('  <classification authority="%s">%s</classification>\n' % (auth, escape(s[1])))
                
                    
        # XXX:  relatedItem, 7XX

        # --- Identifier ---
        if self.fields.has_key(20):
            for instance in self.fields[20]:
                for sub in instance[2]:
                    if sub[0] == 'a':
                        xml.append('  <identifier type="isbn">%s</identifier>\n' % (escape(sub[1])))
        if self.fields.has_key(22):
            for instance in self.fields[22]:
                for sub in instance[2]:
                    if sub[0] == 'a':
                        xml.append('  <identifier type="issn">%s</identifier>\n' % (escape(sub[1])))
        if self.fields.has_key(24):
            for instance in self.fields[24]:
                for sub in instance[2]:
                    if sub[0] == 'a':
                        xml.append('  <identifier type="isrc">%s</identifier>\n' % (escape(sub[1])))
        if self.fields.has_key(28):
            for instance in self.fields[28]:
                for sub in instance[2]:
                    if sub[0] == 'a':
                        xml.append('  <identifier type="matrix number">%s</identifier>\n' % (escape(sub[1])))

        # XXX: location, accessCondition

        # --- recordInformation ---
        xml.append('  <recordInformation>\n')
        if (self.fields.has_key(40)):
            for instance in self.fields[40]:
                for sub in instance[2]:
                    if sub[0] == 'a':
                        xml.append('    <recordContentSource authority="marcorg">%s</recordContentSource>\n' % (escape(sub[1])))
        if (self.fields.has_key(8)):
            date = self.fields[8][0][0:6]
            if (date <> '      '):
                xml.append('    <recordCreationDate encoding="marc">%s</recordCreationDate>\n' % (date))

        if (self.fields.has_key(1)):
            xml.append('    <recordIdentifier>%s</recordIdentifier>\n' % (self.fields[1][0]))
        if (self.fields.has_key(40)):
            instance = self.fields[40][0][2]
            for s in instance:
                if (s[0] == 'b'):
                    xml.append('    <languageOfCataloging><languageTerm authority="iso639-2b">%s</languageTerm></languageOfCataloging>\n' % (escape(s[1])))

        xml.append('  </recordInformation>\n')
        xml.append("</mods>")
        txt = ''.join(xml)
        return txt
        

if __name__ == '__main__':
    for f in sys.argv[1:]:
        marc_file = open(f, 'rb')
        marc_text = marc_file.read ()
        while 1:
            marc_data1 = MARC(marc_text)
            print str (marc_data1)
            new = marc_data1.get_MARC ()
            marc_data2 = MARC (marc_text)
            k1 = marc_data1.fields.keys ()
            k2 = marc_data2.fields.keys ()
            assert (k1 == k2)
            for field in k1:
                same = (marc_data1.fields [field] ==
                        marc_data2.fields [field])
                assert (same)
            marc_text = marc_text[marc_data1.reclen:]
            if len (marc_text) == 0:
                break
        marc_file.close ()



# see http://www.loc.gov/marc/specifications/speccharmarc8.html

import unicodedata

class MARC8_to_Unicode:
    """Converts MARC-8 to Unicode.  Note that currently, unicode strings
    aren't normalized, and some codecs (e.g. iso8859-1) will fail on
    such strings.  When I can require python 2.3, this will go away.

    Warning: MARC-8 EACC (East Asian characters) makes some
    distinctions which aren't captured in Unicode.  The LC tables give
    the option of mapping such characters either to a Unicode private
    use area, or a substitute character which (usually) gives the
    sense.  I've picked the second, so this means that the MARC data
    should be treated as primary and the Unicode data used for display
    purposes only.  (If you know of either of fonts designed for use
    with LC's private-use Unicode assignments, or of attempts to
    standardize Unicode characters to allow round-trips from EACC,
    or if you need the private-use Unicode character translations,
    please inform me, asl2@pobox.com."""


    
    basic_latin = 0x42
    ansel = 0x45
    def __init__ (self, G0 = basic_latin, G1 = ansel):
        self.g0 = G0
        self.g1 = G1

    def is_multibyte (self, charset):
        return charset == 0x31
        
    def translate (self, s):
        uni_list = []
        combinings = []
        pos = 0
        while pos < len (s):
            if s[pos] == '\x1b':
                if (s[pos +1] == s[pos+2] and
                    (s[pos +1] == '$' or s[pos+1] == '(')):
                    # '$' for multiple bytes/char, '(' for single
                    # XXX note that ',' is also acceptable for single, and
                    # '$' for double.
                    self.g0 = ord (s[pos+3])
                    # XXX or !E two-char seq for ANSEL?
                    # XXX or ')', '-', 1-char or '$)', '$-' for G1
                    pos = pos + 4
                    continue
            mb_flag = self.is_multibyte (self.g0)
                
            if mb_flag:
                d = (ord (s[pos]) * 65536 +
                     ord (s[pos+1]) * 256 +
                     ord (s[pos+2]))
                pos += 3
            else:
                d = ord (s[pos])
                pos += 1
                
            if (d < 0x20 or
                (d > 0x80 and d < 0xa0)):
                uni = unichr (d)
                continue
            
            if d > 0x80 and not mb_flag:
                (uni, cflag) = marc_to_unicode.codesets [self.g1] [d]
            else:
                (uni, cflag) = marc_to_unicode.codesets [self.g0] [d]
                
            if cflag:
                combinings.append (unichr (uni))
            else:
                uni_list.append (unichr (uni))
                if len (combinings) > 0:
                    uni_list += combinings
                    combinings = []
        # what to do if combining chars left over?
        uni_str = u"".join (uni_list)
        
        # unicodedata.normalize not available until Python 2.3        
        if hasattr (unicodedata, 'normalize'):
            uni_str = unicodedata.normalize ('NFC', uni_str)
            
        return uni_str

def test_convert (s, enc):
    conv = MARC8_to_Unicode ()
    converted = conv.translate (s)
    converted = unicodedata.normalize ('NFC', converted)
    print converted.encode (enc)

    print repr (converted)

