+ /* mark end of directory */
+ wrbuf_putc(wr_dir, ISO2709_FS);
+
+ /* base address of data (comes after leader+directory) */
+ base_address = 24 + wrbuf_len(wr_dir);
+
+ wr_head = wrbuf_alloc();
+
+ /* write record length */
+ wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
+ /* from "original" leader */
+ wrbuf_write(wr_head, leader+5, 7);
+ /* base address of data */
+ wrbuf_printf(wr_head, "%05d", base_address);
+ /* from "original" leader */
+ wrbuf_write(wr_head, leader+17, 7);
+
+ wrbuf_write(wr, wrbuf_buf(wr_head), 24);
+ wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
+ wrbuf_free(wr_head, 1);
+ wrbuf_free(wr_dir, 1);
+
+ for (n = mt->nodes; n; n = n->next)
+ {
+ struct yaz_marc_subfield *s;
+ switch(n->which)
+ {
+ case YAZ_MARC_DATAFIELD:
+ wrbuf_printf(wr, "%.*s", indicator_length,
+ n->u.datafield.indicator);
+ for (s = n->u.datafield.subfields; s; s = s->next)
+ wrbuf_printf(wr, "%c%s", ISO2709_IDFS, s->code_data);
+ wrbuf_printf(wr, "%c", ISO2709_FS);
+ break;
+ case YAZ_MARC_CONTROLFIELD:
+ wrbuf_printf(wr, "%s%c", n->u.controlfield.data, ISO2709_FS);
+ break;
+ case YAZ_MARC_COMMENT:
+ break;
+ case YAZ_MARC_LEADER:
+ break;
+ }
+ }
+ wrbuf_printf(wr, "%c", ISO2709_RS);
+ return 0;
+}
+
+#if HAVE_XML2
+int yaz_marc_read_xml_subfields(yaz_marc_t mt, const xmlNode *ptr)
+{
+ for (; ptr; ptr = ptr->next)
+ {
+ if (ptr->type == XML_ELEMENT_NODE)
+ {
+ if (!strcmp((const char *) ptr->name, "subfield"))
+ {
+ size_t ctrl_data_len = 0;
+ char *ctrl_data_buf = 0;
+ const xmlNode *p = 0, *ptr_code = 0;
+ struct _xmlAttr *attr;
+ for (attr = ptr->properties; attr; attr = attr->next)
+ if (!strcmp((const char *)attr->name, "code"))
+ ptr_code = attr->children;
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Bad attribute '%.80s' for 'subfield'",
+ attr->name);
+ return -1;
+ }
+ if (!ptr_code)
+ {
+ yaz_marc_cprintf(
+ mt, "Missing attribute 'code' for 'subfield'" );
+ return -1;
+ }
+ if (ptr_code->type == XML_TEXT_NODE)
+ {
+ ctrl_data_len =
+ strlen((const char *)ptr_code->content);
+ }
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Missing value for 'code' in 'subfield'" );
+ return -1;
+ }
+ for (p = ptr->children; p ; p = p->next)
+ if (p->type == XML_TEXT_NODE)
+ ctrl_data_len += strlen((const char *)p->content);
+ ctrl_data_buf = nmem_malloc(mt->nmem, ctrl_data_len+1);
+ strcpy(ctrl_data_buf, (const char *)ptr_code->content);
+ for (p = ptr->children; p ; p = p->next)
+ if (p->type == XML_TEXT_NODE)
+ strcat(ctrl_data_buf, (const char *)p->content);
+ yaz_marc_add_subfield(mt, ctrl_data_buf, ctrl_data_len);
+ }
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Expected element 'subfield', got '%.80s'", ptr->name);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int yaz_marc_read_xml_leader(yaz_marc_t mt, const xmlNode **ptr_p)
+{
+ int indicator_length;
+ int identifier_length;
+ int base_address;
+ int length_data_entry;
+ int length_starting;
+ int length_implementation;
+ const char *leader = 0;
+ const xmlNode *ptr = *ptr_p;
+
+ for(; ptr; ptr = ptr->next)
+ if (ptr->type == XML_ELEMENT_NODE)
+ {
+ if (!strcmp((const char *) ptr->name, "leader"))
+ {
+ xmlNode *p = ptr->children;
+ for(; p; p = p->next)
+ if (p->type == XML_TEXT_NODE)
+ leader = (const char *) p->content;
+ break;
+ }
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Expected element 'leader', got '%.80s'", ptr->name);
+ return -1;
+ }
+ }
+ if (!leader)
+ {
+ yaz_marc_cprintf(mt, "Missing element 'leader'");
+ return -1;
+ }
+ if (strlen(leader) != 24)
+ {
+ yaz_marc_cprintf(mt, "Bad length %d of leader data."
+ " Must have length of 24 characters", strlen(leader));
+ return -1;
+ }
+ yaz_marc_read_leader(mt, leader,
+ &indicator_length,
+ &identifier_length,
+ &base_address,
+ &length_data_entry,
+ &length_starting,
+ &length_implementation);
+ *ptr_p = ptr;
+ return 0;
+}
+
+static int yaz_marc_read_xml_fields(yaz_marc_t mt, const xmlNode *ptr)
+{
+ for(; ptr; ptr = ptr->next)
+ if (ptr->type == XML_ELEMENT_NODE)
+ {
+ if (!strcmp((const char *) ptr->name, "controlfield"))
+ {
+ const xmlNode *ptr_tag = 0;
+ struct _xmlAttr *attr;
+ for (attr = ptr->properties; attr; attr = attr->next)
+ if (!strcmp((const char *)attr->name, "tag"))
+ ptr_tag = attr->children;
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Bad attribute '%.80s' for 'controlfield'",
+ attr->name);
+ return -1;
+ }
+ if (!ptr_tag)
+ {
+ yaz_marc_cprintf(
+ mt, "Missing attribute 'tag' for 'controlfield'" );
+ return -1;
+ }
+ yaz_marc_add_controlfield_xml(mt, ptr_tag, ptr->children);
+ }
+ else if (!strcmp((const char *) ptr->name, "datafield"))
+ {
+ char indstr[11]; /* 0(unused), 1,....9, + zero term */
+ const xmlNode *ptr_tag = 0;
+ struct _xmlAttr *attr;
+ int i;
+ for (i = 0; i<11; i++)
+ indstr[i] = '\0';
+ for (attr = ptr->properties; attr; attr = attr->next)
+ if (!strcmp((const char *)attr->name, "tag"))
+ ptr_tag = attr->children;
+ else if (strlen((const char *)attr->name) == 4 &&
+ !memcmp(attr->name, "ind", 3))
+ {
+ int no = atoi((const char *)attr->name+3);
+ if (attr->children
+ && attr->children->type == XML_TEXT_NODE)
+ indstr[no] = attr->children->content[0];
+ }
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Bad attribute '%.80s' for 'datafield'",
+ attr->name);
+ return -1;
+ }
+ if (!ptr_tag)
+ {
+ yaz_marc_cprintf(
+ mt, "Missing attribute 'tag' for 'datafield'" );
+ return -1;
+ }
+ /* note that indstr[0] is unused so we use indstr[1..] */
+ yaz_marc_add_datafield_xml(mt, ptr_tag,
+ indstr+1, strlen(indstr+1));
+
+ if (yaz_marc_read_xml_subfields(mt, ptr->children))
+ return -1;
+ }
+ else
+ {
+ yaz_marc_cprintf(mt,
+ "Expected element controlfield or datafield,"
+ " got %.80s", ptr->name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int yaz_marc_read_xml(yaz_marc_t mt, const void *xmlnode)
+{
+ const xmlNode *ptr = xmlnode;
+ for(; ptr; ptr = ptr->next)
+ if (ptr->type == XML_ELEMENT_NODE)
+ {
+ if (!strcmp((const char *) ptr->name, "record"))
+ break;
+ else
+ {
+ yaz_marc_cprintf(
+ mt, "Unknown element '%.80s' in MARC XML reader",
+ ptr->name);
+ return -1;
+ }
+ }
+ if (!ptr)
+ {
+ yaz_marc_cprintf(mt, "Missing element 'record' in MARC XML record");
+ return -1;
+ }
+ /* ptr points to record node now */
+ ptr = ptr->children;
+ if (yaz_marc_read_xml_leader(mt, &ptr))
+ return -1;
+ return yaz_marc_read_xml_fields(mt, ptr->next);
+}
+#else
+int yaz_marc_read_xml(yaz_marc_t mt, const void *xmlnode)
+{
+ return -1;
+}
+#endif
+
+int yaz_marc_read_iso2709(yaz_marc_t mt, const char *buf, int bsize)
+{
+ int entry_p;
+ int record_length;
+ int indicator_length;
+ int identifier_length;
+ int end_of_directory;
+ int base_address;
+ int length_data_entry;
+ int length_starting;
+ int length_implementation;
+
+ yaz_marc_reset(mt);
+
+ record_length = atoi_n (buf, 5);
+ if (record_length < 25)
+ {
+ yaz_marc_cprintf(mt, "Record length %d < 24", record_length);
+ return -1;
+ }
+ /* ballout if bsize is known and record_length is less than that */
+ if (bsize != -1 && record_length > bsize)