Rewrite from scratch
Showing
2 changed files
with
799 additions
and
776 deletions
... | @@ -20,7 +20,7 @@ | ... | @@ -20,7 +20,7 @@ |
20 | #define _HEADER0_H | 20 | #define _HEADER0_H |
21 | 21 | ||
22 | #ifdef DMALLOC | 22 | #ifdef DMALLOC |
23 | # include <dmalloc.h> | 23 | # include <dmalloc.h> |
24 | #endif | 24 | #endif |
25 | 25 | ||
26 | #include <mailutils/header.h> | 26 | #include <mailutils/header.h> |
... | @@ -33,38 +33,42 @@ extern "C" { | ... | @@ -33,38 +33,42 @@ extern "C" { |
33 | 33 | ||
34 | /* The structure members are offset that point to the begin/end of header | 34 | /* The structure members are offset that point to the begin/end of header |
35 | fields. */ | 35 | fields. */ |
36 | struct _hdr | 36 | struct mu_hdrent |
37 | { | 37 | { |
38 | char *fn; | 38 | struct mu_hdrent *prev; |
39 | char *fn_end; | 39 | struct mu_hdrent *next; |
40 | char *fv; | 40 | size_t fn; |
41 | char *fv_end; | 41 | size_t nlen; |
42 | size_t fv; | ||
43 | size_t vlen; | ||
44 | size_t nlines; | ||
42 | }; | 45 | }; |
43 | 46 | ||
44 | /* The blurb member represents the headers, hdr_count the number of distinct | ||
45 | header field and the layout is done by struct_hdr *hdr. */ | ||
46 | struct _mu_header | 47 | struct _mu_header |
47 | { | 48 | { |
48 | /* Owner. */ | 49 | /* Owner. */ |
49 | void *owner; | 50 | void *owner; |
50 | 51 | ||
51 | /* Data. */ | 52 | /* Data. */ |
52 | mu_stream_t mstream; | 53 | char *spool; |
53 | size_t stream_len; | 54 | size_t spool_size; |
54 | char *blurb; | 55 | size_t spool_used; |
55 | size_t blurb_len; | 56 | struct mu_hdrent *head, *tail; |
56 | size_t hdr_count; | ||
57 | struct _hdr *hdr; | ||
58 | int flags; | 57 | int flags; |
59 | 58 | ||
60 | mu_assoc_t cache; | 59 | size_t numhdr; |
60 | size_t numlines; | ||
61 | size_t size; | ||
62 | |||
63 | /* Temporary storage */ | ||
64 | mu_stream_t mstream; | ||
65 | size_t mstream_size; | ||
61 | 66 | ||
62 | /* Stream. */ | 67 | /* Stream. */ |
63 | mu_stream_t stream; | 68 | mu_stream_t stream; |
64 | int (*_get_value) (mu_header_t, const char *, char *, size_t , size_t *); | 69 | size_t strpos; |
65 | int (*_set_value) (mu_header_t, const char *, const char *, int); | 70 | |
66 | int (*_lines) (mu_header_t, size_t *); | 71 | /* Methods */ |
67 | int (*_size) (mu_header_t, size_t *); | ||
68 | int (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *); | 72 | int (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *); |
69 | }; | 73 | }; |
70 | 74 | ... | ... |
... | @@ -17,6 +17,17 @@ | ... | @@ -17,6 +17,17 @@ |
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | Boston, MA 02110-1301 USA */ | 18 | Boston, MA 02110-1301 USA */ |
19 | 19 | ||
20 | /* This all header business needs a good rewrite. | ||
21 | * -- Alain Magloire, 2000-07-03 (rev. 1.21) | ||
22 | * | ||
23 | * It's the job that's never started as takes longest to finish. | ||
24 | * -- Hamfast Gamgee, some time in the Third Age | ||
25 | * | ||
26 | * It took almost 7 years to gather the courage to start the job, | ||
27 | * and only one day to finish it. | ||
28 | * -- Sergey Poznyakoff, 2007-06-24 | ||
29 | */ | ||
30 | |||
20 | #ifdef HAVE_CONFIG_H | 31 | #ifdef HAVE_CONFIG_H |
21 | # include <config.h> | 32 | # include <config.h> |
22 | #endif | 33 | #endif |
... | @@ -37,120 +48,267 @@ | ... | @@ -37,120 +48,267 @@ |
37 | 48 | ||
38 | #include <header0.h> | 49 | #include <header0.h> |
39 | 50 | ||
40 | #define HEADER_MODIFIED 1 | 51 | #define HEADER_MODIFIED 0x01 |
52 | #define HEADER_INVALIDATE 0x02 | ||
41 | 53 | ||
42 | static int header_parse (mu_header_t, const char *, int); | 54 | #define HEADER_SET_MODIFIED(h) \ |
43 | static int header_read (mu_stream_t, char *, size_t, mu_off_t, size_t *); | 55 | ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE)) |
44 | static int header_readline (mu_stream_t, char *, size_t, mu_off_t, size_t *); | ||
45 | static int header_write (mu_stream_t, const char *, size_t, mu_off_t, size_t *); | ||
46 | static int fill_blurb (mu_header_t); | ||
47 | static void header_free_cache (mu_header_t); | ||
48 | 56 | ||
49 | int | 57 | |
50 | mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner) | 58 | /* mu_hdrent manipulation */ |
59 | |||
60 | #define MU_HDRENT_NAME(hp,ep) ((hp)->spool + (ep)->fn) | ||
61 | #define MU_HDRENT_VALUE(hp,ep) ((hp)->spool + (ep)->fv) | ||
62 | #define MU_STR_SIZE(nlen,vlen) ((nlen) + 2 + (vlen) + 1) | ||
63 | |||
64 | static struct mu_hdrent * | ||
65 | mu_hdrent_nth (struct _mu_header *hdr, int n) | ||
51 | { | 66 | { |
52 | mu_header_t header; | 67 | struct mu_hdrent *p; |
53 | int status = 0; | 68 | for (p = hdr->head; p; p = p->next) |
69 | if (n-- == 1) | ||
70 | break; | ||
71 | return p; | ||
72 | } | ||
54 | 73 | ||
55 | header = calloc (1, sizeof (*header)); | 74 | static struct mu_hdrent * |
56 | if (header == NULL) | 75 | mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos) |
57 | return ENOMEM; | 76 | { |
77 | struct mu_hdrent *p; | ||
78 | for (p = hdr->head; p; p = p->next) | ||
79 | if (strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && pos-- == 1) | ||
80 | break; | ||
81 | return p; | ||
82 | } | ||
58 | 83 | ||
59 | header->owner = owner; | 84 | static int |
85 | mu_hdrent_find_stream_pos (struct _mu_header *hdr, mu_off_t pos, | ||
86 | struct mu_hdrent **pent, size_t *poff) | ||
87 | { | ||
88 | mu_off_t x; | ||
89 | struct mu_hdrent *p; | ||
60 | 90 | ||
61 | status = header_parse (header, blurb, len); | 91 | for (p = hdr->head, x = 0; p; p = p->next) |
92 | { | ||
93 | size_t strsize = MU_STR_SIZE (p->nlen, p->vlen); | ||
94 | if (x <= pos && pos < x + strsize) | ||
95 | { | ||
96 | *poff = pos - x; | ||
97 | *pent = p; | ||
98 | return 0; | ||
99 | } | ||
100 | x += strsize; | ||
101 | } | ||
102 | if (x == pos) | ||
103 | { | ||
104 | /* To supply the trailing '\n' */ | ||
105 | p = hdr->tail; | ||
106 | *pent = p; | ||
107 | *poff = MU_STR_SIZE (p->nlen, p->vlen) - 1; | ||
108 | return 0; | ||
109 | } | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | static void | ||
114 | mu_hdrent_count (struct _mu_header *hdr, size_t *pcount, size_t *psize, | ||
115 | size_t *plines) | ||
116 | { | ||
117 | if (hdr->flags & HEADER_INVALIDATE) | ||
118 | { | ||
119 | size_t size = 0; | ||
120 | size_t count = 0; | ||
121 | size_t lines = 0; | ||
122 | struct mu_hdrent *p; | ||
123 | for (p = hdr->head; p; p = p->next) | ||
124 | { | ||
125 | count++; | ||
126 | size += MU_STR_SIZE (p->nlen, p->vlen); | ||
127 | lines += p->nlines; | ||
128 | } | ||
62 | 129 | ||
63 | *ph = header; | 130 | hdr->numhdr = count; |
64 | return status; | 131 | hdr->numlines = lines; |
132 | hdr->size = size; | ||
133 | hdr->flags &= ~HEADER_INVALIDATE; | ||
134 | } | ||
135 | |||
136 | *pcount = hdr->numhdr; | ||
137 | *psize = hdr->size; | ||
138 | *plines = hdr->numlines; | ||
65 | } | 139 | } |
66 | 140 | ||
67 | static void | 141 | static void |
68 | header_free_cache (mu_header_t header) | 142 | mu_hdrent_remove (struct _mu_header *hdr, struct mu_hdrent *ent) |
69 | { | 143 | { |
70 | mu_assoc_clear (header->cache); | 144 | struct mu_hdrent *p = ent->prev; |
145 | if (p) | ||
146 | p->next = ent->next; | ||
147 | else | ||
148 | hdr->head = ent->next; | ||
149 | |||
150 | p = ent->next; | ||
151 | if (p) | ||
152 | p->prev = ent->prev; | ||
153 | else | ||
154 | hdr->tail = ent->prev; | ||
71 | } | 155 | } |
72 | 156 | ||
73 | void | 157 | static void |
74 | mu_header_destroy (mu_header_t *ph, void *owner) | 158 | mu_hdrent_prepend (struct _mu_header *hdr, struct mu_hdrent *ent) |
75 | { | 159 | { |
76 | if (ph && *ph) | 160 | struct mu_hdrent *p = hdr->head; |
77 | { | 161 | ent->prev = NULL; |
78 | mu_header_t header = *ph; | 162 | ent->next = p; |
163 | if (p) | ||
164 | p->prev = ent; | ||
165 | else | ||
166 | hdr->tail = ent; | ||
167 | hdr->head = ent; | ||
168 | } | ||
79 | 169 | ||
80 | /* Can we destroy ?. */ | 170 | static void |
81 | if (header->owner == owner) | 171 | mu_hdrent_append (struct _mu_header *hdr, struct mu_hdrent *ent) |
172 | { | ||
173 | struct mu_hdrent *p = hdr->tail; | ||
174 | ent->next = NULL; | ||
175 | ent->prev = p; | ||
176 | if (p) | ||
177 | p->next = ent; | ||
178 | else | ||
179 | hdr->head = ent; | ||
180 | hdr->tail = ent; | ||
181 | } | ||
182 | |||
183 | static int | ||
184 | mu_hdrent_insert (struct _mu_header *hdr, struct mu_hdrent *ent, | ||
185 | const char *name, int pos, int before) | ||
186 | { | ||
187 | struct mu_hdrent *p; | ||
188 | struct mu_hdrent *ref = mu_hdrent_find (hdr, name, pos); | ||
189 | if (!ref) | ||
190 | return MU_ERR_NOENT; | ||
191 | |||
192 | if (before) | ||
193 | { | ||
194 | ref = ref->prev; | ||
195 | if (!ref) | ||
82 | { | 196 | { |
83 | mu_stream_destroy (&(header->stream), header); | 197 | mu_hdrent_prepend (hdr, ent); |
198 | return 0; | ||
199 | } | ||
200 | } | ||
84 | 201 | ||
85 | if (header->hdr) | 202 | p = ref->next; |
86 | free (header->hdr); | 203 | if (!p) |
204 | { | ||
205 | mu_hdrent_append (hdr, ent); | ||
206 | return 0; | ||
207 | } | ||
87 | 208 | ||
88 | if (header->blurb) | 209 | ent->next = p; |
89 | free (header->blurb); | 210 | p->prev = ent; |
211 | ent->prev = ref; | ||
212 | ref->next = ent; | ||
213 | |||
214 | return 0; | ||
215 | } | ||
90 | 216 | ||
91 | header_free_cache (header); | 217 | #define SPOOLBLKSIZ 1024 |
92 | 218 | ||
93 | mu_assoc_destroy (&header->cache); | 219 | static struct mu_hdrent * |
94 | 220 | mu_hdrent_create (struct _mu_header *ph, | |
95 | if (header->mstream) | 221 | struct mu_hdrent *ent, |
96 | mu_stream_destroy (&(header->mstream), NULL); | 222 | const char *name, size_t nsize, |
223 | const char *value, size_t vsize) | ||
224 | { | ||
225 | size_t strsize; | ||
226 | size_t sizeleft; | ||
227 | char *p; | ||
228 | |||
229 | if (!ent) | ||
230 | { | ||
231 | ent = calloc (1, sizeof (*ent)); | ||
232 | if (!ent) | ||
233 | return NULL; | ||
234 | } | ||
235 | |||
236 | strsize = MU_STR_SIZE (nsize, vsize); | ||
237 | sizeleft = ph->spool_size - ph->spool_used; | ||
97 | 238 | ||
98 | free (header); | 239 | /* Ensure there is enough space in spool */ |
99 | } | 240 | if (sizeleft < strsize) |
100 | *ph = NULL; | 241 | { |
242 | char *newp; | ||
243 | size_t delta = (strsize - sizeleft + SPOOLBLKSIZ - 1) / SPOOLBLKSIZ; | ||
244 | delta *= SPOOLBLKSIZ; | ||
245 | newp = realloc (ph->spool, ph->spool_size + delta); | ||
246 | if (!newp) | ||
247 | return 0; | ||
248 | ph->spool = newp; | ||
249 | ph->spool_size += delta; | ||
101 | } | 250 | } |
102 | } | ||
103 | 251 | ||
104 | void * | 252 | /* Copy header name */ |
105 | mu_header_get_owner (mu_header_t header) | 253 | ent->fn = ph->spool_used; |
106 | { | 254 | ent->nlen = nsize; |
107 | return (header) ? header->owner : NULL; | 255 | memcpy (ph->spool + ph->spool_used, name, nsize); |
256 | ph->spool_used += nsize; | ||
257 | ph->spool[ph->spool_used++] = 0; | ||
258 | ph->spool[ph->spool_used++] = ' '; | ||
259 | |||
260 | /* Copy header value */ | ||
261 | ent->fv = ph->spool_used; | ||
262 | ent->vlen = vsize; | ||
263 | memcpy (ph->spool + ph->spool_used, value, vsize); | ||
264 | ph->spool_used += vsize; | ||
265 | ph->spool[ph->spool_used++] = 0; | ||
266 | |||
267 | ent->nlines = 1; | ||
268 | for (p = value; p < value + vsize; p++) | ||
269 | if (*p == '\n') | ||
270 | ent->nlines++; | ||
271 | |||
272 | return ent; | ||
108 | } | 273 | } |
109 | 274 | ||
110 | int | 275 | static void |
111 | mu_header_is_modified (mu_header_t header) | 276 | mu_hdrent_free_list (struct _mu_header *hdr) |
112 | { | 277 | { |
113 | return (header) ? (header->flags & HEADER_MODIFIED) : 0; | 278 | struct mu_hdrent *p; |
279 | for (p = hdr->head; p; ) | ||
280 | { | ||
281 | struct mu_hdrent *next = p->next; | ||
282 | free (p); | ||
283 | p = next; | ||
284 | } | ||
285 | hdr->head = hdr->tail = NULL; | ||
286 | hdr->spool_used = 0; | ||
114 | } | 287 | } |
288 | |||
115 | 289 | ||
116 | int | 290 | |
117 | mu_header_clear_modified (mu_header_t header) | 291 | #define ISLWSP(c) (((c) == ' ' || (c) == '\t')) |
118 | { | ||
119 | if (header) | ||
120 | header->flags &= ~HEADER_MODIFIED; | ||
121 | return 0; | ||
122 | } | ||
123 | 292 | ||
124 | /* Parsing is done in a rather simple fashion, meaning we just consider an | 293 | /* Parsing is done in a rather simple fashion, meaning we just consider an |
125 | entry to be a field-name an a field-value. So they maybe duplicate of | 294 | entry to be a field-name an a field-value. So they maybe duplicate of |
126 | field-name like "Received" they are just put in the array, see _get_value() | 295 | field-name like "Received" they are just put in the array, see _get_value() |
127 | on how to handle the case. in the case of error .i.e a bad header construct | 296 | on how to handle the case. in the case of error .i.e a bad header construct |
128 | we do a full stop and return what we have so far. */ | 297 | we do a full stop and return what we have so far. */ |
298 | |||
129 | static int | 299 | static int |
130 | header_parse (mu_header_t header, const char *blurb, int len) | 300 | header_parse (mu_header_t header, const char *blurb, int len) |
131 | { | 301 | { |
132 | char *header_end; | 302 | const char *header_end; |
133 | char *header_start; | 303 | const char *header_start; |
134 | char *header_start2; | 304 | const char *header_start2; |
135 | struct _hdr *hdr; | 305 | |
136 | |||
137 | header_free_cache (header); | ||
138 | |||
139 | /* Nothing to parse. */ | 306 | /* Nothing to parse. */ |
140 | if (blurb == NULL) | 307 | if (blurb == NULL) |
141 | return 0; | 308 | return 0; |
142 | |||
143 | header->blurb_len = len; | ||
144 | /* Why "+ 1", if for a terminating NULL, where is written? */ | ||
145 | header->blurb = calloc (1, header->blurb_len + 1); | ||
146 | if (header->blurb == NULL) | ||
147 | return ENOMEM; | ||
148 | memcpy (header->blurb, blurb, header->blurb_len); | ||
149 | 309 | ||
150 | if (header->hdr) | 310 | header->flags |= HEADER_INVALIDATE; |
151 | free (header->hdr); | 311 | mu_hdrent_free_list (header); |
152 | header->hdr = NULL; | ||
153 | header->hdr_count = 0; | ||
154 | 312 | ||
155 | /* Get a header, a header is: | 313 | /* Get a header, a header is: |
156 | field-name LWSP ':' | 314 | field-name LWSP ':' |
... | @@ -158,9 +316,10 @@ header_parse (mu_header_t header, const char *blurb, int len) | ... | @@ -158,9 +316,10 @@ header_parse (mu_header_t header, const char *blurb, int len) |
158 | *[ (' ' | '\t') field-value '\r' '\n' ] | 316 | *[ (' ' | '\t') field-value '\r' '\n' ] |
159 | */ | 317 | */ |
160 | /* First loop goes through the blurb */ | 318 | /* First loop goes through the blurb */ |
161 | for (header_start = header->blurb; ; header_start = ++header_end) | 319 | for (header_start = blurb; ; header_start = ++header_end) |
162 | { | 320 | { |
163 | char *fn, *fn_end, *fv, *fv_end; | 321 | const char *fn, *fn_end, *fv, *fv_end; |
322 | struct mu_hdrent *ent; | ||
164 | 323 | ||
165 | if (header_start[0] == ' ' | 324 | if (header_start[0] == ' ' |
166 | || header_start[0] == '\t' | 325 | || header_start[0] == '\t' |
... | @@ -189,11 +348,11 @@ header_parse (mu_header_t header, const char *blurb, int len) | ... | @@ -189,11 +348,11 @@ header_parse (mu_header_t header, const char *blurb, int len) |
189 | } | 348 | } |
190 | 349 | ||
191 | if (header_end == NULL) | 350 | if (header_end == NULL) |
192 | break; /* Bail out. */ | 351 | break; /* FIXME: Bail out. */ |
193 | 352 | ||
194 | /* Now save the header in the data structure. */ | 353 | /* Now save the header in the data structure. */ |
195 | 354 | ||
196 | /* Treats unix "From " specially. */ | 355 | /* Treats unix "From " specially. FIXME: Should we? */ |
197 | if ((header_end - header_start >= 5) | 356 | if ((header_end - header_start >= 5) |
198 | && strncmp (header_start, "From ", 5) == 0) | 357 | && strncmp (header_start, "From ", 5) == 0) |
199 | { | 358 | { |
... | @@ -206,366 +365,320 @@ header_parse (mu_header_t header, const char *blurb, int len) | ... | @@ -206,366 +365,320 @@ header_parse (mu_header_t header, const char *blurb, int len) |
206 | { | 365 | { |
207 | char *colon = memchr (header_start, ':', header_end - header_start); | 366 | char *colon = memchr (header_start, ':', header_end - header_start); |
208 | 367 | ||
209 | #define ISLWSP(c) (((c) == ' ' || (c) == '\t')) | ||
210 | /* Houston we have a problem. */ | 368 | /* Houston we have a problem. */ |
211 | if (colon == NULL) | 369 | if (colon == NULL) |
212 | break; /* Disregard the rest and bailout. */ | 370 | break; /* FIXME: Disregard the rest and bailout. */ |
213 | 371 | ||
214 | fn = header_start; | 372 | fn = header_start; |
215 | fn_end = colon; | 373 | fn_end = colon; |
216 | /* Shrink any LWSP after the field name -- CRITICAL for | 374 | /* Shrink any LWSP after the field name -- CRITICAL for |
217 | later name comparisons to work correctly! */ | 375 | later name comparisons to work correctly! */ |
218 | while(ISLWSP(fn_end[-1])) | 376 | while (ISLWSP (fn_end[-1])) |
219 | fn_end--; | 377 | fn_end--; |
220 | 378 | ||
221 | fv = colon + 1; | 379 | fv = colon + 1; |
222 | fv_end = header_end; | 380 | fv_end = header_end; |
223 | 381 | ||
224 | /* Skip any LWSP before the field value -- unnecessary, but | 382 | /* Skip any LWSP before the field value -- unnecessary, but |
225 | might make some field values look a little tidier. */ | 383 | might make some field values look a little tidier. */ |
226 | while(ISLWSP(fv[0])) | 384 | while (ISLWSP (fv[0])) |
227 | fv++; | 385 | fv++; |
228 | } | 386 | } |
229 | #undef ISLWSP | 387 | |
230 | /* Allocate a new slot for the field:value. */ | 388 | /* Register this header */ |
231 | hdr = realloc (header->hdr, (header->hdr_count + 1) * sizeof (*hdr)); | 389 | ent = mu_hdrent_create (header, NULL, fn, fn_end - fn, fv, fv_end - fv); |
232 | if (hdr == NULL) | 390 | if (!ent) |
233 | { | 391 | return ENOMEM; |
234 | free (header->blurb); | 392 | mu_hdrent_append (header, ent); |
235 | free (header->hdr); | ||
236 | header->blurb = NULL; | ||
237 | header->hdr = NULL; | ||
238 | return ENOMEM; | ||
239 | } | ||
240 | hdr[header->hdr_count].fn = fn; | ||
241 | hdr[header->hdr_count].fn_end = fn_end; | ||
242 | hdr[header->hdr_count].fv = fv; | ||
243 | hdr[header->hdr_count].fv_end = fv_end; | ||
244 | header->hdr = hdr; | ||
245 | header->hdr_count++; | ||
246 | } /* for (header_start ...) */ | 393 | } /* for (header_start ...) */ |
247 | 394 | ||
248 | return 0; | 395 | return 0; |
249 | } | 396 | } |
250 | 397 | ||
251 | /* FIXME: grossly inneficient, too many copies and reallocating. | 398 | |
252 | This all header business needs a good rewrite. */ | 399 | static int |
253 | int | 400 | mu_header_fill (mu_header_t header) |
254 | mu_header_set_value (mu_header_t header, const char *fn, const char *fv, | ||
255 | int replace) | ||
256 | { | 401 | { |
402 | int status; | ||
403 | char buf[1024]; | ||
404 | size_t nread; | ||
405 | mu_stream_t mstream; | ||
406 | size_t stream_len; | ||
257 | char *blurb; | 407 | char *blurb; |
258 | size_t len; | 408 | |
259 | 409 | if (header->spool_used) | |
260 | if (header == NULL || fn == NULL) | 410 | return 0; |
261 | return EINVAL; | 411 | |
262 | 412 | if (header->_fill == NULL) | |
263 | /* An fv of NULL means delete the field, but only do it if replace | 413 | return 0; /* FIXME: Really? */ |
264 | was also set to true! */ | ||
265 | if (fv == NULL && !replace) | ||
266 | return EINVAL; | ||
267 | |||
268 | /* Overload. */ | ||
269 | if (header->_set_value) | ||
270 | return header->_set_value (header, fn, fv, replace); | ||
271 | |||
272 | /* Try to fill out the buffer, if we know how. */ | ||
273 | if (header->blurb == NULL) | ||
274 | { | ||
275 | int err = fill_blurb (header); | ||
276 | if (err != 0) | ||
277 | return err; | ||
278 | } | ||
279 | |||
280 | /* Easy approach: if replace, overwrite the field-{name,value} and readjust | ||
281 | the pointers by calling header_parse () this is wastefull, we're just | ||
282 | fragmenting the memory it can be done better. But that may imply a | ||
283 | rewite of the headers ... for another day. */ | ||
284 | |||
285 | /* If replace, remove all fields in the header blurb that have the | ||
286 | same name as the field we are writing. | ||
287 | 414 | ||
288 | Algorithm: | 415 | status = mu_memory_stream_create (&mstream, NULL, MU_STREAM_RDWR); |
416 | if (status != 0) | ||
417 | return status; | ||
418 | mu_stream_open (mstream); | ||
419 | stream_len = 0; | ||
289 | 420 | ||
290 | for i = 0, ... i < max_hdrs | 421 | /* Bring in the entire header. */ |
291 | - if ith field has name 'fn' memmove() all following fields up over | 422 | do |
292 | this field | ||
293 | - reparse the headers | ||
294 | - restart the for loop at the ith field | ||
295 | */ | ||
296 | if (replace) | ||
297 | { | 423 | { |
298 | size_t name_len; | 424 | nread = 0; |
299 | size_t i; | 425 | status = header->_fill (header, buf, sizeof buf, |
300 | size_t fn_len; /* Field Name len. */ | 426 | stream_len, &nread); |
301 | size_t fv_len; /* Field Value len. */ | 427 | if (status) |
302 | len = header->blurb_len; | 428 | { |
303 | /* Find FN in the header fields... */ | 429 | if (status != EAGAIN && status != EINTR) |
304 | for (name_len = strlen (fn), i = 0; i < header->hdr_count; i++) | 430 | mu_stream_destroy (&mstream, NULL); |
431 | return status; | ||
432 | } | ||
433 | if (nread > 0) | ||
305 | { | 434 | { |
306 | fn_len = header->hdr[i].fn_end - header->hdr[i].fn; | 435 | status = mu_stream_write (mstream, buf, nread, stream_len, NULL); |
307 | fv_len = header->hdr[i].fv_end - header->hdr[i].fv; | 436 | if (status) |
308 | if (fn_len == name_len && | ||
309 | strncasecmp (header->hdr[i].fn, fn, fn_len) == 0) | ||
310 | { | 437 | { |
311 | blurb = header->blurb; | 438 | mu_stream_destroy (&mstream, NULL); |
312 | /* ... and if its NOT the last field, move the next field | 439 | return status; |
313 | through to last field into its place, */ | ||
314 | if ((i + 1) < header->hdr_count) | ||
315 | { | ||
316 | memmove (header->hdr[i].fn, header->hdr[i + 1].fn, | ||
317 | header->hdr[header->hdr_count - 1].fv_end | ||
318 | - header->hdr[i + 1].fn + 3); | ||
319 | } | ||
320 | /* or if it is the last, just truncate the fields. */ | ||
321 | else | ||
322 | { | ||
323 | header->hdr[i].fn[0] = '\n'; | ||
324 | header->hdr[i].fn[1] = '\0'; | ||
325 | } | ||
326 | /* Readjust the pointers. */ | ||
327 | len -= header->hdr[i].fv_end - header->hdr[i].fn + 1; | ||
328 | i--; | ||
329 | blurb = header->blurb; | ||
330 | header_parse (header, blurb, len); | ||
331 | free (blurb); | ||
332 | header->flags |= HEADER_MODIFIED; | ||
333 | } | 440 | } |
441 | stream_len += nread; | ||
334 | } | 442 | } |
335 | } | 443 | } |
444 | while (nread > 0); | ||
336 | 445 | ||
337 | /* If FV is NULL, then we are done. */ | 446 | /* parse it. */ |
338 | if (!fv) | 447 | blurb = calloc (1, stream_len + 1); |
339 | return 0; | 448 | if (blurb) |
449 | { | ||
450 | size_t len; | ||
451 | |||
452 | status = mu_stream_read (mstream, blurb, stream_len, 0, &len); | ||
453 | if (status == 0) | ||
454 | status = header_parse (header, blurb, len); | ||
455 | free (blurb); | ||
456 | } | ||
457 | else | ||
458 | status = ENOMEM; | ||
459 | |||
460 | mu_stream_destroy (&mstream, NULL); | ||
461 | return status; | ||
462 | } | ||
340 | 463 | ||
341 | /* Replacing was taken care of above, now write the new header. | 464 | |
342 | header. Really not cute. | 465 | |
343 | COLON SPACE NL = 3 ; */ | 466 | int |
344 | len = strlen (fn) + strlen (fv) + 3; | 467 | mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner) |
345 | /* Add one for the NULL and leak a bit by adding one more | 468 | { |
346 | it will be the separtor \n from the body if the first | 469 | mu_header_t header; |
347 | blurb did not have it. */ | 470 | int status = 0; |
348 | blurb = calloc (header->blurb_len + len + 2, 1); | 471 | |
349 | if (blurb == NULL) | 472 | header = calloc (1, sizeof (*header)); |
473 | if (header == NULL) | ||
350 | return ENOMEM; | 474 | return ENOMEM; |
475 | |||
476 | header->owner = owner; | ||
351 | 477 | ||
352 | sprintf (blurb, "%s: %s", fn, fv); | 478 | status = header_parse (header, blurb, len); |
353 | 479 | ||
354 | /* Strip off trailing newlines and LWSP. */ | 480 | *ph = header; |
355 | while (blurb[strlen (blurb) - 1] == '\n' || | 481 | return status; |
356 | blurb[strlen (blurb) - 1] == ' ' || | 482 | } |
357 | blurb[strlen (blurb) - 1] == '\t') | ||
358 | { | ||
359 | blurb[strlen (blurb) - 1] = '\0'; | ||
360 | } | ||
361 | len = strlen (blurb); | ||
362 | blurb[len] = '\n'; | ||
363 | len++; | ||
364 | 483 | ||
365 | /* Prepend the rest of the headers. */ | 484 | void |
366 | if (header->blurb) | 485 | mu_header_destroy (mu_header_t *ph, void *owner) |
367 | { | 486 | { |
368 | memcpy (blurb + len, header->blurb, header->blurb_len); | 487 | if (ph && *ph) |
369 | free (header->blurb); | 488 | { |
370 | header->blurb = NULL; | 489 | mu_header_t header = *ph; |
371 | } | ||
372 | else | ||
373 | blurb[len] = '\n'; | ||
374 | 490 | ||
375 | /* before parsing the new blurb make sure it is properly terminated | 491 | if (header->owner == owner) |
376 | by \n\n. The trailing NL separator. */ | 492 | { |
377 | if (blurb[header->blurb_len + len - 1] != '\n' | 493 | mu_stream_destroy (&header->stream, header); |
378 | || blurb[header->blurb_len + len - 2] != '\n') | 494 | mu_hdrent_free_list (header); |
379 | { | 495 | free (header->spool); |
380 | blurb[header->blurb_len + len] = '\n'; | 496 | |
381 | len++; | 497 | free (header); |
498 | *ph = NULL; | ||
499 | } | ||
382 | } | 500 | } |
383 | header_parse (header, blurb, len + header->blurb_len); | ||
384 | free (blurb); | ||
385 | header->flags |= HEADER_MODIFIED; | ||
386 | return 0; | ||
387 | } | 501 | } |
388 | 502 | ||
389 | struct _hdr_cache { | 503 | |
390 | size_t len; | 504 | int |
391 | char *ptr; | 505 | mu_header_set_value (mu_header_t header, const char *fn, const char *fv, |
392 | }; | 506 | int replace) |
393 | |||
394 | static void | ||
395 | _hdr_cache_destroy (void *data) | ||
396 | { | 507 | { |
397 | struct _hdr_cache *hc = data; | 508 | int status; |
398 | free (hc->ptr); | 509 | struct mu_hdrent *ent = NULL; |
399 | } | 510 | |
511 | if (header == NULL || fn == NULL) | ||
512 | return EINVAL; | ||
400 | 513 | ||
401 | static int | 514 | status = mu_header_fill (header); |
402 | header_get_fvalue (mu_header_t header, const char *name, char *buffer, | 515 | if (status) |
403 | size_t buflen, size_t *pn) | 516 | return status; |
404 | { | 517 | |
405 | struct _hdr_cache *hdr = mu_assoc_ref (header->cache, name); | 518 | /* An fv of NULL means delete the field, but only do it if replace |
406 | if (hdr) | 519 | was also set to true! */ |
520 | if (fv == NULL && !replace) | ||
521 | return EINVAL; | ||
522 | |||
523 | if (replace) | ||
407 | { | 524 | { |
408 | if (hdr->len == 0) | 525 | ent = mu_hdrent_find (header, fn, 1); |
409 | return MU_ERR_NOENT; | 526 | if (ent) |
410 | else | ||
411 | { | 527 | { |
412 | size_t fv_len = hdr->len; | 528 | if (fv == NULL) |
413 | |||
414 | if (buffer && buflen > 0) | ||
415 | { | 529 | { |
416 | buflen--; | 530 | /* Delete the header */ |
417 | fv_len = (fv_len < buflen) ? fv_len : buflen; | 531 | mu_hdrent_remove (header, ent); |
418 | memcpy (buffer, hdr->ptr, fv_len); | 532 | free (ent); |
419 | buffer[fv_len] = '\0'; | 533 | return 0; |
420 | } | 534 | } |
421 | if (pn) | 535 | mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv)); |
422 | *pn = fv_len; | 536 | HEADER_SET_MODIFIED (header); |
423 | return 0; | 537 | return 0; |
424 | } | 538 | } |
539 | else if (fv == NULL) | ||
540 | return 0; | ||
425 | } | 541 | } |
426 | return MU_ERR_NOENT; | 542 | |
543 | ent = mu_hdrent_create (header, NULL, | ||
544 | fn, strlen (fn), fv, strlen (fv)); | ||
545 | if (!ent) | ||
546 | return ENOMEM; | ||
547 | mu_hdrent_prepend (header, ent); | ||
548 | HEADER_SET_MODIFIED (header); | ||
549 | return 0; | ||
427 | } | 550 | } |
428 | 551 | ||
429 | /* We try to cache the headers here to reduce networking access | 552 | int |
430 | especially for IMAP. When the buffer is NULL it means that | 553 | mu_header_remove (mu_header_t header, const char *fn, int n) |
431 | the field does not exist on the server and we should not | ||
432 | attempt to contact the server again for this field. */ | ||
433 | static int | ||
434 | header_set_fvalue (mu_header_t header, const char *name, char *buffer, | ||
435 | size_t len) | ||
436 | { | 554 | { |
437 | int rc; | 555 | int status; |
438 | struct _hdr_cache hcache; | 556 | struct mu_hdrent *ent; |
439 | 557 | ||
440 | if (!header->cache) | 558 | if (header == NULL || fn == NULL) |
441 | { | 559 | return EINVAL; |
442 | rc = mu_assoc_create (&header->cache, sizeof(struct _hdr_cache), | 560 | |
443 | MU_ASSOC_ICASE); | 561 | status = mu_header_fill (header); |
444 | if (rc) | 562 | if (status) |
445 | return rc; | 563 | return status; |
446 | mu_assoc_set_free (header->cache, _hdr_cache_destroy); | 564 | |
447 | } | 565 | ent = mu_hdrent_find (header, fn, n); |
448 | if (buffer == NULL) | 566 | if (!ent) |
567 | return MU_ERR_NOENT; | ||
568 | |||
569 | mu_hdrent_remove (header, ent); | ||
570 | free (ent); | ||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | int | ||
575 | mu_header_insert (mu_header_t header, | ||
576 | const char *fn, const char *fv, | ||
577 | const char *ref, int n, int flags) | ||
578 | { | ||
579 | int status; | ||
580 | struct mu_hdrent *ent; | ||
581 | |||
582 | if (header == NULL || fn == NULL || fv == NULL) | ||
583 | return EINVAL; | ||
584 | |||
585 | status = mu_header_fill (header); | ||
586 | if (status) | ||
587 | return status; | ||
588 | |||
589 | if (flags & MU_HEADER_REPLACE) | ||
449 | { | 590 | { |
450 | hcache.ptr = NULL; | 591 | if (!ref) |
451 | hcache.len = 0; | 592 | ref = fn; |
593 | ent = mu_hdrent_find (header, ref, n); | ||
594 | mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv)); | ||
452 | } | 595 | } |
453 | else | 596 | else |
454 | { | 597 | { |
455 | if (!len) | 598 | ent = mu_hdrent_create (header, NULL, |
456 | len = strlen (buffer); | 599 | fn, strlen (fn), fv, strlen (fv)); |
457 | hcache.ptr = malloc (len + 1); | 600 | if (!ent) |
458 | if (!hcache.ptr) | ||
459 | return ENOMEM; | 601 | return ENOMEM; |
460 | memcpy (hcache.ptr, buffer, len); | 602 | if (ref) |
461 | hcache.ptr[len] = 0; | 603 | return mu_hdrent_insert (header, ent, ref, n, |
462 | hcache.len = len; | 604 | flags & MU_HEADER_BEFORE); |
605 | else | ||
606 | mu_hdrent_prepend (header, ent); | ||
463 | } | 607 | } |
464 | rc = mu_assoc_install (header->cache, name, &hcache); | 608 | HEADER_SET_MODIFIED (header); |
465 | if (rc) | 609 | return 0; |
466 | free (hcache.ptr); | ||
467 | return rc; | ||
468 | } | 610 | } |
469 | 611 | ||
612 | |||
470 | int | 613 | int |
471 | mu_header_get_value (mu_header_t header, const char *name, char *buffer, | 614 | mu_header_sget_value_n (mu_header_t header, |
472 | size_t buflen, size_t *pn) | 615 | const char *name, int n, |
616 | const char **pval) | ||
473 | { | 617 | { |
474 | size_t i = 0; | 618 | int status; |
475 | size_t name_len; | 619 | struct mu_hdrent *ent; |
476 | size_t fn_len = 0, fv_len = 0; | ||
477 | int err = 0; | ||
478 | 620 | ||
479 | if (header == NULL || name == NULL) | 621 | if (header == NULL || name == NULL) |
480 | return EINVAL; | 622 | return EINVAL; |
623 | status = mu_header_fill (header); | ||
624 | if (status) | ||
625 | return status; | ||
626 | |||
627 | ent = mu_hdrent_find (header, name, n); | ||
628 | if (!ent) | ||
629 | return MU_ERR_NOENT; | ||
630 | |||
631 | *pval = MU_HDRENT_VALUE (header, ent); | ||
632 | return 0; | ||
633 | } | ||
481 | 634 | ||
482 | /* First scan our cache headers for hits. */ | 635 | int |
483 | err = header_get_fvalue (header, name, buffer, buflen, pn); | 636 | mu_header_aget_value_n (mu_header_t header, |
484 | switch (err) | 637 | const char *name, int n, |
638 | char **pval) | ||
639 | { | ||
640 | const char *s; | ||
641 | int status = mu_header_sget_value_n (header, name, n, &s); | ||
642 | if (status == 0) | ||
485 | { | 643 | { |
486 | case EINVAL: /* Permanent failure. */ | 644 | *pval = strdup (s); |
487 | err = MU_ERR_NOENT; | 645 | if (!*pval) |
488 | case ENOMEM: | 646 | status = ENOMEM; |
489 | if (pn) | ||
490 | *pn = 0; | ||
491 | case 0: | ||
492 | return err; | ||
493 | } | 647 | } |
648 | return status; | ||
649 | } | ||
494 | 650 | ||
495 | if (header->_get_value) | 651 | int |
496 | { | 652 | mu_header_get_value_n (mu_header_t header, const char *name, int n, |
497 | char buf[1024]; /* should suffice for field-value. */ | 653 | char *buffer, size_t buflen, size_t *pn) |
498 | size_t len = 0; | 654 | { |
499 | err = header->_get_value (header, name, buf, sizeof (buf), &len); | 655 | const char *s; |
500 | if (err == 0) | 656 | int status = mu_header_sget_value_n (header, name, n, &s); |
501 | { | 657 | if (status == 0) |
502 | /* Save in the fast header buffer. */ | ||
503 | header_set_fvalue (header, name, buf, 0); | ||
504 | if (buffer && buflen > 0) | ||
505 | { | ||
506 | buflen--; | ||
507 | buflen = (len < buflen) ? len : buflen; | ||
508 | memcpy (buffer, buf, buflen); | ||
509 | buffer[buflen] = '\0'; | ||
510 | } | ||
511 | else | ||
512 | buflen = len; | ||
513 | if (pn) | ||
514 | *pn = buflen; | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | /* Cache permanent failure also. */ | ||
519 | header_set_fvalue (header, name, NULL, 0); | ||
520 | } | ||
521 | return err; | ||
522 | } | ||
523 | |||
524 | /* Try to fill out the buffer, if we know how. */ | ||
525 | if (header->blurb == NULL) | ||
526 | { | 658 | { |
527 | err = fill_blurb (header); | 659 | size_t slen = strlen (s); |
528 | if (err != 0) | ||
529 | return err; | ||
530 | } | ||
531 | |||
532 | /* We set the threshold to be 1 less for the null. */ | ||
533 | --buflen; | ||
534 | 660 | ||
535 | for (name_len = strlen (name), i = 0; i < header->hdr_count; i++) | 661 | if (buffer) |
536 | { | ||
537 | fn_len = header->hdr[i].fn_end - header->hdr[i].fn; | ||
538 | if (fn_len == name_len && | ||
539 | strncasecmp (header->hdr[i].fn, name, fn_len) == 0) | ||
540 | { | 662 | { |
541 | fv_len = (header->hdr[i].fv_end - header->hdr[i].fv); | 663 | if (slen > buflen) |
542 | header_set_fvalue (header, name, header->hdr[i].fv, fv_len); | 664 | slen = buflen; |
543 | /* Can everything fit in the buffer. */ | 665 | memcpy (buffer, s, slen); |
544 | if (buffer && buflen > 0) | 666 | buffer[slen] = 0; |
545 | { | ||
546 | if (fv_len > buflen) | ||
547 | fv_len = buflen; | ||
548 | memcpy (buffer, header->hdr[i].fv, fv_len); | ||
549 | buffer[fv_len] = 0; | ||
550 | } | ||
551 | |||
552 | if (pn) | ||
553 | *pn = fv_len; | ||
554 | return 0; | ||
555 | } | 667 | } |
668 | if (pn) | ||
669 | *pn = slen; | ||
556 | } | 670 | } |
557 | 671 | return status; | |
558 | if (buffer) | ||
559 | *buffer = 0; | ||
560 | |||
561 | return MU_ERR_NOENT; | ||
562 | } | 672 | } |
563 | 673 | ||
674 | |||
675 | /* Unfolding functions */ | ||
564 | int | 676 | int |
565 | mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer, | 677 | mu_header_get_value_unfold_n (mu_header_t header, |
566 | size_t buflen, size_t *pn) | 678 | const char *name, int n, char *buffer, |
679 | size_t buflen, size_t *pn) | ||
567 | { | 680 | { |
568 | int rc = mu_header_get_value (header, name, buffer, buflen, pn); | 681 | int rc = mu_header_get_value_n (header, name, n, buffer, buflen, pn); |
569 | 682 | ||
570 | if (rc == 0) | 683 | if (rc == 0) |
571 | mu_string_unfold (buffer, pn); | 684 | mu_string_unfold (buffer, pn); |
... | @@ -573,413 +686,343 @@ mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer, | ... | @@ -573,413 +686,343 @@ mu_header_get_value_unfold (mu_header_t header, const char *name, char *buffer, |
573 | } | 686 | } |
574 | 687 | ||
575 | int | 688 | int |
576 | mu_header_aget_value (mu_header_t header, const char *name, char **pvalue) | 689 | mu_header_aget_value_unfold_n (mu_header_t header, const char *name, int n, |
690 | char **pvalue) | ||
577 | { | 691 | { |
578 | char *value; | 692 | int rc = mu_header_aget_value_n (header, name, n, pvalue); |
579 | size_t n = 0; | ||
580 | int status = mu_header_get_value (header, name, NULL, 0, &n); | ||
581 | if (status == 0) | ||
582 | { | ||
583 | value = calloc (n + 1, 1); | ||
584 | if (value == NULL) | ||
585 | return ENOMEM; | ||
586 | mu_header_get_value (header, name, value, n + 1, NULL); | ||
587 | *pvalue = value; | ||
588 | } | ||
589 | |||
590 | return status; | ||
591 | } | ||
592 | |||
593 | int | ||
594 | mu_header_aget_value_unfold (mu_header_t header, const char *name, char **pvalue) | ||
595 | { | ||
596 | int rc = mu_header_aget_value (header, name, pvalue); | ||
597 | if (rc == 0) | 693 | if (rc == 0) |
598 | mu_string_unfold (*pvalue, NULL); | 694 | mu_string_unfold (*pvalue, NULL); |
599 | return rc; | 695 | return rc; |
600 | } | 696 | } |
601 | 697 | ||
698 | |||
602 | int | 699 | int |
603 | mu_header_get_address (mu_header_t header, const char *name, mu_address_t *addr) | 700 | mu_header_get_address_n (mu_header_t header, const char *name, int n, |
701 | mu_address_t *addr) | ||
604 | { | 702 | { |
605 | char* value = NULL; | 703 | const char *value = NULL; |
606 | int status = mu_header_aget_value(header, name, &value); | 704 | int status = mu_header_sget_value_n (header, name, n, &value); |
607 | 705 | ||
608 | if(status) | 706 | if (status) |
609 | return status; | 707 | return status; |
610 | 708 | ||
611 | status = mu_address_create(addr, value); | 709 | status = mu_address_create(addr, value); |
612 | 710 | ||
613 | free(value); | ||
614 | |||
615 | return status; | 711 | return status; |
616 | } | 712 | } |
617 | 713 | ||
714 | |||
618 | int | 715 | int |
619 | mu_header_get_field_count (mu_header_t header, size_t *pcount) | 716 | mu_header_get_field_count (mu_header_t header, size_t *pcount) |
620 | { | 717 | { |
718 | size_t count; | ||
719 | size_t size; | ||
720 | size_t lines; | ||
721 | int status; | ||
722 | |||
621 | if (header == NULL) | 723 | if (header == NULL) |
622 | { | 724 | return EINVAL; |
623 | if (pcount) | ||
624 | *pcount = 0; | ||
625 | return EINVAL; | ||
626 | } | ||
627 | 725 | ||
628 | /* Try to fill out the buffer, if we know how. */ | 726 | status = mu_header_fill (header); |
629 | if (header->blurb == NULL) | 727 | if (status == 0) |
630 | { | 728 | { |
631 | int err = fill_blurb (header); | 729 | mu_hdrent_count (header, &count, &size, &lines); |
632 | if (err != 0) | 730 | |
633 | return err; | 731 | if (pcount) |
732 | *pcount = count; | ||
634 | } | 733 | } |
635 | 734 | ||
636 | if (pcount) | 735 | return status; |
637 | *pcount = header->hdr_count; | ||
638 | return 0; | ||
639 | } | 736 | } |
640 | 737 | ||
641 | int | 738 | int |
642 | mu_header_get_field_name (mu_header_t header, size_t num, char *buf, | 739 | mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr) |
643 | size_t buflen, size_t *nwriten) | ||
644 | { | 740 | { |
645 | size_t len; | 741 | int status; |
646 | 742 | ||
647 | if (header == NULL) | 743 | if (header == NULL) |
648 | return EINVAL; | 744 | return EINVAL; |
649 | 745 | ||
650 | /* Try to fill out the buffer, if we know how. */ | 746 | status = mu_header_fill (header); |
651 | if (header->blurb == NULL) | 747 | if (status == 0) |
652 | { | 748 | { |
653 | int err = fill_blurb (header); | 749 | struct mu_hdrent *ent = mu_hdrent_nth (header, num); |
654 | if (err != 0) | 750 | if (ent) |
655 | return err; | 751 | *sptr = MU_HDRENT_NAME (header, ent); |
752 | else | ||
753 | status = MU_ERR_NOENT; | ||
656 | } | 754 | } |
755 | return status; | ||
756 | } | ||
657 | 757 | ||
658 | if (header->hdr_count == 0 || num > header->hdr_count || num == 0) | 758 | int |
659 | return MU_ERR_NOENT; | 759 | mu_header_get_field_name (mu_header_t header, size_t num, char *buffer, |
660 | 760 | size_t buflen, size_t *pn) | |
661 | num--; | 761 | { |
662 | len = (header->hdr[num].fn_end - header->hdr[num].fn); | 762 | const char *s; |
663 | if (buf && buflen) | 763 | int status = mu_header_sget_field_name (header, num, &s); |
764 | if (status == 0) | ||
664 | { | 765 | { |
665 | /* save one for the null */ | 766 | size_t slen = strlen (s); |
666 | --buflen; | 767 | |
667 | len = (len > buflen) ? buflen : len; | 768 | if (buffer) |
668 | memcpy (buf, header->hdr[num].fn, len); | 769 | { |
669 | buf[len] = '\0'; | 770 | if (slen > buflen) |
771 | slen = buflen; | ||
772 | memcpy (buffer, s, slen); | ||
773 | buffer[slen] = 0; | ||
774 | } | ||
775 | if (pn) | ||
776 | *pn = slen; | ||
670 | } | 777 | } |
671 | if (nwriten) | 778 | return status; |
672 | *nwriten = len; | ||
673 | return 0; | ||
674 | } | 779 | } |
675 | 780 | ||
676 | int | 781 | int |
677 | mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue) | 782 | mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue) |
678 | { | 783 | { |
679 | char *value; | 784 | const char *s; |
680 | size_t n = 0; | 785 | int status = mu_header_sget_field_name (header, num, &s); |
681 | int status = mu_header_get_field_name (header, num, NULL, 0, &n); | ||
682 | if (status == 0) | 786 | if (status == 0) |
683 | { | 787 | { |
684 | value = calloc (n + 1, 1); | 788 | if ((*pvalue = strdup (s)) == NULL) |
685 | if (value == NULL) | 789 | status = ENOMEM; |
686 | return ENOMEM; | ||
687 | mu_header_get_field_name (header, num, value, n + 1, NULL); | ||
688 | *pvalue = value; | ||
689 | } | 790 | } |
690 | else | ||
691 | *pvalue = strdup (""); | ||
692 | return status; | 791 | return status; |
693 | } | 792 | } |
694 | 793 | ||
794 | |||
695 | int | 795 | int |
696 | mu_header_get_field_value (mu_header_t header, size_t num, char *buf, | 796 | mu_header_sget_field_value (mu_header_t header, size_t num, const char **sptr) |
697 | size_t buflen, size_t *nwritten) | ||
698 | { | 797 | { |
699 | size_t len; | 798 | int status; |
700 | 799 | ||
701 | if (header == NULL || num < 1) | 800 | if (header == NULL) |
702 | return EINVAL; | 801 | return EINVAL; |
703 | 802 | ||
704 | /* Try to fill out the buffer, if we know how. */ | 803 | status = mu_header_fill (header); |
705 | if (header->blurb == NULL) | 804 | if (status == 0) |
706 | { | ||
707 | int err = fill_blurb (header); | ||
708 | if (err != 0) | ||
709 | return err; | ||
710 | } | ||
711 | |||
712 | if (header->hdr_count == 0 || num > header->hdr_count || num == 0) | ||
713 | return MU_ERR_NOENT; | ||
714 | |||
715 | num--; | ||
716 | len = header->hdr[num].fv_end - header->hdr[num].fv; | ||
717 | if (buf && buflen > 0) | ||
718 | { | 805 | { |
719 | /* save one for the null */ | 806 | struct mu_hdrent *ent = mu_hdrent_nth (header, num); |
720 | --buflen; | 807 | if (ent) |
721 | len = (len > buflen) ? buflen : len; | 808 | *sptr = MU_HDRENT_VALUE (header, ent); |
722 | memcpy (buf, header->hdr[num].fv, len); | 809 | else |
723 | buf[len] = '\0'; | 810 | status = MU_ERR_NOENT; |
724 | } | 811 | } |
725 | 812 | return status; | |
726 | if (nwritten) | ||
727 | *nwritten = len; | ||
728 | return 0; | ||
729 | } | 813 | } |
730 | 814 | ||
731 | int | 815 | int |
732 | mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf, | 816 | mu_header_get_field_value (mu_header_t header, size_t num, char *buffer, |
733 | size_t buflen, size_t *nwritten) | 817 | size_t buflen, size_t *pn) |
734 | { | 818 | { |
735 | int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten); | 819 | const char *s; |
736 | if (rc == 0) | 820 | int status = mu_header_sget_field_value (header, num, &s); |
737 | mu_string_unfold (buf, nwritten); | 821 | if (status == 0) |
738 | return rc; | 822 | { |
823 | size_t slen = strlen (s); | ||
824 | |||
825 | if (buffer) | ||
826 | { | ||
827 | if (slen > buflen) | ||
828 | slen = buflen; | ||
829 | memcpy (buffer, s, slen); | ||
830 | buffer[slen] = 0; | ||
831 | } | ||
832 | if (pn) | ||
833 | *pn = slen; | ||
834 | } | ||
835 | return status; | ||
739 | } | 836 | } |
740 | 837 | ||
741 | int | 838 | int |
742 | mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue) | 839 | mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue) |
743 | { | 840 | { |
744 | char *value; | 841 | const char *s; |
745 | size_t n = 0; | 842 | int status = mu_header_sget_field_value (header, num, &s); |
746 | int status = mu_header_get_field_value (header, num, NULL, 0, &n); | ||
747 | if (status == 0) | 843 | if (status == 0) |
748 | { | 844 | { |
749 | value = calloc (n + 1, 1); | 845 | if ((*pvalue = strdup (s)) == NULL) |
750 | if (value == NULL) | 846 | status = ENOMEM; |
751 | return ENOMEM; | ||
752 | mu_header_get_field_value (header, num, value, n + 1, NULL); | ||
753 | *pvalue = value; | ||
754 | } | 847 | } |
755 | else | ||
756 | *pvalue = strdup (""); | ||
757 | return status; | 848 | return status; |
758 | } | 849 | } |
759 | 850 | ||
760 | int | 851 | int |
761 | mu_header_aget_field_value_unfold (mu_header_t header, size_t num, char **pvalue) | 852 | mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf, |
853 | size_t buflen, size_t *nwritten) | ||
762 | { | 854 | { |
763 | int rc = mu_header_aget_field_value (header, num, pvalue); | 855 | int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten); |
764 | if (rc == 0) | 856 | if (rc == 0) |
765 | mu_string_unfold (*pvalue, NULL); | 857 | mu_string_unfold (buf, nwritten); |
766 | return rc; | 858 | return rc; |
767 | } | 859 | } |
768 | 860 | ||
769 | int | 861 | int |
770 | mu_header_set_lines (mu_header_t header, int (*_lines) | 862 | mu_header_aget_field_value_unfold (mu_header_t header, size_t num, |
771 | (mu_header_t, size_t *), void *owner) | 863 | char **pvalue) |
772 | { | 864 | { |
773 | if (header == NULL) | 865 | int rc = mu_header_aget_field_value (header, num, pvalue); |
774 | return EINVAL; | 866 | if (rc == 0) |
775 | if (header->owner != owner) | 867 | mu_string_unfold (*pvalue, NULL); |
776 | return EACCES; | 868 | return rc; |
777 | header->_lines = _lines; | ||
778 | return 0; | ||
779 | } | 869 | } |
780 | 870 | ||
871 | |||
781 | int | 872 | int |
782 | mu_header_lines (mu_header_t header, size_t *plines) | 873 | mu_header_lines (mu_header_t header, size_t *plines) |
783 | { | 874 | { |
784 | int n; | 875 | int status; |
785 | size_t lines = 0; | ||
786 | 876 | ||
787 | if (header == NULL) | 877 | if (header == NULL) |
788 | return EINVAL; | 878 | return EINVAL; |
789 | if (plines == NULL) | 879 | if (plines == NULL) |
790 | return MU_ERR_OUT_PTR_NULL; | 880 | return MU_ERR_OUT_PTR_NULL; |
791 | 881 | ||
792 | /* Overload. */ | 882 | status = mu_header_fill (header); |
793 | if (header->_lines) | 883 | if (status == 0) |
794 | return header->_lines (header, plines); | ||
795 | |||
796 | /* Try to fill out the buffer, if we know how. */ | ||
797 | if (header->blurb == NULL) | ||
798 | { | ||
799 | int err = fill_blurb (header); | ||
800 | if (err != 0) | ||
801 | return err; | ||
802 | } | ||
803 | |||
804 | for (n = header->blurb_len - 1; n >= 0; n--) | ||
805 | { | 884 | { |
806 | if (header->blurb[n] == '\n') | 885 | size_t count; |
807 | lines++; | 886 | size_t size; |
887 | size_t lines; | ||
888 | mu_hdrent_count (header, &count, &size, &lines); | ||
889 | *plines = lines + 1; | ||
808 | } | 890 | } |
809 | if (plines) | 891 | return status; |
810 | *plines = lines; | ||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | int | ||
815 | mu_header_set_size (mu_header_t header, int (*_size) | ||
816 | (mu_header_t, size_t *), void *owner) | ||
817 | { | ||
818 | if (header == NULL) | ||
819 | return EINVAL; | ||
820 | if (header->owner != owner) | ||
821 | return EACCES; | ||
822 | header->_size = _size; | ||
823 | return 0; | ||
824 | } | 892 | } |
825 | 893 | ||
826 | int | 894 | int |
827 | mu_header_size (mu_header_t header, size_t *psize) | 895 | mu_header_size (mu_header_t header, size_t *psize) |
828 | { | 896 | { |
829 | if (header == NULL) | 897 | int status; |
830 | return EINVAL; | ||
831 | 898 | ||
832 | /* Overload. */ | 899 | if (header == NULL) |
833 | if (header->_size) | 900 | return EINVAL; |
834 | return header->_size (header, psize); | 901 | if (psize == NULL) |
902 | return MU_ERR_OUT_PTR_NULL; | ||
835 | 903 | ||
836 | /* Try to fill out the buffer, if we know how. */ | 904 | status = mu_header_fill (header); |
837 | if (header->blurb == NULL) | 905 | if (status == 0) |
838 | { | 906 | { |
839 | int err = fill_blurb (header); | 907 | size_t count; |
840 | if (err != 0) | 908 | size_t size; |
841 | return err; | 909 | size_t lines; |
910 | mu_hdrent_count (header, &count, &size, &lines); | ||
911 | *psize = size + 1; | ||
842 | } | 912 | } |
843 | 913 | return status; | |
844 | if (psize) | ||
845 | *psize = header->blurb_len; | ||
846 | return 0; | ||
847 | } | 914 | } |
848 | 915 | ||
849 | int | 916 | |
850 | mu_header_set_get_value (mu_header_t header, int (*_get_value) | 917 | static void |
851 | (mu_header_t, const char *, char *, size_t, size_t *), | 918 | mu_hdrent_fixup (mu_header_t hdr, struct mu_hdrent *ent) |
852 | void *owner) | ||
853 | { | 919 | { |
854 | if (header == NULL) | 920 | char *s = MU_HDRENT_NAME (hdr, ent); |
855 | return EINVAL; | 921 | s[ent->nlen] = ':'; |
856 | if (header->owner != owner) | 922 | s = MU_HDRENT_VALUE (hdr, ent); |
857 | return EACCES; | 923 | s[ent->vlen] = '\n'; |
858 | header->_get_value = _get_value; | ||
859 | return 0; | ||
860 | } | 924 | } |
861 | 925 | ||
862 | int | 926 | static void |
863 | mu_header_set_set_value (mu_header_t header, int (*_set_value) | 927 | mu_hdrent_unroll_fixup (mu_header_t hdr, struct mu_hdrent *ent) |
864 | (mu_header_t , const char *, const char *, int), | ||
865 | void *owner) | ||
866 | { | 928 | { |
867 | if (header == NULL) | 929 | char *s = MU_HDRENT_NAME (hdr, ent); |
868 | return EINVAL; | 930 | s[ent->nlen] = 0; |
869 | if (header->owner != owner) | 931 | s = MU_HDRENT_VALUE (hdr, ent); |
870 | return EACCES; | 932 | s[ent->vlen] = 0; |
871 | header->_set_value = _set_value; | ||
872 | return 0; | ||
873 | } | 933 | } |
874 | 934 | ||
875 | int | 935 | static int |
876 | mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner) | 936 | header_read (mu_stream_t is, char *buffer, size_t buflen, |
937 | mu_off_t off, size_t *pnread) | ||
877 | { | 938 | { |
878 | if (header == NULL) | 939 | mu_header_t header; |
940 | struct mu_hdrent *ent; | ||
941 | size_t ent_off; | ||
942 | int status; | ||
943 | size_t nread; | ||
944 | |||
945 | if (is == NULL) | ||
879 | return EINVAL; | 946 | return EINVAL; |
880 | if (header->owner != owner) | ||
881 | return EACCES; | ||
882 | header->stream = stream; | ||
883 | return 0; | ||
884 | } | ||
885 | 947 | ||
886 | int | 948 | header = mu_stream_get_owner (is); |
887 | mu_header_set_fill (mu_header_t header, int | 949 | status = mu_header_fill (header); |
888 | (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *), | 950 | if (status) |
889 | void *owner) | 951 | return status; |
890 | { | 952 | |
891 | if (header == NULL) | 953 | if (mu_hdrent_find_stream_pos (header, off, &ent, &ent_off)) |
892 | return EINVAL; | 954 | { |
893 | if (header->owner != owner) | 955 | if (pnread) |
894 | return EACCES; | 956 | *pnread = 0; |
895 | header->_fill = _fill; | 957 | return 0; |
958 | } | ||
959 | |||
960 | for (nread = 0; nread < buflen && ent; ent = ent->next) | ||
961 | { | ||
962 | size_t rest = buflen - nread; | ||
963 | size_t strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off; | ||
964 | if (rest > strsize) | ||
965 | rest = strsize; | ||
966 | mu_hdrent_fixup (header, ent); | ||
967 | memcpy (buffer + nread, MU_HDRENT_NAME (header, ent) + ent_off, rest); | ||
968 | mu_hdrent_unroll_fixup (header, ent); | ||
969 | nread += rest; | ||
970 | off += rest; | ||
971 | ent_off = 0; | ||
972 | } | ||
973 | if (pnread) | ||
974 | *pnread = nread; | ||
896 | return 0; | 975 | return 0; |
897 | } | 976 | } |
898 | 977 | ||
899 | static int | 978 | static int |
900 | fill_blurb (mu_header_t header) | 979 | header_readline (mu_stream_t is, char *buffer, size_t buflen, |
980 | mu_off_t off, size_t *pnread) | ||
901 | { | 981 | { |
982 | mu_header_t header; | ||
983 | struct mu_hdrent *ent; | ||
984 | size_t ent_off; | ||
902 | int status; | 985 | int status; |
903 | char buf[1024]; | 986 | size_t strsize; |
904 | size_t nread; | 987 | |
905 | 988 | if (is == NULL) | |
906 | if (header->_fill == NULL) | 989 | return EINVAL; |
907 | return 0; | ||
908 | |||
909 | /* The entire header is now ours(part of mu_header_t), clear all the | ||
910 | overloading. */ | ||
911 | header_free_cache (header); | ||
912 | header->_get_value = NULL; | ||
913 | header->_set_value = NULL; | ||
914 | header->_size = NULL; | ||
915 | header->_lines = NULL; | ||
916 | |||
917 | if (header->mstream == NULL) | ||
918 | { | ||
919 | status = mu_memory_stream_create (&header->mstream, NULL, MU_STREAM_RDWR); | ||
920 | if (status != 0) | ||
921 | return status; | ||
922 | mu_stream_open (header->mstream); | ||
923 | header->stream_len = 0; | ||
924 | } | ||
925 | 990 | ||
926 | /* Bring in the entire header. */ | 991 | header = mu_stream_get_owner (is); |
927 | do | 992 | status = mu_header_fill (header); |
993 | if (status) | ||
994 | return status; | ||
995 | |||
996 | if (mu_hdrent_find_stream_pos (header, off, &ent, &ent_off)) | ||
928 | { | 997 | { |
929 | nread = 0; | 998 | if (pnread) |
930 | status = header->_fill (header, buf, sizeof buf, | 999 | *pnread = 0; |
931 | header->stream_len, &nread) ; | 1000 | return 0; |
932 | if (status != 0) | ||
933 | { | ||
934 | if (status != EAGAIN && status != EINTR) | ||
935 | { | ||
936 | mu_stream_destroy (&(header->mstream), NULL); | ||
937 | header->stream_len = 0; | ||
938 | } | ||
939 | return status; | ||
940 | } | ||
941 | if (nread > 0) | ||
942 | { | ||
943 | status = mu_stream_write (header->mstream, buf, nread, header->stream_len, NULL); | ||
944 | if (status != 0) | ||
945 | { | ||
946 | mu_stream_destroy (&(header->mstream), NULL); | ||
947 | header->stream_len = 0; | ||
948 | return status; | ||
949 | } | ||
950 | header->stream_len += nread; | ||
951 | } | ||
952 | } | 1001 | } |
953 | while (nread > 0); | ||
954 | 1002 | ||
955 | /* parse it. */ | 1003 | strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off; |
956 | { | 1004 | if (buflen > strsize) |
957 | char *blurb; | 1005 | buflen = strsize; |
958 | size_t len = header->stream_len; | 1006 | mu_hdrent_fixup (header, ent); |
959 | blurb = calloc (1, len + 1); | 1007 | memcpy (buffer, MU_HDRENT_NAME (header, ent) + ent_off, buflen); |
960 | if (blurb) | 1008 | mu_hdrent_unroll_fixup (header, ent); |
961 | { | 1009 | if (pnread) |
962 | mu_stream_read (header->mstream, blurb, len, 0, &len); | 1010 | *pnread = buflen; |
963 | status = header_parse (header, blurb, len); | 1011 | return 0; |
964 | } | ||
965 | free (blurb); | ||
966 | } | ||
967 | mu_stream_destroy (&header->mstream, NULL); | ||
968 | header->stream_len = 0; | ||
969 | return status; | ||
970 | } | 1012 | } |
971 | 1013 | ||
972 | static int | 1014 | static int |
973 | header_write (mu_stream_t os, const char *buf, size_t buflen, | 1015 | header_write (mu_stream_t os, const char *buf, size_t buflen, |
974 | mu_off_t off, size_t *pnwrite) | 1016 | mu_off_t off, size_t *pnwrite) |
975 | { | 1017 | { |
1018 | size_t wrsize = 0; | ||
976 | mu_header_t header = mu_stream_get_owner (os); | 1019 | mu_header_t header = mu_stream_get_owner (os); |
977 | int status; | 1020 | int status; |
978 | 1021 | ||
979 | if (header == NULL) | 1022 | if (header == NULL) |
980 | return EINVAL; | 1023 | return EINVAL; |
981 | 1024 | ||
982 | if ((size_t)off != header->stream_len) | 1025 | if ((size_t)off != header->mstream_size) |
983 | return ESPIPE; | 1026 | return ESPIPE; |
984 | 1027 | ||
985 | /* Skip the obvious. */ | 1028 | /* Skip the obvious. */ |
... | @@ -990,53 +1033,49 @@ header_write (mu_stream_t os, const char *buf, size_t buflen, | ... | @@ -990,53 +1033,49 @@ header_write (mu_stream_t os, const char *buf, size_t buflen, |
990 | return 0; | 1033 | return 0; |
991 | } | 1034 | } |
992 | 1035 | ||
993 | if (header->mstream == NULL) | 1036 | if (!header->mstream) |
994 | { | 1037 | { |
995 | status = mu_memory_stream_create (&header->mstream, NULL, MU_STREAM_RDWR); | 1038 | status = mu_memory_stream_create (&header->mstream, NULL, |
996 | if (status != 0) | 1039 | MU_STREAM_RDWR); |
1040 | if (status) | ||
997 | return status; | 1041 | return status; |
998 | status = mu_stream_open (header->mstream); | 1042 | status = mu_stream_open (header->mstream); |
999 | if (status != 0) | 1043 | if (status) |
1000 | { | 1044 | { |
1001 | mu_stream_destroy(&header->mstream, NULL); | 1045 | mu_stream_destroy (&header->mstream, NULL); |
1002 | return status; | 1046 | return status; |
1003 | } | 1047 | } |
1004 | header->stream_len = 0; | 1048 | header->mstream_size = 0; |
1005 | } | 1049 | } |
1006 | 1050 | ||
1007 | status = mu_stream_write (header->mstream, buf, buflen, header->stream_len, &buflen); | 1051 | do |
1008 | if (status != 0) | ||
1009 | { | 1052 | { |
1010 | mu_stream_destroy (&header->mstream, NULL); | 1053 | size_t nbytes; |
1011 | header->stream_len = 0; | 1054 | status = mu_stream_write (header->mstream, buf + wrsize, buflen - wrsize, |
1012 | return status; | 1055 | header->mstream_size, &nbytes); |
1056 | if (status) | ||
1057 | { | ||
1058 | mu_stream_destroy (&header->mstream, NULL); | ||
1059 | header->mstream_size = 0; | ||
1060 | return status; | ||
1061 | } | ||
1062 | if (nbytes == 0) | ||
1063 | break; | ||
1064 | wrsize += nbytes; | ||
1065 | header->mstream_size += nbytes; | ||
1013 | } | 1066 | } |
1014 | header->stream_len += buflen; | 1067 | while (buflen); |
1015 | 1068 | ||
1016 | /* We detect an empty line .i.e "^\n$" this signal the end of the | 1069 | if (header->mstream_size > 1) |
1017 | header. */ | ||
1018 | if (header->stream_len) | ||
1019 | { | 1070 | { |
1020 | int finish = 0; | 1071 | char nlbuf[2]; |
1021 | char nlnl[2]; | 1072 | size_t len; |
1022 | nlnl[1] = nlnl[0] = '\0'; | 1073 | status = mu_stream_read (header->mstream, nlbuf, 2, |
1023 | mu_stream_read (header->mstream, nlnl, 1, 0, NULL); | 1074 | header->mstream_size - 2, &len); |
1024 | if (nlnl[0] == '\n') | 1075 | if (status == 0 && len == 2 && memcmp (nlbuf, "\n\n", 2) == 0) |
1025 | { | ||
1026 | finish = 1; | ||
1027 | } | ||
1028 | else | ||
1029 | { | ||
1030 | mu_stream_read (header->mstream, nlnl, 2, header->stream_len - 2, NULL); | ||
1031 | if (nlnl[0] == '\n' && nlnl[1] == '\n') | ||
1032 | { | ||
1033 | finish = 1; | ||
1034 | } | ||
1035 | } | ||
1036 | if (finish) | ||
1037 | { | 1076 | { |
1038 | char *blurb; | 1077 | char *blurb; |
1039 | size_t len = header->stream_len; | 1078 | size_t len = header->mstream_size; |
1040 | blurb = calloc (1, len + 1); | 1079 | blurb = calloc (1, len + 1); |
1041 | if (blurb) | 1080 | if (blurb) |
1042 | { | 1081 | { |
... | @@ -1045,95 +1084,36 @@ header_write (mu_stream_t os, const char *buf, size_t buflen, | ... | @@ -1045,95 +1084,36 @@ header_write (mu_stream_t os, const char *buf, size_t buflen, |
1045 | } | 1084 | } |
1046 | free (blurb); | 1085 | free (blurb); |
1047 | mu_stream_destroy (&header->mstream, NULL); | 1086 | mu_stream_destroy (&header->mstream, NULL); |
1048 | header->stream_len = 0; | 1087 | header->mstream_size = 0; |
1049 | } | 1088 | } |
1050 | } | ||
1051 | |||
1052 | if (pnwrite) | ||
1053 | *pnwrite = buflen; | ||
1054 | return 0; | ||
1055 | |||
1056 | } | ||
1057 | |||
1058 | static int | ||
1059 | header_read (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pnread) | ||
1060 | { | ||
1061 | int len; | ||
1062 | mu_header_t header = mu_stream_get_owner (is); | ||
1063 | |||
1064 | if (is == NULL || header == NULL) | ||
1065 | return EINVAL; | ||
1066 | |||
1067 | if (buf == NULL || buflen == 0) | ||
1068 | { | ||
1069 | if (pnread) | ||
1070 | *pnread = 0; | ||
1071 | return 0; | ||
1072 | } | 1089 | } |
1073 | 1090 | ||
1074 | /* Try to fill out the buffer, if we know how. */ | 1091 | if (pnwrite) |
1075 | if (header->blurb == NULL) | 1092 | *pnwrite = wrsize; |
1076 | { | 1093 | |
1077 | int err = fill_blurb (header); | ||
1078 | if (err != 0) | ||
1079 | return err; | ||
1080 | } | ||
1081 | |||
1082 | len = header->blurb_len - off; | ||
1083 | if (len > 0) | ||
1084 | { | ||
1085 | len = (buflen < (size_t)len) ? buflen : (size_t)len; | ||
1086 | memcpy (buf, header->blurb + off, len); | ||
1087 | } | ||
1088 | else | ||
1089 | len = 0; | ||
1090 | |||
1091 | if (pnread) | ||
1092 | *pnread = len; | ||
1093 | return 0; | 1094 | return 0; |
1094 | } | 1095 | } |
1095 | 1096 | ||
1096 | static int | 1097 | static int |
1097 | header_readline (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pn) | 1098 | header_size (mu_stream_t str, mu_off_t *psize) |
1098 | { | 1099 | { |
1099 | int len; | 1100 | mu_header_t header; |
1100 | mu_header_t header = mu_stream_get_owner (is); | 1101 | int status; |
1101 | 1102 | size_t size; | |
1102 | if (is == NULL || header == NULL) | 1103 | |
1104 | if (str == NULL) | ||
1103 | return EINVAL; | 1105 | return EINVAL; |
1104 | 1106 | if (psize == NULL) | |
1105 | if (buf == NULL || buflen == 0) | 1107 | return MU_ERR_OUT_PTR_NULL; |
1106 | { | 1108 | |
1107 | if (pn) | 1109 | header = mu_stream_get_owner (str); |
1108 | *pn = 0; | 1110 | status = mu_header_fill (header); |
1109 | return 0; | 1111 | if (status) |
1110 | } | 1112 | return status; |
1111 | 1113 | status = mu_header_size (header, &size); | |
1112 | /* Try to fill out the buffer, if we know how. */ | 1114 | if (status == 0) |
1113 | if (header->blurb == NULL) | 1115 | *psize = size; |
1114 | { | 1116 | return status; |
1115 | int err = fill_blurb (header); | ||
1116 | if (err != 0) | ||
1117 | return err; | ||
1118 | } | ||
1119 | |||
1120 | buflen--; /* Space for the null. */ | ||
1121 | |||
1122 | len = header->blurb_len - off; | ||
1123 | if (len > 0) | ||
1124 | { | ||
1125 | char *nl = memchr (header->blurb + off, '\n', len); | ||
1126 | if (nl) | ||
1127 | len = nl - (header->blurb + off) + 1; | ||
1128 | len = (buflen < (size_t)len) ? buflen : (size_t)len; | ||
1129 | memcpy (buf, header->blurb + off, len); | ||
1130 | } | ||
1131 | else | ||
1132 | len = 0; | ||
1133 | if (pn) | ||
1134 | *pn = len; | ||
1135 | buf[len] = '\0'; | ||
1136 | return 0; | ||
1137 | } | 1117 | } |
1138 | 1118 | ||
1139 | int | 1119 | int |
... | @@ -1141,18 +1121,57 @@ mu_header_get_stream (mu_header_t header, mu_stream_t *pstream) | ... | @@ -1141,18 +1121,57 @@ mu_header_get_stream (mu_header_t header, mu_stream_t *pstream) |
1141 | { | 1121 | { |
1142 | if (header == NULL) | 1122 | if (header == NULL) |
1143 | return EINVAL; | 1123 | return EINVAL; |
1124 | |||
1144 | if (pstream == NULL) | 1125 | if (pstream == NULL) |
1145 | return MU_ERR_OUT_PTR_NULL; | 1126 | return MU_ERR_OUT_PTR_NULL; |
1127 | |||
1146 | if (header->stream == NULL) | 1128 | if (header->stream == NULL) |
1147 | { | 1129 | { |
1148 | int status = mu_stream_create (&(header->stream), MU_STREAM_RDWR, header); | 1130 | int status = mu_stream_create (&header->stream, MU_STREAM_RDWR, header); |
1149 | if (status != 0) | 1131 | if (status != 0) |
1150 | return status; | 1132 | return status; |
1151 | mu_stream_set_read (header->stream, header_read, header); | 1133 | mu_stream_set_read (header->stream, header_read, header); |
1152 | mu_stream_set_readline (header->stream, header_readline, header); | 1134 | mu_stream_set_readline (header->stream, header_readline, header); |
1153 | mu_stream_set_write (header->stream, header_write, header); | 1135 | mu_stream_set_write (header->stream, header_write, header); |
1136 | mu_stream_set_size (header->stream, header_size, header); | ||
1154 | } | 1137 | } |
1155 | *pstream = header->stream; | 1138 | *pstream = header->stream; |
1156 | return 0; | 1139 | return 0; |
1157 | } | 1140 | } |
1158 | 1141 | ||
1142 | |||
1143 | |||
1144 | int | ||
1145 | mu_header_set_fill (mu_header_t header, int | ||
1146 | (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *), | ||
1147 | void *owner) | ||
1148 | { | ||
1149 | if (header == NULL) | ||
1150 | return EINVAL; | ||
1151 | if (header->owner != owner) | ||
1152 | return EACCES; | ||
1153 | header->_fill = _fill; | ||
1154 | return 0; | ||
1155 | } | ||
1156 | |||
1157 | |||
1158 | void * | ||
1159 | mu_header_get_owner (mu_header_t header) | ||
1160 | { | ||
1161 | return (header) ? header->owner : NULL; | ||
1162 | } | ||
1163 | |||
1164 | int | ||
1165 | mu_header_is_modified (mu_header_t header) | ||
1166 | { | ||
1167 | return (header) ? (header->flags & HEADER_MODIFIED) : 0; | ||
1168 | } | ||
1169 | |||
1170 | int | ||
1171 | mu_header_clear_modified (mu_header_t header) | ||
1172 | { | ||
1173 | if (header) | ||
1174 | header->flags &= ~HEADER_MODIFIED; | ||
1175 | return 0; | ||
1176 | } | ||
1177 | ... | ... |
-
Please register or sign in to post a comment