common: Fix iobuf API of filter function for alignment.
[gnupg.git] / common / t-iobuf.c
1 #include <config.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <stdlib.h>
6
7 #include "iobuf.h"
8 #include "stringhelp.h"
9
10 /* Return every other byte.  In particular, reads two bytes, returns
11    the second one.  */
12 static int
13 every_other_filter (void *opaque, int control,
14                     iobuf_t chain, byte *buf, size_t *len)
15 {
16   (void) opaque;
17
18   if (control == IOBUFCTRL_DESC)
19     {
20       mem2str (buf, "every_other_filter", *len);
21     }
22   if (control == IOBUFCTRL_UNDERFLOW)
23     {
24       int c = iobuf_readbyte (chain);
25       int c2;
26       if (c == -1)
27         c2 = -1;
28       else
29         c2 = iobuf_readbyte (chain);
30
31       /* printf ("Discarding %d (%c); return %d (%c)\n", c, c, c2, c2); */
32
33       if (c2 == -1)
34         {
35           *len = 0;
36           return -1;
37         }
38
39       *buf = c2;
40       *len = 1;
41
42       return 0;
43     }
44
45   return 0;
46 }
47
48 static int
49 double_filter (void *opaque, int control,
50                iobuf_t chain, byte *buf, size_t *len)
51 {
52   (void) opaque;
53
54   if (control == IOBUFCTRL_DESC)
55     {
56       mem2str (buf, "double_filter", *len);
57     }
58   if (control == IOBUFCTRL_FLUSH)
59     {
60       int i;
61
62       for (i = 0; i < *len; i ++)
63         {
64           int rc;
65
66           rc = iobuf_writebyte (chain, buf[i]);
67           if (rc)
68             return rc;
69           rc = iobuf_writebyte (chain, buf[i]);
70           if (rc)
71             return rc;
72         }
73     }
74
75   return 0;
76 }
77
78 struct content_filter_state
79 {
80   int pos;
81   int len;
82   const char *buffer;
83 };
84
85 static struct content_filter_state *
86 content_filter_new (const char *buffer)
87 {
88   struct content_filter_state *state
89     = malloc (sizeof (struct content_filter_state));
90
91   state->pos = 0;
92   state->len = strlen (buffer);
93   state->buffer = buffer;
94
95   return state;
96 }
97
98 static int
99 content_filter (void *opaque, int control,
100                 iobuf_t chain, byte *buf, size_t *len)
101 {
102   struct content_filter_state *state = opaque;
103
104   (void) chain;
105
106   if (control == IOBUFCTRL_UNDERFLOW)
107     {
108       int remaining = state->len - state->pos;
109       int toread = *len;
110       assert (toread > 0);
111
112       if (toread > remaining)
113         toread = remaining;
114
115       memcpy (buf, &state->buffer[state->pos], toread);
116
117       state->pos += toread;
118
119       *len = toread;
120
121       if (toread == 0)
122         return -1;
123       return 0;
124     }
125
126   return 0;
127 }
128
129 int
130 main (int argc, char *argv[])
131 {
132   (void) argc;
133   (void) argv;
134
135   /* A simple test to make sure filters work.  We use a static buffer
136      and then add a filter in front of it that returns every other
137      character.  */
138   {
139     char *content = "0123456789abcdefghijklm";
140     iobuf_t iobuf;
141     int c;
142     int n;
143     int rc;
144
145     iobuf = iobuf_temp_with_content (content, strlen (content));
146     rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
147     assert (rc == 0);
148
149     n = 0;
150     while ((c = iobuf_readbyte (iobuf)) != -1)
151       {
152         /* printf ("%d: %c\n", n + 1, (char) c); */
153         assert (content[2 * n + 1] == c);
154         n ++;
155       }
156     /* printf ("Got EOF after reading %d bytes (content: %d)\n", */
157     /*         n, strlen (content)); */
158     assert (n == strlen (content) / 2);
159
160     iobuf_close (iobuf);
161   }
162
163   /* A simple test to check buffering.  Make sure that when we add a
164      filter to a pipeline, any buffered data gets processed by the */
165   {
166     char *content = "0123456789abcdefghijklm";
167     iobuf_t iobuf;
168     int c;
169     int n;
170     int rc;
171     int i;
172
173     iobuf = iobuf_temp_with_content (content, strlen (content));
174
175     n = 0;
176     for (i = 0; i < 10; i ++)
177       {
178         c = iobuf_readbyte (iobuf);
179         assert (content[i] == c);
180         n ++;
181       }
182
183     rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
184     assert (rc == 0);
185
186     while ((c = iobuf_readbyte (iobuf)) != -1)
187       {
188         /* printf ("%d: %c\n", n + 1, (char) c); */
189         assert (content[2 * (n - 5) + 1] == c);
190         n ++;
191       }
192     assert (n == 10 + (strlen (content) - 10) / 2);
193   }
194
195
196   /* A simple test to check that iobuf_read_line works.  */
197   {
198     /* - 3 characters plus new line
199        - 4 characters plus new line
200        - 5 characters plus new line
201        - 5 characters, no new line
202      */
203     char *content = "abc\ndefg\nhijkl\nmnopq";
204     iobuf_t iobuf;
205     byte *buffer;
206     unsigned size;
207     unsigned max_len;
208     int n;
209
210     iobuf = iobuf_temp_with_content (content, strlen(content));
211
212     /* We read a line with 3 characters plus a newline.  If we
213        allocate a buffer that is 5 bytes long, then no reallocation
214        should be required.  */
215     size = 5;
216     buffer = malloc (size);
217     assert (buffer);
218     max_len = 100;
219     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
220     assert (n == 4);
221     assert (strcmp (buffer, "abc\n") == 0);
222     assert (size == 5);
223     assert (max_len == 100);
224     free (buffer);
225
226     /* We now read a line with 4 characters plus a newline.  This
227        requires 6 bytes of storage.  We pass a buffer that is 5 bytes
228        large and we allow the buffer to be grown.  */
229     size = 5;
230     buffer = malloc (size);
231     max_len = 100;
232     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
233     assert (n == 5);
234     assert (strcmp (buffer, "defg\n") == 0);
235     assert (size >= 6);
236     /* The string shouldn't have been truncated (max_len == 0).  */
237     assert (max_len == 100);
238     free (buffer);
239
240     /* We now read a line with 5 characters plus a newline.  This
241        requires 7 bytes of storage.  We pass a buffer that is 5 bytes
242        large and we don't allow the buffer to be grown.  */
243     size = 5;
244     buffer = malloc (size);
245     max_len = 5;
246     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
247     assert (n == 4);
248     /* Note: the string should still have a trailing \n.  */
249     assert (strcmp (buffer, "hij\n") == 0);
250     assert (size == 5);
251     /* The string should have been truncated (max_len == 0).  */
252     assert (max_len == 0);
253     free (buffer);
254
255     /* We now read a line with 6 characters without a newline.  This
256        requires 7 bytes of storage.  We pass a NULL buffer and we
257        don't allow the buffer to be grown larger than 5 bytes.  */
258     size = 5;
259     buffer = NULL;
260     max_len = 5;
261     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
262     assert (n == 4);
263     /* Note: the string should still have a trailing \n.  */
264     assert (strcmp (buffer, "mno\n") == 0);
265     assert (size == 5);
266     /* The string should have been truncated (max_len == 0).  */
267     assert (max_len == 0);
268     free (buffer);
269   }
270
271   {
272     /* - 10 characters, EOF
273        - 17 characters, EOF
274      */
275     char *content = "abcdefghijklmnopq";
276     char *content2 = "0123456789";
277     iobuf_t iobuf;
278     int rc;
279     int c;
280     int n;
281     int lastc = 0;
282
283     iobuf = iobuf_temp_with_content (content, strlen(content));
284     rc = iobuf_push_filter (iobuf,
285                             content_filter, content_filter_new (content2));
286     assert (rc == 0);
287
288     n = 0;
289     while (1)
290       {
291         c = iobuf_readbyte (iobuf);
292         if (c == -1 && lastc == -1)
293           {
294             /* printf("Two EOFs in a row.  Done.\n");  */
295             assert (n == 27);
296             break;
297           }
298
299         lastc = c;
300
301         if (c == -1)
302           {
303             /* printf("After %d bytes, got EOF.\n", n); */
304             assert (n == 10 || n == 27);
305           }
306         else
307           {
308             n ++;
309             /* printf ("%d: '%c' (%d)\n", n, c, c); */
310           }
311       }
312   }
313
314   /* Write some data to a temporary filter.  Push a new filter.  The
315      already written data should not be processed by the new
316      filter.  */
317   {
318     iobuf_t iobuf;
319     int rc;
320     char *content = "0123456789";
321     char *content2 = "abc";
322     char buffer[4096];
323     int n;
324
325     iobuf = iobuf_temp ();
326     assert (iobuf);
327
328     rc = iobuf_write (iobuf, content, strlen (content));
329     assert (rc == 0);
330
331     rc = iobuf_push_filter (iobuf, double_filter, NULL);
332     assert (rc == 0);
333
334     /* Include a NUL.  */
335     rc = iobuf_write (iobuf, content2, strlen (content2) + 1);
336     assert (rc == 0);
337
338     n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer));
339 #if 0
340     printf ("Got %d bytes\n", n);
341     printf ("buffer: `");
342     fwrite (buffer, n, 1, stdout);
343     fputc ('\'', stdout);
344     fputc ('\n', stdout);
345 #endif
346
347     assert (n == strlen (content) + 2 * (strlen (content2) + 1));
348     assert (strcmp (buffer, "0123456789aabbcc") == 0);
349   }
350
351   {
352     iobuf_t iobuf;
353     int rc;
354     char *content = "0123456789";
355     int n;
356     int c;
357     char buffer[strlen (content)];
358
359     iobuf = iobuf_temp_with_content (content, strlen (content));
360     assert (iobuf);
361
362     rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
363     assert (rc == 0);
364     rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
365     assert (rc == 0);
366
367     for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++)
368       {
369         /* printf ("%d: `%c'\n", n, c);  */
370         buffer[n] = c;
371       }
372
373     assert (n == 2);
374     assert (buffer[0] == '3');
375     assert (buffer[1] == '7');
376   }
377
378   return 0;
379 }