chiark / gitweb /
0e6f508a5b4a12b1df103e31d292f8ac6bfcdeea
[gnupg2.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     iobuf_close (iobuf);
195   }
196
197
198   /* A simple test to check that iobuf_read_line works.  */
199   {
200     /* - 3 characters plus new line
201        - 4 characters plus new line
202        - 5 characters plus new line
203        - 5 characters, no new line
204      */
205     char *content = "abc\ndefg\nhijkl\nmnopq";
206     iobuf_t iobuf;
207     byte *buffer;
208     unsigned size;
209     unsigned max_len;
210     int n;
211
212     iobuf = iobuf_temp_with_content (content, strlen(content));
213
214     /* We read a line with 3 characters plus a newline.  If we
215        allocate a buffer that is 5 bytes long, then no reallocation
216        should be required.  */
217     size = 5;
218     buffer = malloc (size);
219     assert (buffer);
220     max_len = 100;
221     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
222     assert (n == 4);
223     assert (strcmp (buffer, "abc\n") == 0);
224     assert (size == 5);
225     assert (max_len == 100);
226     free (buffer);
227
228     /* We now read a line with 4 characters plus a newline.  This
229        requires 6 bytes of storage.  We pass a buffer that is 5 bytes
230        large and we allow the buffer to be grown.  */
231     size = 5;
232     buffer = malloc (size);
233     max_len = 100;
234     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
235     assert (n == 5);
236     assert (strcmp (buffer, "defg\n") == 0);
237     assert (size >= 6);
238     /* The string shouldn't have been truncated (max_len == 0).  */
239     assert (max_len == 100);
240     free (buffer);
241
242     /* We now read a line with 5 characters plus a newline.  This
243        requires 7 bytes of storage.  We pass a buffer that is 5 bytes
244        large and we don't allow the buffer to be grown.  */
245     size = 5;
246     buffer = malloc (size);
247     max_len = 5;
248     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
249     assert (n == 4);
250     /* Note: the string should still have a trailing \n.  */
251     assert (strcmp (buffer, "hij\n") == 0);
252     assert (size == 5);
253     /* The string should have been truncated (max_len == 0).  */
254     assert (max_len == 0);
255     free (buffer);
256
257     /* We now read a line with 6 characters without a newline.  This
258        requires 7 bytes of storage.  We pass a NULL buffer and we
259        don't allow the buffer to be grown larger than 5 bytes.  */
260     size = 5;
261     buffer = NULL;
262     max_len = 5;
263     n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
264     assert (n == 4);
265     /* Note: the string should still have a trailing \n.  */
266     assert (strcmp (buffer, "mno\n") == 0);
267     assert (size == 5);
268     /* The string should have been truncated (max_len == 0).  */
269     assert (max_len == 0);
270     free (buffer);
271
272     iobuf_close (iobuf);
273   }
274
275   {
276     /* - 10 characters, EOF
277        - 17 characters, EOF
278      */
279     char *content = "abcdefghijklmnopq";
280     char *content2 = "0123456789";
281     iobuf_t iobuf;
282     int rc;
283     int c;
284     int n;
285     int lastc = 0;
286     struct content_filter_state *state;
287
288     iobuf = iobuf_temp_with_content (content, strlen(content));
289     rc = iobuf_push_filter (iobuf,
290                             content_filter,
291                             state=content_filter_new (content2));
292     assert (rc == 0);
293
294     n = 0;
295     while (1)
296       {
297         c = iobuf_readbyte (iobuf);
298         if (c == -1 && lastc == -1)
299           {
300             /* printf("Two EOFs in a row.  Done.\n");  */
301             assert (n == 27);
302             break;
303           }
304
305         lastc = c;
306
307         if (c == -1)
308           {
309             /* printf("After %d bytes, got EOF.\n", n); */
310             assert (n == 10 || n == 27);
311           }
312         else
313           {
314             n ++;
315             /* printf ("%d: '%c' (%d)\n", n, c, c); */
316           }
317       }
318
319     iobuf_close (iobuf);
320     free (state);
321   }
322
323   /* Write some data to a temporary filter.  Push a new filter.  The
324      already written data should not be processed by the new
325      filter.  */
326   {
327     iobuf_t iobuf;
328     int rc;
329     char *content = "0123456789";
330     char *content2 = "abc";
331     char buffer[4096];
332     int n;
333
334     iobuf = iobuf_temp ();
335     assert (iobuf);
336
337     rc = iobuf_write (iobuf, content, strlen (content));
338     assert (rc == 0);
339
340     rc = iobuf_push_filter (iobuf, double_filter, NULL);
341     assert (rc == 0);
342
343     /* Include a NUL.  */
344     rc = iobuf_write (iobuf, content2, strlen (content2) + 1);
345     assert (rc == 0);
346
347     n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer));
348 #if 0
349     printf ("Got %d bytes\n", n);
350     printf ("buffer: `");
351     fwrite (buffer, n, 1, stdout);
352     fputc ('\'', stdout);
353     fputc ('\n', stdout);
354 #endif
355
356     assert (n == strlen (content) + 2 * (strlen (content2) + 1));
357     assert (strcmp (buffer, "0123456789aabbcc") == 0);
358
359     iobuf_close (iobuf);
360   }
361
362   {
363     iobuf_t iobuf;
364     int rc;
365     char *content = "0123456789";
366     int n;
367     int c;
368     char buffer[strlen (content)];
369
370     iobuf = iobuf_temp_with_content (content, strlen (content));
371     assert (iobuf);
372
373     rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
374     assert (rc == 0);
375     rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
376     assert (rc == 0);
377
378     for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++)
379       {
380         /* printf ("%d: `%c'\n", n, c);  */
381         buffer[n] = c;
382       }
383
384     assert (n == 2);
385     assert (buffer[0] == '3');
386     assert (buffer[1] == '7');
387
388     iobuf_close (iobuf);
389   }
390
391   return 0;
392 }