Adjust marcdump so that it skips until record separator is read
[yaz-moved-to-github.git] / util / marcdump.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marcdump.c,v 1.49 2007-03-08 12:45:02 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 write_using_libxml2,
130                  int print_offset, const char *split_fname, int split_chunk,
131                  int verbose, FILE *cfile, const char *leader_spec)
132 {
133     yaz_marc_t mt = yaz_marc_create();
134     yaz_iconv_t cd = 0;
135
136     if (yaz_marc_leader_spec(mt, leader_spec))
137     {
138         fprintf(stderr, "bad leader spec: %s\n", leader_spec);
139         yaz_marc_destroy(mt);
140         exit(2);
141     }
142     if (from && to)
143     {
144         cd = yaz_iconv_open(to, from);
145         if (!cd)
146         {
147             fprintf(stderr, "conversion from %s to %s "
148                     "unsupported\n", from, to);
149             yaz_marc_destroy(mt);
150             exit(2);
151         }
152         yaz_marc_iconv(mt, cd);
153     }
154     yaz_marc_xml(mt, output_format);
155     yaz_marc_write_using_libxml2(mt, write_using_libxml2);
156     yaz_marc_debug(mt, verbose);
157
158     if (input_format == YAZ_MARC_MARCXML || input_format == YAZ_MARC_XCHANGE)
159     {
160 #if YAZ_HAVE_XML2
161         marcdump_read_xml(mt, fname);
162 #endif
163     }
164     else if (input_format == YAZ_MARC_LINE)
165     {
166         marcdump_read_line(mt, fname);
167     }
168     else if (input_format == YAZ_MARC_ISO2709)
169     {
170         FILE *inf = fopen(fname, "rb");
171         int num = 1;
172         int marc_no = 0;
173         int split_file_no = -1;
174         if (!inf)
175         {
176             fprintf (stderr, "%s: cannot open %s:%s\n",
177                      prog, fname, strerror (errno));
178             exit(1);
179         }
180         if (cfile)
181             fprintf (cfile, "char *marc_records[] = {\n");
182         for(;; marc_no++)
183         {
184             char *result = 0;
185             size_t len;
186             size_t rlen;
187             int len_result;
188             size_t r;
189             char buf[100001];
190             
191             r = fread (buf, 1, 5, inf);
192             if (r < 5)
193             {
194                 if (r && print_offset && verbose)
195                     printf ("<!-- Extra %ld bytes at end of file -->\n",
196                             (long) r);
197                 break;
198             }
199             while (*buf < '0' || *buf > '9')
200             {
201                 int i;
202                 long off = ftell(inf) - 5;
203                 if (verbose || print_offset)
204                     printf("<!-- Skipping bad byte %d (0x%02X) at offset "
205                            "%ld (0x%lx) -->\n", 
206                            *buf & 0xff, *buf & 0xff,
207                            off, off);
208                 for (i = 0; i<4; i++)
209                     buf[i] = buf[i+1];
210                 r = fread(buf+4, 1, 1, inf);
211                 if (r < 1)
212                     break;
213             }
214             if (r < 1)
215             {
216                 if (verbose || print_offset)
217                     printf ("<!-- End of file with data -->\n");
218                 break;
219             }
220             if (print_offset)
221             {
222                 long off = ftell(inf) - 5;
223                 printf ("<!-- Record %d offset %ld (0x%lx) -->\n",
224                         num, off, off);
225             }
226             len = atoi_n(buf, 5);
227             if (len < 25 || len > 100000)
228             {
229                 long off = ftell(inf) - 5;
230                 printf("Bad Length %ld read at offset %ld (%lx)\n",
231                        (long)len, (long) off, (long) off);
232                 break;
233             }
234             rlen = len - 5;
235             r = fread (buf + 5, 1, rlen, inf);
236             if (r < rlen)
237                 break;
238             while (buf[len-1] != ISO2709_RS)
239             {
240                 if (len > sizeof(buf)-2)
241                     break;
242                 r = fread (buf + len, 1, 1, inf);
243                 if (r != 1)
244                     break;
245                 len++;
246             }
247             if (split_fname)
248             {
249                 char fname[256];
250                 const char *mode = 0;
251                 FILE *sf;
252                 if ((marc_no % split_chunk) == 0)
253                 {
254                     mode = "wb";
255                     split_file_no++;
256                 }
257                 else
258                     mode = "ab";
259                 sprintf(fname, "%.200s%07d", split_fname, split_file_no);
260                 sf = fopen(fname, mode);
261                 if (!sf)
262                 {
263                     fprintf(stderr, "Could not open %s\n", fname);
264                     split_fname = 0;
265                 }
266                 else
267                 {
268                     if (fwrite(buf, 1, len, sf) != len)
269                     {
270                         fprintf(stderr, "Could write content to %s\n",
271                                 fname);
272                         split_fname = 0;
273                     }
274                     fclose(sf);
275                 }
276             }
277             len_result = (int) rlen;
278             r = yaz_marc_decode_buf(mt, buf, -1, &result, &len_result);
279             if (r > 0 && result)
280             {
281                 fwrite (result, len_result, 1, stdout);
282             }
283             if (r > 0 && cfile)
284             {
285                 char *p = buf;
286                 size_t i;
287                 if (marc_no)
288                     fprintf (cfile, ",");
289                 fprintf (cfile, "\n");
290                 for (i = 0; i < r; i++)
291                 {
292                     if ((i & 15) == 0)
293                         fprintf (cfile, "  \"");
294                     fprintf (cfile, "\\x%02X", p[i] & 255);
295                     
296                     if (i < r - 1 && (i & 15) == 15)
297                         fprintf (cfile, "\"\n");
298                     
299                 }
300                 fprintf (cfile, "\"\n");
301             }
302             num++;
303             if (verbose)
304                 printf("\n");
305         }
306         if (cfile)
307             fprintf (cfile, "};\n");
308         fclose(inf);
309     }
310     if (cd)
311         yaz_iconv_close(cd);
312     yaz_marc_destroy(mt);
313 }
314
315 int main (int argc, char **argv)
316 {
317     int r;
318     int print_offset = 0;
319     char *arg;
320     int verbose = 0;
321     int no = 0;
322     int output_format = YAZ_MARC_LINE;
323     FILE *cfile = 0;
324     char *from = 0, *to = 0;
325     int input_format = YAZ_MARC_ISO2709;
326     int split_chunk = 1;
327     const char *split_fname = 0;
328     const char *leader_spec = 0;
329     int write_using_libxml2 = 0;
330     
331 #if HAVE_LOCALE_H
332     setlocale(LC_CTYPE, "");
333 #endif
334 #if HAVE_LANGINFO_H
335 #ifdef CODESET
336     to = nl_langinfo(CODESET);
337 #endif
338 #endif
339
340     prog = *argv;
341     while ((r = options("i:o:C:npvc:xOeXIf:t:s:l:", argv, argc, &arg)) != -2)
342     {
343         no++;
344         switch (r)
345         {
346         case 'i':
347             input_format = yaz_marc_decode_formatstr(arg);
348             if (input_format == -1)
349             {
350                 fprintf(stderr, "%s: bad input format: %s\n", prog, arg);
351                 exit(1);
352             }
353 #if YAZ_HAVE_XML2
354 #else
355             if (input_format == YAZ_MARC_MARCXML 
356                 || input_format == YAZ_MARC_XCHANGE)
357             {
358                 fprintf(stderr, "%s: Libxml2 support not enabled\n", prog);
359                 exit(3);
360             }
361 #endif
362             break;
363         case 'o':
364             /* dirty hack so we can make Libxml2 do the writing ..
365                rather than WRBUF */
366             if (strlen(arg) > 4 && strncmp(arg, "xml,", 4) == 0)
367             {
368                 arg = arg + 4;
369                 write_using_libxml2 = 1;
370             }
371             output_format = yaz_marc_decode_formatstr(arg);
372             if (output_format == -1)
373             {
374                 fprintf(stderr, "%s: bad output format: %s\n", prog, arg);
375                 exit(1);
376             }
377             break;
378         case 'l':
379             leader_spec = arg;
380             break;
381         case 'f':
382             from = arg;
383             break;
384         case 't':
385             to = arg;
386             break;
387         case 'c':
388             if (cfile)
389                 fclose (cfile);
390             cfile = fopen(arg, "w");
391             break;
392         case 'x':
393             fprintf(stderr, "%s: -x no longer supported. "
394                     "Use -i marcxml instead\n", prog);
395             exit(1);
396             break;
397         case 'O':
398             fprintf(stderr, "%s: OAI MARC no longer supported."
399                     " Use MARCXML instead.\n", prog);
400             exit(1);
401             break;
402         case 'e':
403             fprintf(stderr, "%s: -e no longer supported. "
404                     "Use -o marcxchange instead\n", prog);
405             exit(1);
406             break;
407         case 'X':
408             fprintf(stderr, "%s: -X no longer supported. "
409                     "Use -o marcxml instead\n", prog);
410             exit(1);
411             break;
412         case 'I':
413             fprintf(stderr, "%s: -I no longer supported. "
414                     "Use -o marc instead\n", prog);
415             exit(1);
416             break;
417         case 'n':
418             output_format = YAZ_MARC_CHECK;
419             break;
420         case 'p':
421             print_offset = 1;
422             break;
423         case 's':
424             split_fname = arg;
425             break;
426         case 'C':
427             split_chunk = atoi(arg);
428             break;
429         case 0:
430             dump(arg, from, to, input_format, output_format,
431                  write_using_libxml2,
432                  print_offset, split_fname, split_chunk,
433                  verbose, cfile, leader_spec);
434             break;
435         case 'v':
436             verbose++;
437             break;
438         default:
439             usage(prog);
440             exit(1);
441         }
442     }
443     if (cfile)
444         fclose (cfile);
445     if (!no)
446     {
447         usage(prog);
448         exit (1);
449     }
450     exit (0);
451 }
452 /*
453  * Local variables:
454  * c-basic-offset: 4
455  * indent-tabs-mode: nil
456  * End:
457  * vim: shiftwidth=4 tabstop=8 expandtab
458  */
459