First stab at a marc map as a fast alternat to XSLT
[pazpar2-moved-to-github.git] / src / marchash.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4
5 #include <libxml/tree.h>
6 #include <libxml/parser.h>
7 #include <yaz/nmem.h>
8
9 #include <marchash.h>
10
11 // Jenkins one-at-a-time hash (from pp2 reclists.c, wikipedia)
12 static unsigned int hash(const unsigned char *key)
13 {
14     unsigned int hash = 0;
15
16     while (*key)
17     {
18         hash += *(key++);
19         hash += (hash << 10);
20         hash ^= (hash >> 6);
21     }
22     hash += (hash << 3);
23     hash ^= (hash >> 11);
24     hash += (hash << 15);
25     return hash;
26 }
27
28 inline char *strtrimcat (char *dest, char *src)
29 {
30     char *in;
31     char *out;
32     char *last_nonspace;
33     in = src;
34     out = dest;
35     // move to end of dest
36     while (*out)
37         out++;
38     // initialise last non-space charater
39     last_nonspace = out;
40     // skip leading whitespace
41     while (isspace(*in))
42         in++;
43     while (*in)
44     {
45         *out = *in;
46         if (!isspace(*in))
47             last_nonspace = out;
48         out++;
49         in++;
50     }
51     *(++last_nonspace) = '\0';
52 }
53
54 inline char *strtrimcpy (char *dest, char *src)
55 {
56     *dest = '\0';
57     strtrimcat(dest, src);
58 }
59
60 struct marchash *marchash_create (NMEM nmem)
61 {
62     struct marchash *new;
63     new = nmem_malloc(nmem, sizeof (struct marchash));
64     memset(new, 0, sizeof (struct marchash));
65     new->nmem = nmem;
66     return new;
67 }
68
69 int marchash_ingest_marcxml (struct marchash *marchash, xmlNodePtr rec_node)
70 {
71      xmlNodePtr field_node;
72      xmlNodePtr sub_node;
73      field_node = rec_node->children;
74      struct marcfield *field;
75
76      while (field_node)
77      {
78          if (field_node->type == XML_ELEMENT_NODE)
79          {
80              field = NULL;
81              if (!strcmp(field_node->name, "controlfield"))
82              {
83                  field = marchash_add_field(marchash, xmlGetProp(field_node, "tag"), xmlNodeGetContent(field_node));
84              }
85              else if (!strcmp(field_node->name, "datafield"))
86              {
87                  field = marchash_add_field(marchash, xmlGetProp(field_node, "tag"), xmlNodeGetContent(field_node));
88              }
89              if (field)
90              {
91                  sub_node = field_node->children;
92                  while (sub_node) 
93                  {
94                      if ((sub_node->type == XML_ELEMENT_NODE) && (!strcmp(sub_node->name, "subfield")))
95                      {
96                          marchash_add_subfield(marchash, field, xmlGetProp(sub_node, "code")[0], xmlNodeGetContent(sub_node));
97                      }
98                      sub_node = sub_node->next;
99                  } 
100              }
101          }
102          field_node = field_node->next;
103      }
104 }
105
106 struct marcfield *marchash_add_field (struct marchash *marchash, char *key, char *val)
107 {
108     int slot;
109     struct marcfield *new;
110     struct marcfield *last;
111     
112     slot = hash(key) & MARCHASH_MASK;
113     new = marchash->table[slot];
114     last = NULL;
115     
116     while (new) 
117     {
118         last = new; 
119         new = new->next;     
120     }
121
122     new = nmem_malloc(marchash->nmem, sizeof (struct marcfield));
123
124     if (last)
125         last->next = new;
126     else
127         marchash->table[slot] = new;
128
129     new->next = NULL;
130     new->subfields = NULL;
131     strncpy(new->key, key, 4);
132     
133     // only 3 char in a marc field name 
134     if (new->key[3] != '\0')
135         return 0;
136
137     new->val = nmem_malloc(marchash->nmem, sizeof (char) * strlen(val) + 1);
138     strtrimcpy(new->val, val);
139
140     return new;
141 }
142
143 struct marcsubfield *marchash_add_subfield (struct marchash *marchash, struct marcfield *field, char key, char *val)
144 {
145     struct marcsubfield *new;
146     struct marcsubfield *last;
147     last = NULL;
148     new = field->subfields;
149
150     while (new)
151     {
152         last = new;
153         new = new->next;
154     }
155
156     new = nmem_malloc(marchash->nmem, sizeof (struct marcsubfield));
157
158     if (last)
159         last->next = new;
160     else
161         field->subfields = new;
162
163     new->next = NULL;
164     new->key = key;
165     new->val = nmem_malloc(marchash->nmem, sizeof (char) * strlen(val) + 1);
166     strcpy(new->val, val);
167     return new;
168 }
169
170 struct marcfield *marchash_get_field (struct marchash *marchash, char *key, struct marcfield *last)
171 {
172     struct marcfield *cur;
173     if (last)
174         cur = last->next;
175     else 
176         cur = marchash->table[hash(key) & MARCHASH_MASK];
177     while (cur)
178     {
179         if (!strcmp(cur->key, key))
180             return cur;
181         cur = cur->next;
182     }
183     return NULL;
184 }
185
186 struct marcsubfield *marchash_get_subfield (char key, struct marcfield *field, struct marcsubfield *last)
187 {
188     struct marcsubfield *cur;
189     if (last)
190         cur = last->next;
191     else
192         cur = field->subfields;
193     while (cur)
194     {
195         if (cur->key == key)
196           return cur;
197         cur = cur->next;
198     }
199     return NULL;
200 }
201
202 char *marchash_catenate_subfields (struct marcfield *field, char *delim, NMEM nmem)
203 {
204     char *output;
205     struct marcsubfield *cur;
206     int delimsize = strlen(delim);
207     int outsize = 1-delimsize;
208     // maybe it would make sense to have an nmem strcpy/strcat?
209     cur = field -> subfields;
210     while (cur)
211     {
212         outsize += strlen(cur->val) + delimsize;
213         cur = cur->next;
214     }  
215     if (outsize > 0)
216         output = nmem_malloc(nmem, outsize); 
217     else
218         return NULL;
219     *output = '\0';
220     cur = field -> subfields;
221     while (cur)
222     {
223         strtrimcat(output, cur->val);
224         if (cur->next)
225             strcat(output, delim); 
226         cur = cur->next;
227     } 
228     return output;
229 }