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