Implemented function yaz_marc_read_line which parses MARC line format
[yaz-moved-to-github.git] / util / marcdump.c
1 /*
2  * Copyright (C) 1995-2006, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marcdump.c,v 1.45 2006-12-15 19:28:48 adam Exp $
6  */
7
8 #define _FILE_OFFSET_BITS 64
9
10 #if HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #if YAZ_HAVE_XML2
15 #include <libxml/parser.h>
16 #include <libxml/tree.h>
17
18 #include <libxml/xpath.h>
19 #include <libxml/xpathInternals.h>
20
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #if HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32 #if HAVE_LANGINFO_H
33 #include <langinfo.h>
34 #endif
35
36 #include <yaz/marcdisp.h>
37 #include <yaz/yaz-util.h>
38 #include <yaz/xmalloc.h>
39 #include <yaz/options.h>
40
41 #ifndef SEEK_SET
42 #define SEEK_SET 0
43 #endif
44 #ifndef SEEK_END
45 #define SEEK_END 2
46 #endif
47
48
49 static char *prog;
50
51 static void usage(const char *prog)
52 {
53     fprintf (stderr, "Usage: %s [-c cfile] [-f from] [-t to] "
54              "[-i format] [-o format] "
55              "[-n] [-l pos=value] [-v] [-C chunk] [-s splitfname] file...\n",
56              prog);
57
58
59 static int getbyte_stream(void *client_data)
60 {
61     FILE *f = (FILE*) client_data;
62
63     int c = fgetc(f);
64     if (c == EOF)
65         return 0;
66     return c;
67 }
68
69 static void ungetbyte_stream(int c, void *client_data)
70 {
71     FILE *f = (FILE*) client_data;
72
73     if (c == 0)
74         c = EOF;
75     ungetc(c, f);
76 }
77
78 static void marcdump_read_line(yaz_marc_t mt, const char *fname)
79 {
80     FILE *inf = fopen(fname, "rb");
81     if (!inf)
82     {
83         fprintf (stderr, "%s: cannot open %s:%s\n",
84                  prog, fname, strerror (errno));
85         exit(1);
86     }
87     
88     while (yaz_marc_read_line(mt, getbyte_stream,
89                               ungetbyte_stream, inf) == 0)
90     {
91         WRBUF wrbuf = wrbuf_alloc();
92         yaz_marc_write_mode(mt, wrbuf);
93         fputs(wrbuf_buf(wrbuf), stdout);
94         wrbuf_free(wrbuf, 1);
95     }
96     fclose(inf);
97 }
98
99 #if YAZ_HAVE_XML2
100 static void marcdump_read_xml(yaz_marc_t mt, const char *fname)
101 {
102     xmlNodePtr ptr;
103     xmlDocPtr doc = xmlParseFile(fname);
104     if (!doc)
105         return;
106
107     ptr = xmlDocGetRootElement(doc);
108     if (ptr)
109     {
110         int r;
111         WRBUF wrbuf = wrbuf_alloc();
112         r = yaz_marc_read_xml(mt, ptr);
113         if (r)
114             fprintf(stderr, "yaz_marc_read_xml failed\n");
115         else
116         {
117             yaz_marc_write_mode(mt, wrbuf);
118             
119             fputs(wrbuf_buf(wrbuf), stdout);
120         }
121         wrbuf_free(wrbuf, 1);
122     }
123     xmlFreeDoc(doc);
124 }
125 #endif
126
127 static void dump(const char *fname, const char *from, const char *to,
128                  int input_format, int output_format,
129                  int print_offset, const char *split_fname, int split_chunk,
130                  int verbose, FILE *cfile, const char *leader_spec)
131 {
132     yaz_marc_t mt = yaz_marc_create();
133     yaz_iconv_t cd = 0;
134
135     if (yaz_marc_leader_spec(mt, leader_spec))
136     {
137         fprintf(stderr, "bad leader spec: %s\n", leader_spec);
138         yaz_marc_destroy(mt);
139         exit(2);
140     }
141     if (from && to)
142     {
143         cd = yaz_iconv_open(to, from);
144         if (!cd)
145         {
146             fprintf(stderr, "conversion from %s to %s "
147                     "unsupported\n", from, to);
148             yaz_marc_destroy(mt);
149             exit(2);
150         }
151         yaz_marc_iconv(mt, cd);
152     }
153     yaz_marc_xml(mt, output_format);
154     yaz_marc_debug(mt, verbose);
155
156     if (input_format == YAZ_MARC_MARCXML || input_format == YAZ_MARC_XCHANGE)
157     {
158 #if YAZ_HAVE_XML2
159         marcdump_read_xml(mt, fname);
160 #endif
161     }
162     else if (input_format == YAZ_MARC_LINE)
163     {
164         marcdump_read_line(mt, fname);
165     }
166     else if (input_format == YAZ_MARC_ISO2709)
167     {
168         FILE *inf = fopen(fname, "rb");
169         int num = 1;
170         int marc_no = 0;
171         int split_file_no = -1;
172         if (!inf)
173         {
174             fprintf (stderr, "%s: cannot open %s:%s\n",
175                      prog, fname, strerror (errno));
176             exit(1);
177         }
178         if (cfile)
179             fprintf (cfile, "char *marc_records[] = {\n");
180         for(;; marc_no++)
181         {
182             char *result = 0;
183             size_t len;
184             size_t rlen;
185             int len_result;
186             size_t r;
187             char buf[100001];
188             
189             r = fread (buf, 1, 5, inf);
190             if (r < 5)
191             {
192                 if (r && print_offset && verbose)
193                     printf ("<!-- Extra %ld bytes at end of file -->\n",
194                             (long) r);
195                 break;
196             }
197             while (*buf < '0' || *buf > '9')
198             {
199                 int i;
200                 long off = ftell(inf) - 5;
201                 if (verbose || print_offset)
202                     printf("<!-- Skipping bad byte %d (0x%02X) at offset "
203                            "%ld (0x%lx) -->\n", 
204                            *buf & 0xff, *buf & 0xff,
205                            off, off);
206                 for (i = 0; i<4; i++)
207                     buf[i] = buf[i+1];
208                 r = fread(buf+4, 1, 1, inf);
209                 if (r < 1)
210                     break;
211             }
212             if (r < 1)
213             {
214                 if (verbose || print_offset)
215                     printf ("<!-- End of file with data -->\n");
216                 break;
217             }
218             if (print_offset)
219             {
220                 long off = ftell(inf) - 5;
221                 printf ("<!-- Record %d offset %ld (0x%lx) -->\n",
222                         num, off, off);
223             }
224             len = atoi_n(buf, 5);
225             if (len < 25 || len > 100000)
226             {
227                 long off = ftell(inf) - 5;
228                 printf("Bad Length %ld read at offset %ld (%lx)\n",
229                        (long)len, (long) off, (long) off);
230                 break;
231             }
232             rlen = len - 5;
233             r = fread (buf + 5, 1, rlen, inf);
234             if (r < rlen)
235                 break;
236             if (split_fname)
237             {
238                 char fname[256];
239                 const char *mode = 0;
240                 FILE *sf;
241                 if ((marc_no % split_chunk) == 0)
242                 {
243                     mode = "wb";
244                     split_file_no++;
245                 }
246                 else
247                     mode = "ab";
248                 sprintf(fname, "%.200s%07d", split_fname, split_file_no);
249                 sf = fopen(fname, mode);
250                 if (!sf)
251                 {
252                     fprintf(stderr, "Could not open %s\n", fname);
253                     split_fname = 0;
254                 }
255                 else
256                 {
257                     if (fwrite(buf, 1, len, sf) != len)
258                     {
259                         fprintf(stderr, "Could write content to %s\n",
260                                 fname);
261                         split_fname = 0;
262                     }
263                     fclose(sf);
264                 }
265             }
266             len_result = (int) rlen;
267             r = yaz_marc_decode_buf(mt, buf, -1, &result, &len_result);
268             if (r > 0 && result)
269             {
270                 fwrite (result, len_result, 1, stdout);
271             }
272             if (r > 0 && cfile)
273             {
274                 char *p = buf;
275                 size_t i;
276                 if (marc_no)
277                     fprintf (cfile, ",");
278                 fprintf (cfile, "\n");
279                 for (i = 0; i < r; i++)
280                 {
281                     if ((i & 15) == 0)
282                         fprintf (cfile, "  \"");
283                     fprintf (cfile, "\\x%02X", p[i] & 255);
284                     
285                     if (i < r - 1 && (i & 15) == 15)
286                         fprintf (cfile, "\"\n");
287                     
288                 }
289                 fprintf (cfile, "\"\n");
290             }
291             num++;
292             if (verbose)
293                 printf("\n");
294         }
295         if (cfile)
296             fprintf (cfile, "};\n");
297         fclose(inf);
298     }
299     if (cd)
300         yaz_iconv_close(cd);
301     yaz_marc_destroy(mt);
302 }
303
304 int main (int argc, char **argv)
305 {
306     int r;
307     int print_offset = 0;
308     char *arg;
309     int verbose = 0;
310     int no = 0;
311     int output_format = YAZ_MARC_LINE;
312     FILE *cfile = 0;
313     char *from = 0, *to = 0;
314     int input_format = YAZ_MARC_ISO2709;
315     int split_chunk = 1;
316     const char *split_fname = 0;
317     const char *leader_spec = 0;
318     
319 #if HAVE_LOCALE_H
320     setlocale(LC_CTYPE, "");
321 #endif
322 #if HAVE_LANGINFO_H
323 #ifdef CODESET
324     to = nl_langinfo(CODESET);
325 #endif
326 #endif
327
328     prog = *argv;
329     while ((r = options("i:o:C:npvc:xOeXIf:t:s:l:", argv, argc, &arg)) != -2)
330     {
331         no++;
332         switch (r)
333         {
334         case 'i':
335             input_format = yaz_marc_decode_formatstr(arg);
336             if (input_format == -1)
337             {
338                 fprintf(stderr, "%s: bad input format: %s\n", prog, arg);
339                 exit(1);
340             }
341             break;
342         case 'o':
343             output_format = yaz_marc_decode_formatstr(arg);
344             if (output_format == -1)
345             {
346                 fprintf(stderr, "%s: bad output format: %s\n", prog, arg);
347                 exit(1);
348             }
349             break;
350         case 'l':
351             leader_spec = arg;
352             break;
353         case 'f':
354             from = arg;
355             break;
356         case 't':
357             to = arg;
358             break;
359         case 'c':
360             if (cfile)
361                 fclose (cfile);
362             cfile = fopen(arg, "w");
363             break;
364         case 'x':
365             fprintf(stderr, "%s: -x no longer supported. "
366                     "Use -i marcxml instead\n", prog);
367             exit(1);
368             break;
369         case 'O':
370             fprintf(stderr, "%s: OAI MARC no longer supported."
371                     " Use MARCXML instead.\n", prog);
372             exit(1);
373             break;
374         case 'e':
375             fprintf(stderr, "%s: -e no longer supported. "
376                     "Use -o marcxchange instead\n", prog);
377             exit(1);
378             break;
379         case 'X':
380             fprintf(stderr, "%s: -X no longer supported. "
381                     "Use -o marcxml instead\n", prog);
382             exit(1);
383             break;
384         case 'I':
385             fprintf(stderr, "%s: -I no longer supported. "
386                     "Use -o marc instead\n", prog);
387             exit(1);
388             break;
389         case 'n':
390             output_format = YAZ_MARC_CHECK;
391             break;
392         case 'p':
393             print_offset = 1;
394             break;
395         case 's':
396             split_fname = arg;
397             break;
398         case 'C':
399             split_chunk = atoi(arg);
400             break;
401         case 0:
402             dump(arg, from, to, input_format, output_format,
403                  print_offset, split_fname, split_chunk,
404                  verbose, cfile, leader_spec);
405             break;
406         case 'v':
407             verbose++;
408             break;
409         default:
410             usage(prog);
411             exit(1);
412         }
413     }
414     if (cfile)
415         fclose (cfile);
416     if (!no)
417     {
418         usage(prog);
419         exit (1);
420     }
421     exit (0);
422 }
423 /*
424  * Local variables:
425  * c-basic-offset: 4
426  * indent-tabs-mode: nil
427  * End:
428  * vim: shiftwidth=4 tabstop=8 expandtab
429  */
430