Version 5.0.18
[yaz-moved-to-github.git] / src / ber_tag.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file ber_tag.c
8  * \brief Implements BER tags encoding and decoding
9  *
10  * This source file implements BER encoding and decoding of
11  * the tags.
12  */
13 #if HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #include <stdio.h>
18 #include "odr-priv.h"
19
20 /**
21  * \brief Encode/decode BER tags
22  *
23  * On encoding:
24  * \verbatim
25  *      if  p: write tag. return 1 (success) or -1 (error).
26  *      if !p: return 0.
27  * \endverbatim
28  * On decoding:
29  * \verbatim
30  *      if tag && zclass match up, advance pointer and return 1. set cons.
31  *      else leave pointer unchanged. Return 0.
32  * \endverbatim
33  */
34 int ber_tag(ODR o, void *p, int zclass, int tag, int *constructed, int opt,
35             const char *name)
36 {
37     struct Odr_ber_tag *odr_ber_tag = &o->op->odr_ber_tag;
38     int rd;
39     char **pp = (char **)p;
40
41     if (o->direction == ODR_DECODE)
42         *pp = 0;
43     o->op->t_class = -1;
44     if (ODR_STACK_EMPTY(o))
45     {
46         odr_seek(o, ODR_S_SET, 0);
47         o->op->top = 0;
48         o->op->bp = o->op->buf;
49         odr_ber_tag->lclass = -1;
50     }
51     switch (o->direction)
52     {
53     case ODR_ENCODE:
54         if (!*pp)
55         {
56             if (!opt)
57             {
58                 odr_seterror(o, OREQUIRED, 24);
59                 odr_setelement (o, name);
60             }
61             return 0;
62         }
63         if ((rd = ber_enctag(o, zclass, tag, *constructed)) < 0)
64             return -1;
65         return 1;
66     case ODR_DECODE:
67         if (ODR_STACK_NOT_EMPTY(o) && !odr_constructed_more(o))
68         {
69             if (!opt)
70             {
71                 odr_seterror(o, OREQUIRED, 25);
72                 odr_setelement(o, name);
73             }
74             return 0;
75         }
76         if (odr_ber_tag->lclass < 0)
77         {
78             if ((odr_ber_tag->br =
79                  ber_dectag(o->op->bp, &odr_ber_tag->lclass,
80                             &odr_ber_tag->ltag, &odr_ber_tag->lcons,
81                             odr_max(o))) <= 0)
82             {
83                 odr_seterror(o, OPROTO, 26);
84                 odr_setelement(o, name);
85                 return 0;
86             }
87         }
88         if (zclass == odr_ber_tag->lclass && tag == odr_ber_tag->ltag)
89         {
90             o->op->bp += odr_ber_tag->br;
91             *constructed = odr_ber_tag->lcons;
92             odr_ber_tag->lclass = -1;
93             return 1;
94         }
95         else
96         {
97             if (!opt)
98             {
99                 odr_seterror(o, OREQUIRED, 27);
100                 odr_setelement(o, name);
101             }
102             return 0;
103         }
104     case ODR_PRINT:
105         if (!*pp && !opt)
106         {
107             odr_seterror(o,OREQUIRED, 28);
108             odr_setelement(o, name);
109         }
110         return *pp != 0;
111     default:
112         odr_seterror(o, OOTHER, 29);
113         odr_setelement(o, name);
114         return 0;
115     }
116 }
117
118 /**
119  * \brief BER-encode a zclass/tag/constructed package (identifier octets).
120  *
121  * Return number of bytes encoded, or -1 if out of bounds.
122  */
123 int ber_enctag(ODR o, int zclass, int tag, int constructed)
124 {
125     int cons = (constructed ? 1 : 0), n = 0;
126     unsigned char octs[sizeof(int)], b;
127
128     b = (zclass << 6) & 0XC0;
129     b |= (cons << 5) & 0X20;
130     if (tag <= 30)
131     {
132         b |= tag & 0X1F;
133         if (odr_putc(o, b) < 0)
134             return -1;
135         return 1;
136     }
137     else
138     {
139         b |= 0X1F;
140         if (odr_putc(o, b) < 0)
141             return -1;
142         do
143         {
144             octs[n++] = tag & 0X7F;
145             tag >>= 7;
146         }
147         while (tag);
148         while (n--)
149         {
150             unsigned char oo;
151
152             oo = octs[n] | ((n > 0) << 7);
153             if (odr_putc(o, oo) < 0)
154                 return -1;
155         }
156         return 0;
157     }
158 }
159
160 /**
161  * \brief Decodes BER identifier octets.
162  *
163  * Returns number of bytes read or -1 for error.
164  */
165 int ber_dectag(const char *cp, int *zclass, int *tag,
166                int *constructed, int max)
167 {
168     const unsigned char *b = (const unsigned char *) cp;
169     int l = 1;
170
171     if (l > max)
172         return -1;
173
174     *zclass = *b >> 6;
175     *constructed = (*b >> 5) & 0X01;
176     if ((*tag = *b & 0x1F) <= 30)
177         return 1;
178     *tag = 0;
179     do
180     {
181         if (l >= max)
182             return -1;
183         *tag <<= 7;
184         *tag |= b[l] & 0X7F;
185     }
186     while (b[l++] & 0X80);
187     return l;
188 }
189 /*
190  * Local variables:
191  * c-basic-offset: 4
192  * c-file-style: "Stroustrup"
193  * indent-tabs-mode: nil
194  * End:
195  * vim: shiftwidth=4 tabstop=8 expandtab
196  */
197