common/iobuf.c: Make control flow more obvious.
[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       if (toread == 0)
115         return -1;
116
117       memcpy (buf, &state->buffer[state->pos], toread);
118
119       state->pos += toread;
120
121       *len = toread;
122
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     /* - 3 characters plus new line
273        - 4 characters plus new line
274        - 5 characters plus new line
275        - 5 characters, no new line
276      */
277     char *content = "abcdefghijklmnopq";
278     char *content2 = "0123456789";
279     iobuf_t iobuf;
280     int rc;
281     int c;
282     int n;
283     int lastc = 0;
284
285     iobuf = iobuf_temp_with_content (content, strlen(content));
286     rc = iobuf_push_filter (iobuf,
287                             content_filter, content_filter_new (content2));
288     assert (rc == 0);
289
290     n = 0;
291     while (1)
292       {
293         c = iobuf_readbyte (iobuf);
294         if (c == -1 && lastc == -1)
295           {
296             // printf("Two EOFs in a row.  Done.\n");
297             assert (n == 44);
298             break;
299           }
300
301         lastc = c;
302
303         if (c == -1)
304           {
305             // printf("After %d bytes, got EOF.\n", n);
306             assert (n == 27 || n == 44);
307           }
308         else
309           {
310             n ++;
311             // printf ("%d: '%c' (%d)\n", n, c, c);
312           }
313       }
314   }
315
316   /* Write some data to a temporary filter.  Push a new filter.  The
317      already written data should not be processed by the new
318      filter.  */
319   {
320     iobuf_t iobuf;
321     int rc;
322     char *content = "0123456789";
323     char *content2 = "abc";
324     char buffer[4096];
325     int n;
326
327     iobuf = iobuf_temp ();
328     assert (iobuf);
329
330     rc = iobuf_write (iobuf, content, strlen (content));
331     assert (rc == 0);
332
333     rc = iobuf_push_filter (iobuf, double_filter, NULL);
334     assert (rc == 0);
335
336     /* Include a NUL.  */
337     rc = iobuf_write (iobuf, content2, strlen (content2) + 1);
338     assert (rc == 0);
339
340     n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer));
341     printf ("Got %d bytes\n", n);
342     printf ("buffer: `");
343     fwrite (buffer, n, 1, stdout);
344     fputc ('\'', stdout);
345     fputc ('\n', stdout);
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 }