Improve display of MARC records with multi-byte subfield IDs YAZ-695
[yaz-moved-to-github.git] / src / odr.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file odr.c
8  * \brief Implements fundamental ODR functionality
9  */
10
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <assert.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19
20 #include <yaz/xmalloc.h>
21 #include <yaz/log.h>
22 #include <yaz/snprintf.h>
23 #include "odr-priv.h"
24
25 static int log_level = 0;
26 static int log_level_initialized = 0;
27
28 Odr_null *ODR_NULLVAL = (Odr_null *) "NULL";  /* the presence of a null value */
29
30 Odr_null *odr_nullval(void)
31 {
32     return ODR_NULLVAL;
33 }
34
35 char *odr_errlist[] =
36 {
37     "No (unknown) error",
38     "Memory allocation failed",
39     "System error",
40     "No space in buffer",
41     "Required data element missing",
42     "Unexpected tag",
43     "Other error",
44     "Protocol error",
45     "Malformed data",
46     "Stack overflow",
47     "Length of constructed type different from sum of members",
48     "Overflow writing definite length of constructed type",
49     "Bad HTTP Request"
50 };
51
52 char *odr_errmsg(int n)
53 {
54     return odr_errlist[n];
55 }
56
57 void odr_perror(ODR o, const char *message)
58 {
59     const char *e = odr_getelement(o);
60     const char **element_path = odr_get_element_path(o);
61     int err, x;
62
63     err =  odr_geterrorx(o, &x);
64     fprintf(stderr, "%s: %s (code %d:%d)", message, odr_errlist[err], err, x);
65     if (e && *e)
66         fprintf(stderr, " element %s", e);
67
68     fprintf(stderr, "\n");
69     if (element_path)
70     {
71         fprintf(stderr, "Element path:");
72         while (*element_path)
73             fprintf(stderr, " %s", *element_path++);
74         fprintf(stderr, "\n");
75     }
76 }
77
78 int odr_geterror(ODR o)
79 {
80     return o->error;
81 }
82
83 int odr_geterrorx(ODR o, int *x)
84 {
85     if (x)
86         *x = o->op->error_id;
87     return o->error;
88 }
89
90 const char *odr_getelement(ODR o)
91 {
92     return o->op->element;
93 }
94
95 const char **odr_get_element_path(ODR o)
96 {
97     int cur_sz = 0;
98     struct odr_constack *st;
99
100     for (st = o->op->stack_top; st; st = st->prev)
101         cur_sz++;
102     if (o->op->tmp_names_sz < cur_sz + 1)
103     {
104         o->op->tmp_names_sz = 2 * cur_sz + 5;
105         o->op->tmp_names_buf = (const char **)
106             odr_malloc(o, o->op->tmp_names_sz * sizeof(char*));
107     }
108     o->op->tmp_names_buf[cur_sz] = 0;
109     for (st = o->op->stack_top; st; st = st->prev)
110     {
111         cur_sz--;
112         o->op->tmp_names_buf[cur_sz] = st->name;
113     }
114     assert(cur_sz == 0);
115     return o->op->tmp_names_buf;
116 }
117
118 void odr_seterror(ODR o, int error, int id)
119 {
120     o->error = error;
121     o->op->error_id = id;
122     o->op->element[0] = '\0';
123 }
124
125 void odr_setelement(ODR o, const char *element)
126 {
127     if (element)
128     {
129         strncpy(o->op->element, element, sizeof(o->op->element)-1);
130         o->op->element[sizeof(o->op->element)-1] = '\0';
131     }
132 }
133
134 void odr_FILE_write(ODR o, void *handle, int type,
135                     const char *buf, int len)
136 {
137     int i;
138 #if 0
139     if (type  == ODR_OCTETSTRING)
140     {
141         const char **stack_names = odr_get_element_path(o);
142         for (i = 0; stack_names[i]; i++)
143             fprintf((FILE*) handle, "[%s]", stack_names[i]);
144         fputs("\n", (FILE*) handle);
145     }
146 #endif
147     for (i = 0; i < len; i++)
148     {
149         unsigned c = ((const unsigned char *) buf)[i];
150         if (i == 20000 && len > 31000)
151         {
152             fputs(" ..... ", (FILE*) handle);
153                 i = len - 1000;
154         }
155         if (strchr("\r\n\f\t", c) || (c >= ' ' && c <= 126))
156             putc(c, (FILE*) handle);
157         else
158         {
159             char x[5];
160             sprintf(x, "\\X%02X", c);
161             fputs(x, (FILE*) handle);
162         }
163     }
164 }
165
166 void odr_FILE_close(void *handle)
167 {
168     FILE *f = (FILE *) handle;
169     if (f && f != stderr && f != stdout)
170         fclose(f);
171 }
172
173 void odr_setprint(ODR o, FILE *file)
174 {
175     odr_set_stream(o, file, odr_FILE_write, odr_FILE_close);
176 }
177
178
179 void odr_set_stream(ODR o, void *handle,
180                     void (*stream_write)(ODR o,
181                                          void *handle, int type,
182                                          const char *buf, int len),
183                     void (*stream_close)(void *handle))
184 {
185     o->op->print = (FILE*) handle;
186     o->op->stream_write = stream_write;
187     o->op->stream_close = stream_close;
188 }
189
190 int odr_set_charset(ODR o, const char *to, const char *from)
191 {
192     if (o->op->iconv_handle)
193         yaz_iconv_close (o->op->iconv_handle);
194     o->op->iconv_handle = 0;
195     if (to && from)
196     {
197         o->op->iconv_handle = yaz_iconv_open(to, from);
198         if (o->op->iconv_handle == 0)
199             return -1;
200     }
201     return 0;
202 }
203
204
205 ODR odr_createmem(int direction)
206 {
207     ODR o;
208     if (!log_level_initialized)
209     {
210         log_level = yaz_log_module_level("odr");
211         log_level_initialized = 1;
212     }
213
214     if (!(o = (ODR) xmalloc(sizeof(*o))))
215         return 0;
216     o->op = (struct Odr_private *) xmalloc(sizeof(*o->op));
217     o->direction = direction;
218     o->buf = 0;
219     o->size = o->pos = o->top = 0;
220     o->op->can_grow = 1;
221     o->mem = nmem_create();
222     o->op->enable_bias = 1;
223     o->op->odr_ber_tag.lclass = -1;
224     o->op->iconv_handle = 0;
225     odr_setprint(o, stderr);
226     odr_reset(o);
227     yaz_log(log_level, "odr_createmem dir=%d o=%p", direction, o);
228     return o;
229 }
230
231 void odr_reset(ODR o)
232 {
233     if (!log_level_initialized)
234     {
235         log_level = yaz_log_module_level("odr");
236         log_level_initialized = 1;
237     }
238
239     odr_seterror(o, ONONE, 0);
240     o->bp = o->buf;
241     odr_seek(o, ODR_S_SET, 0);
242     o->top = 0;
243     o->op->t_class = -1;
244     o->op->t_tag = -1;
245     o->op->indent = 0;
246     o->op->stack_first = 0;
247     o->op->stack_top = 0;
248     o->op->tmp_names_sz = 0;
249     o->op->tmp_names_buf = 0;
250     nmem_reset(o->mem);
251     o->op->choice_bias = -1;
252     o->op->lenlen = 1;
253     if (o->op->iconv_handle != 0)
254         yaz_iconv(o->op->iconv_handle, 0, 0, 0, 0);
255     yaz_log(log_level, "odr_reset o=%p", o);
256 }
257
258 void odr_destroy(ODR o)
259 {
260     nmem_destroy(o->mem);
261     if (o->buf && o->op->can_grow)
262        xfree(o->buf);
263     if (o->op->stream_close)
264         o->op->stream_close(o->op->print);
265     if (o->op->iconv_handle != 0)
266         yaz_iconv_close(o->op->iconv_handle);
267     xfree(o->op);
268     xfree(o);
269     yaz_log(log_level, "odr_destroy o=%p", o);
270 }
271
272 void odr_setbuf(ODR o, char *buf, int len, int can_grow)
273 {
274     odr_seterror(o, ONONE, 0);
275     o->bp = (unsigned char *) buf;
276     o->buf = (unsigned char *) buf;
277     o->op->can_grow = can_grow;
278     o->top = o->pos = 0;
279     o->size = len;
280 }
281
282 char *odr_getbuf(ODR o, int *len, int *size)
283 {
284     *len = o->top;
285     if (size)
286         *size = o->size;
287     return (char*) o->buf;
288 }
289
290 void odr_printf(ODR o, const char *fmt, ...)
291 {
292     va_list ap;
293     char buf[4096];
294
295     va_start(ap, fmt);
296     yaz_vsnprintf(buf, sizeof(buf), fmt, ap);
297     o->op->stream_write(o, o->op->print, ODR_VISIBLESTRING, buf, strlen(buf));
298     va_end(ap);
299 }
300 /*
301  * Local variables:
302  * c-basic-offset: 4
303  * c-file-style: "Stroustrup"
304  * indent-tabs-mode: nil
305  * End:
306  * vim: shiftwidth=4 tabstop=8 expandtab
307  */
308