Fixed bug #830: pkg-config support. YAZ installs yaz.pc for Debian
[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.47 2007-01-03 08:42:16 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             if (split_fname)
239             {
240                 char fname[256];
241                 const char *mode = 0;
242                 FILE *sf;
243                 if ((marc_no % split_chunk) == 0)
244                 {
245                     mode = "wb";
246                     split_file_no++;
247                 }
248                 else
249                     mode = "ab";
250                 sprintf(fname, "%.200s%07d", split_fname, split_file_no);
251                 sf = fopen(fname, mode);
252                 if (!sf)
253                 {
254                     fprintf(stderr, "Could not open %s\n", fname);
255                     split_fname = 0;
256                 }
257                 else
258                 {
259                     if (fwrite(buf, 1, len, sf) != len)
260                     {
261                         fprintf(stderr, "Could write content to %s\n",
262                                 fname);
263                         split_fname = 0;
264                     }
265                     fclose(sf);
266                 }
267             }
268             len_result = (int) rlen;
269             r = yaz_marc_decode_buf(mt, buf, -1, &result, &len_result);
270             if (r > 0 && result)
271             {
272                 fwrite (result, len_result, 1, stdout);
273             }
274             if (r > 0 && cfile)
275             {
276                 char *p = buf;
277                 size_t i;
278                 if (marc_no)
279                     fprintf (cfile, ",");
280                 fprintf (cfile, "\n");
281                 for (i = 0; i < r; i++)
282                 {
283                     if ((i & 15) == 0)
284                         fprintf (cfile, "  \"");
285                     fprintf (cfile, "\\x%02X", p[i] & 255);
286                     
287                     if (i < r - 1 && (i & 15) == 15)
288                         fprintf (cfile, "\"\n");
289                     
290                 }
291                 fprintf (cfile, "\"\n");
292             }
293             num++;
294             if (verbose)
295                 printf("\n");
296         }
297         if (cfile)
298             fprintf (cfile, "};\n");
299         fclose(inf);
300     }
301     if (cd)
302         yaz_iconv_close(cd);
303     yaz_marc_destroy(mt);
304 }
305
306 int main (int argc, char **argv)
307 {
308     int r;
309     int print_offset = 0;
310     char *arg;
311     int verbose = 0;
312     int no = 0;
313     int output_format = YAZ_MARC_LINE;
314     FILE *cfile = 0;
315     char *from = 0, *to = 0;
316     int input_format = YAZ_MARC_ISO2709;
317     int split_chunk = 1;
318     const char *split_fname = 0;
319     const char *leader_spec = 0;
320     int write_using_libxml2 = 0;
321     
322 #if HAVE_LOCALE_H
323     setlocale(LC_CTYPE, "");
324 #endif
325 #if HAVE_LANGINFO_H
326 #ifdef CODESET
327     to = nl_langinfo(CODESET);
328 #endif
329 #endif
330
331     prog = *argv;
332     while ((r = options("i:o:C:npvc:xOeXIf:t:s:l:", argv, argc, &arg)) != -2)
333     {
334         no++;
335         switch (r)
336         {
337         case 'i':
338             input_format = yaz_marc_decode_formatstr(arg);
339             if (input_format == -1)
340             {
341                 fprintf(stderr, "%s: bad input format: %s\n", prog, arg);
342                 exit(1);
343             }
344             break;
345         case 'o':
346             /* dirty hack so we can make Libxml2 do the writing ..
347                rather than WRBUF */
348             if (strlen(arg) > 4 && strncmp(arg, "xml,", 4) == 0)
349             {
350                 arg = arg + 4;
351                 write_using_libxml2 = 1;
352             }
353             output_format = yaz_marc_decode_formatstr(arg);
354             if (output_format == -1)
355             {
356                 fprintf(stderr, "%s: bad output format: %s\n", prog, arg);
357                 exit(1);
358             }
359             break;
360         case 'l':
361             leader_spec = arg;
362             break;
363         case 'f':
364             from = arg;
365             break;
366         case 't':
367             to = arg;
368             break;
369         case 'c':
370             if (cfile)
371                 fclose (cfile);
372             cfile = fopen(arg, "w");
373             break;
374         case 'x':
375             fprintf(stderr, "%s: -x no longer supported. "
376                     "Use -i marcxml instead\n", prog);
377             exit(1);
378             break;
379         case 'O':
380             fprintf(stderr, "%s: OAI MARC no longer supported."
381                     " Use MARCXML instead.\n", prog);
382             exit(1);
383             break;
384         case 'e':
385             fprintf(stderr, "%s: -e no longer supported. "
386                     "Use -o marcxchange instead\n", prog);
387             exit(1);
388             break;
389         case 'X':
390             fprintf(stderr, "%s: -X no longer supported. "
391                     "Use -o marcxml instead\n", prog);
392             exit(1);
393             break;
394         case 'I':
395             fprintf(stderr, "%s: -I no longer supported. "
396                     "Use -o marc instead\n", prog);
397             exit(1);
398             break;
399         case 'n':
400             output_format = YAZ_MARC_CHECK;
401             break;
402         case 'p':
403             print_offset = 1;
404             break;
405         case 's':
406             split_fname = arg;
407             break;
408         case 'C':
409             split_chunk = atoi(arg);
410             break;
411         case 0:
412             dump(arg, from, to, input_format, output_format,
413                  write_using_libxml2,
414                  print_offset, split_fname, split_chunk,
415                  verbose, cfile, leader_spec);
416             break;
417         case 'v':
418             verbose++;
419             break;
420         default:
421             usage(prog);
422             exit(1);
423         }
424     }
425     if (cfile)
426         fclose (cfile);
427     if (!no)
428     {
429         usage(prog);
430         exit (1);
431     }
432     exit (0);
433 }
434 /*
435  * Local variables:
436  * c-basic-offset: 4
437  * indent-tabs-mode: nil
438  * End:
439  * vim: shiftwidth=4 tabstop=8 expandtab
440  */
441