cbf73e1be2e37bc483a4a1f582399eb6ae5c8403
[idzebra-moved-to-github.git] / recctrl / csvread.c
1 /* $Id: csvread.c,v 1.4 2006-04-26 11:12:31 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24
25 #include <yaz/log.h>
26 #include <yaz/nmem.h>
27 #include <yaz/yaz-util.h>
28
29 /* #include <d1_absyn.h> */
30 #include <idzebra/data1.h>
31 #include <idzebra/recgrs.h>
32
33 /* #include <assert.h> */
34 #include <ctype.h>
35
36 /*
37 struct csv_getc_info {
38     char *buf;
39     int buf_size;
40     int size;
41     int off;
42     off_t moffset;
43     void *fh;
44     int (*readf)(void *, char *, size_t);
45     WRBUF wrbuf;
46 };
47 */
48
49 struct csv_t {
50   NMEM nmem;
51   int buf_size;
52   char *buf;
53   int name_size;
54   int value_size;
55   char *value;
56   char field_char;
57   char record_char;
58   char string_char;
59   char *root_element;
60   int field_line;
61   int lower_case;
62   int max_nr_fields;
63   int nr_fields;
64   /* char *field_names; */
65   char **field_name;
66 };
67
68
69 static ZEBRA_RES grs_config_csv(void *clientData, Res res, const char *args)
70 {
71   int i;
72   struct csv_t *csvp = (struct csv_t*) clientData;
73
74   yaz_log (YLOG_LOG, "Called CSV filter grs_config_csv");
75   yaz_log (YLOG_LOG, "'%s'", args);
76
77   csvp->buf_size = 64;
78   csvp->buf = nmem_malloc(csvp->nmem, csvp->buf_size);
79   csvp->name_size = 256;
80   csvp->value_size = 4096;
81   csvp->value = nmem_malloc(csvp->nmem, csvp->value_size);
82
83   csvp->field_char = '|';
84   csvp->record_char = '\n';
85   csvp->string_char = 0;    
86   csvp->root_element = nmem_strdup(csvp->nmem, "csv");
87   csvp->field_line = 1;
88   csvp->lower_case = 1;
89   csvp->max_nr_fields = 512;
90   csvp->nr_fields = 0;
91   /* csvp->field_names = 0; */ /*nmem_strdup(csvp->nmem, "a|b|c|d|e");*/
92
93   csvp->field_name 
94       = nmem_malloc(csvp->nmem, 
95                     sizeof(*(csvp->field_name)) * csvp->max_nr_fields);
96   for (i = 0; i < csvp->max_nr_fields; i++){
97     csvp->field_name[i] = 0; 
98   }
99
100   /* know field names from config file */
101   /*if (strlen(csvp->field_names))
102     yaz_log (YLOG_LOG, "CSV filter grs_config_csv field names");
103   */
104
105   yaz_log (YLOG_LOG, "Ended CSV filter grs_config_csv");
106   return ZEBRA_OK;
107 }
108
109
110 static data1_node *grs_read_csv (struct grs_read_info *gri)
111 {
112   data1_node *root_node = 0;
113   data1_node *node = 0;
114   struct csv_t *csvp = (struct csv_t *)gri->clientData;
115   int field_nr = 0; 
116   int end_of_record = 0;
117   int read_header = 0;
118   int read_bytes = 0;
119   char *cb = csvp->buf;
120   char *cv = csvp->value; 
121
122   yaz_log (YLOG_LOG, "Called CSV filter grs_read_csv");
123
124   /* if on start of first line, read header line for dynamic configure */ 
125   if(csvp->field_line && gri->offset == 0)
126     read_header = 1;
127
128   while (!end_of_record){
129
130 #if 0    
131     /* configure grs.csv filter with first line in file containing field 
132        name information */
133     if (read_header){
134       yaz_log (YLOG_LOG, "CSV filter grs_read_csv reading header line");
135       
136       /* create new memory for fieldname and value */
137       if (old_nr_fields < csvp->nr_fields){
138         yaz_log(YLOG_LOG, 
139                 "CSV filter grs_read_csv name:'%d' ", csvp->nr_fields);
140         old_nr_fields = csvp->nr_fields;
141         csvp->field_name[csvp->nr_fields] 
142           = nmem_malloc(csvp->nmem, csvp->name_size);
143         csvp->field_value[csvp->nr_fields] 
144           = nmem_malloc(csvp->nmem, csvp->value_size);
145
146         /* read buf and copy values to field_name[] */  
147         read_bytes = (*gri->readf)(gri->fh, csvp->buf, csvp->buf_size);
148        gri-> offset = (*gri->tellf)(gri->fh);
149         /* yaz_log(YLOG_LOG, "CSV filter grs_read_csv offset:'%d' ", offset); */
150         read_header = 0;
151       }
152     } else {
153       /* read buf and copy values to field_value[] */  
154       read_bytes = (*gri->readf)(gri->fh, csvp->buf, csvp->buf_size);
155       gri->offset = (*gri->tellf)(gri->fh);
156       yaz_log(YLOG_LOG, "CSV filter grs_read_csv offset:'%d' ", offset);
157     }
158     
159 #endif
160
161
162     /* read new buffer from file */  
163     read_bytes = (*gri->readf)(gri->fh, csvp->buf, csvp->buf_size);
164
165     yaz_log (YLOG_LOG, "CSV filter grs_read_csv read_bytes  %d", read_bytes);
166     yaz_log (YLOG_LOG, "CSV filter grs_read_csv csvp->buf %s", csvp->buf);
167
168     gri->offset = (*gri->tellf)(gri->fh);
169     yaz_log(YLOG_LOG, "CSV filter grs_read_csv gri->offset:'%d' ", 
170             (int)gri->offset);
171
172     /* work on buffer */
173     cb = csvp->buf;
174     while ((cb - csvp->buf < read_bytes)
175            && (cv - csvp->value < csvp->value_size)
176            && !end_of_record){
177
178       if (*cb == csvp->field_char){
179         /* if field finished */
180         *cv = '\0';
181         if (read_header){
182           /* read field names from header line */
183           if (csvp->nr_fields < csvp->max_nr_fields){
184             csvp->field_name[csvp->nr_fields] 
185               = nmem_strdup(csvp->nmem, csvp->value);
186             
187             csvp->nr_fields++;
188             yaz_log (YLOG_LOG, "CSV filter grs_read_csv field %d name '%s'", 
189                        field_nr, csvp->value);
190           } else {
191             yaz_log (YLOG_WARN, "CSV filter grs_read_csv field %d name '%s' "
192                      "exceeds configured max number of fields %d", 
193                      field_nr, csvp->value, csvp->max_nr_fields);
194           }
195         } else {
196           /* process following value line fields */
197           if (field_nr < csvp->nr_fields){
198             /* less or qual fields number */
199             yaz_log (YLOG_LOG, "CSV filter grs_read_csv field %d %s: '%s'", 
200                      field_nr, csvp->field_name[field_nr], csvp->value);
201           } else {
202           /* too many fields */
203             yaz_log (YLOG_WARN, "CSV filter grs_read_csv field value %d %s "
204                      "exceeds dynamic configured number of fields %d", 
205                      field_nr, csvp->value, csvp->nr_fields);
206           }
207           
208         }
209         /* advance buffer and proceed to next field */
210         cb++;
211         cv = csvp->value;
212         field_nr++;
213       } else if (*cb == csvp->record_char){
214         /* if record finished */
215         /* advance buffer and proceed to record */
216         *cv = '\0';
217         cb++;
218         cv = csvp->value;
219         field_nr = 0;
220         if (read_header){
221           read_header = 0;
222           yaz_log (YLOG_LOG, "CSV filter grs_read_csv header end");
223         } else {
224           end_of_record = 1;
225           yaz_log (YLOG_LOG, "CSV filter grs_read_csv record end");
226         }
227       } else {
228         /* just plain char to be stored in value, no special action at all */
229         if (csvp->lower_case && read_header){
230           *cv = tolower(*cb);
231         } else {
232           *cv = *cb;
233         }
234          cb++;
235          cv++;
236       }
237     }
238   
239       
240     /* if (gri->endf)
241       (*gri->endf)(gri->fh, offset - 1);  */
242   }
243
244   /* try to build GRS node and document */
245   
246   root_node = data1_mk_root(gri->dh, gri->mem, csvp->root_element);
247   node = data1_mk_node2(gri->dh, gri->mem, DATA1N_data, root_node);
248   node = data1_mk_tag(gri->dh, gri->mem, "pr_name_gn", 0, node);  
249   data1_mk_text_n(gri->dh, gri->mem, csvp->buf, read_bytes, node);
250   
251   if (!root_node){
252     yaz_log (YLOG_WARN, "empty CSV record of type '%s' "
253              "near file offset %d "
254              "or missing abstract syntax file '%s.abs'",
255              csvp->root_element, (int)gri->offset, csvp->root_element);
256     return 0;
257   }
258
259   yaz_log (YLOG_LOG, "Ended CSV filter grs_read_csv");
260   return root_node;
261 }
262
263 static void *grs_init_csv(Res res, RecType recType)
264 {
265   NMEM m = nmem_create();
266   struct csv_t *csvp = (struct csv_t *) nmem_malloc(m, sizeof(*csvp));
267   yaz_log (YLOG_LOG, "Called CSV filter grs_init_csv");
268   csvp->nmem = m;
269   yaz_log (YLOG_LOG, "Ended CSV filter grs_init_csv");
270   return csvp;
271 }
272
273 static void grs_destroy_csv(void *clientData)
274 {
275   struct csv_t *csvp = (struct csv_t*) clientData;
276
277   yaz_log (YLOG_LOG, "Called CSV filter grs_destroy_csv");
278
279   nmem_destroy(csvp->nmem);
280   clientData = 0;
281
282   yaz_log (YLOG_LOG, "Ended CSV filter grs_destroy_csv");
283 }
284
285 static int grs_extract_csv(void *clientData, struct recExtractCtrl *ctrl)
286 {
287   int res;
288   /* struct csv_t *csvp = (struct csv_t*) clientData; */
289
290   yaz_log (YLOG_LOG, "Called CSV filter grs_extract_csv");
291   yaz_log (YLOG_LOG, "recExtractCtr fh     %d", (int)ctrl->fh);
292   yaz_log (YLOG_LOG, "recExtractCtr offset %d", (int)ctrl->offset);
293
294   res = zebra_grs_extract(clientData, ctrl, grs_read_csv);
295
296   yaz_log (YLOG_LOG, "recExtractCtr fh     %d", (int)ctrl->fh);
297   yaz_log (YLOG_LOG, "recExtractCtr offset %d", (int)ctrl->offset);
298   yaz_log (YLOG_LOG, "Ended CSV filter grs_extract_csv");
299
300   return res;
301 }
302
303 static int grs_retrieve_csv(void *clientData, struct recRetrieveCtrl *ctrl)
304 {
305   int res;
306   /* struct csv_t *csvp = (struct csv_t*) clientData; */
307   
308   yaz_log (YLOG_LOG, "Called CSV filter grs_retrieve_csv");
309   res = zebra_grs_retrieve(clientData, ctrl, grs_read_csv);
310   yaz_log (YLOG_LOG, "Ended CSV filter grs_retrieve_csv");
311
312   return res;
313 }
314
315 static struct recType grs_type_csv =
316 {
317     0,
318     "grs.csv",
319     grs_init_csv,
320     grs_config_csv,
321     grs_destroy_csv,
322     grs_extract_csv,
323     grs_retrieve_csv
324 };
325
326 RecType
327 #ifdef IDZEBRA_STATIC_GRS_CSV
328 idzebra_filter_grs_csv
329 #else
330 idzebra_filter
331 #endif
332
333 [] = {
334     &grs_type_csv,
335     0,
336 };