7cebcd761e580dfdcde51810a74bfb08cdd3c902
[pazpar2-moved-to-github.git] / src / marchash.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2009 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 /** \file 
21     \brief MARC MAP utilities (hash lookup etc)
22 */
23
24 #if HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include <libxml/tree.h>
34 #include <libxml/parser.h>
35 #include <yaz/nmem.h>
36
37 #include "jenkins_hash.h"
38 #include <marchash.h>
39
40 static inline void strtrimcat(char *dest, const char *src)
41 {
42     const char *in;
43     char *out;
44     char *last_nonspace;
45     in = src;
46     out = dest;
47     // move to end of dest
48     while (*out)
49         out++;
50     // initialise last non-space charater
51     last_nonspace = out;
52     // skip leading whitespace
53     while (isspace(*in))
54         in++;
55     while (*in)
56     {
57         *out = *in;
58         if (!isspace(*in))
59             last_nonspace = out;
60         out++;
61         in++;
62     }
63     *(++last_nonspace) = '\0';
64 }
65
66 static inline void strtrimcpy(char *dest, const char *src)
67 {
68     *dest = '\0';
69     strtrimcat(dest, src);
70 }
71
72 struct marchash *marchash_create(NMEM nmem)
73 {
74     struct marchash *new;
75     new = nmem_malloc(nmem, sizeof (struct marchash));
76     memset(new, 0, sizeof (struct marchash));
77     new->nmem = nmem;
78     return new;
79 }
80
81 void marchash_ingest_marcxml(struct marchash *marchash, xmlNodePtr rec_node)
82 {
83      xmlNodePtr field_node;
84      xmlNodePtr sub_node;
85      struct marcfield *field;
86      field_node = rec_node->children;
87
88      while (field_node)
89      {
90          if (field_node->type == XML_ELEMENT_NODE)
91          {
92              field = NULL;
93              if (!strcmp((const char *) field_node->name, "controlfield"))
94              {
95                  xmlChar *content = xmlNodeGetContent(field_node);
96                  xmlChar *tag = xmlGetProp(field_node, BAD_CAST "tag");
97                  if (tag && content)
98                      field = marchash_add_field(
99                          marchash, (const char *) tag, (const char *) content);
100                  xmlFree(content);
101                  xmlFree(tag);
102              }
103              else if (!strcmp((const char *) field_node->name, "datafield"))
104              {
105                  xmlChar *content = xmlNodeGetContent(field_node);
106                  xmlChar *tag = xmlGetProp(field_node, BAD_CAST "tag");
107                  if (tag && content)
108                      field = marchash_add_field(
109                          marchash, (const char *) tag, (const char *) content);
110                  xmlFree(content);
111                  xmlFree(tag);
112              }
113              if (field)
114              {
115                  sub_node = field_node->children;
116                  while (sub_node) 
117                  {
118                      if ((sub_node->type == XML_ELEMENT_NODE) &&
119                          !strcmp((const char *) sub_node->name, "subfield"))
120                      {
121                          xmlChar *content = xmlNodeGetContent(sub_node);
122                          xmlChar *code = xmlGetProp(sub_node, BAD_CAST "code");
123                          if (code && content)
124                              marchash_add_subfield(
125                                  marchash, field,
126                                  code[0], (const char *) content);
127                          xmlFree(content);
128                          xmlFree(code);
129                      }
130                      sub_node = sub_node->next;
131                  } 
132              }
133          }
134          field_node = field_node->next;
135      }
136 }
137
138 struct marcfield *marchash_add_field(struct marchash *marchash,
139                                      const char *key, const char *val)
140 {
141     int slot;
142     struct marcfield *new;
143     struct marcfield *last;
144     
145     slot = jenkins_hash((const unsigned char *) key) & MARCHASH_MASK;
146     new = marchash->table[slot];
147     last = NULL;
148     
149     while (new) 
150     {
151         last = new; 
152         new = new->next;     
153     }
154
155     new = nmem_malloc(marchash->nmem, sizeof (struct marcfield));
156
157     if (last)
158         last->next = new;
159     else
160         marchash->table[slot] = new;
161
162     new->next = NULL;
163     new->subfields = NULL;
164     strncpy(new->key, key, 4);
165     
166     // only 3 char in a marc field name 
167     if (new->key[3] != '\0')
168         return 0;
169
170     new->val = nmem_malloc(marchash->nmem, sizeof (char) * strlen(val) + 1);
171     strtrimcpy(new->val, val);
172
173     return new;
174 }
175
176 struct marcsubfield *marchash_add_subfield(struct marchash *marchash,
177                                            struct marcfield *field,
178                                            const char key, const char *val)
179 {
180     struct marcsubfield *new;
181     struct marcsubfield *last;
182     last = NULL;
183     new = field->subfields;
184
185     while (new)
186     {
187         last = new;
188         new = new->next;
189     }
190
191     new = nmem_malloc(marchash->nmem, sizeof (struct marcsubfield));
192
193     if (last)
194         last->next = new;
195     else
196         field->subfields = new;
197
198     new->next = NULL;
199     new->key = key;
200     new->val = nmem_malloc(marchash->nmem, sizeof (char) * strlen(val) + 1);
201     strcpy(new->val, val);
202     return new;
203 }
204
205 struct marcfield *marchash_get_field (struct marchash *marchash,
206                                       const char *key, struct marcfield *last)
207 {
208     struct marcfield *cur;
209     if (last)
210         cur = last->next;
211     else 
212         cur = marchash->table[jenkins_hash((const unsigned char *)key) & MARCHASH_MASK];
213     while (cur)
214     {
215         if (!strcmp(cur->key, key))
216             return cur;
217         cur = cur->next;
218     }
219     return NULL;
220 }
221
222 struct marcsubfield *marchash_get_subfield(char key,
223                                            struct marcfield *field,
224                                            struct marcsubfield *last)
225 {
226     struct marcsubfield *cur;
227     if (last)
228         cur = last->next;
229     else
230         cur = field->subfields;
231     while (cur)
232     {
233         if (cur->key == key)
234           return cur;
235         cur = cur->next;
236     }
237     return NULL;
238 }
239
240 char *marchash_catenate_subfields(struct marcfield *field,
241                                   const char *delim, NMEM nmem)
242 {
243     char *output;
244     struct marcsubfield *cur;
245     int delimsize = strlen(delim);
246     int outsize = 1-delimsize;
247     // maybe it would make sense to have an nmem strcpy/strcat?
248     cur = field -> subfields;
249     while (cur)
250     {
251         outsize += strlen(cur->val) + delimsize;
252         cur = cur->next;
253     }  
254     if (outsize > 0)
255         output = nmem_malloc(nmem, outsize); 
256     else
257         return NULL;
258     *output = '\0';
259     cur = field -> subfields;
260     while (cur)
261     {
262         strtrimcat(output, cur->val);
263         if (cur->next)
264             strcat(output, delim); 
265         cur = cur->next;
266     } 
267     return output;
268 }
269 /*
270  * Local variables:
271  * c-basic-offset: 4
272  * c-file-style: "Stroustrup"
273  * indent-tabs-mode: nil
274  * End:
275  * vim: shiftwidth=4 tabstop=8 expandtab
276  */