Sicherung
[gnupg.git] / util / iobuf.c
1 /* iobuf.c  -  file handling
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *
4  * This file is part of G10.
5  *
6  * G10 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * G10 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "memory.h"
31 #include "util.h"
32 #include "iobuf.h"
33
34 typedef struct {
35     FILE *fp;      /* open file handle */
36     char fname[1]; /* name of the file */
37 } file_filter_ctx_t ;
38
39 typedef struct {
40     int usage;
41     size_t size;
42     size_t count;
43     int eof;
44 } block_filter_ctx_t;
45
46 static int underflow(IOBUF a);
47
48 /****************
49  * Read data from a file into buf which has an allocated length of *LEN.
50  * return the number of read bytes in *LEN. OPAQUE is the FILE * of
51  * the stream. A is not used.
52  * control maybe:
53  * IOBUFCTRL_INIT: called just before the function is linked into the
54  *                 list of function. This can be used to prepare internal
55  *                 data structures of the function.
56  * IOBUFCTRL_FREE: called just before the function is removed from the
57  *                  list of functions and can be used to release internal
58  *                  data structures or close a file etc.
59  * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer
60  *                  with new stuff. *RET_LEN is the available size of the
61  *                  buffer, and should be set to the number of bytes
62  *                  which were put into the buffer. The function
63  *                  returns 0 to indicate success, -1 on EOF and
64  *                  G10ERR_xxxxx for other errors.
65  *
66  * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff.
67  *                  *RET_LAN is the number of bytes in BUF.
68  *
69  */
70 static int
71 file_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
72 {
73     file_filter_ctx_t *a = opaque;
74     FILE *fp = a->fp;
75     size_t size = *ret_len;
76     size_t nbytes = 0;
77     int c, rc = 0;
78     char *p;
79
80     if( control == IOBUFCTRL_UNDERFLOW ) {
81         assert( size ); /* need a buffer */
82         for(; size; size-- ) {
83             if( (c=getc(fp)) == EOF ) {
84                 if( ferror(fp) ) {
85                     log_error("%s: read error: %s\n",
86                                         a->fname, strerror(errno));
87                     rc = G10ERR_READ_FILE;
88                 }
89                 else if( !nbytes )
90                     rc = -1; /* okay: we can return EOF now. */
91                 break;
92             }
93             buf[nbytes++] = c & 0xff;
94         }
95         *ret_len = nbytes;
96     }
97     else if( control == IOBUFCTRL_FLUSH ) {
98         for(p=buf; nbytes < size; nbytes++, p++ ) {
99             if( putc(*p, fp) == EOF ) {
100                 log_error("%s: write error: %s\n",
101                                     a->fname, strerror(errno));
102                 rc = G10ERR_WRITE_FILE;
103                 break;
104             }
105         }
106         *ret_len = nbytes;
107     }
108     else if( control == IOBUFCTRL_INIT ) {
109     }
110     else if( control == IOBUFCTRL_DESC ) {
111         *(char**)buf = "file_filter";
112     }
113     else if( control == IOBUFCTRL_FREE ) {
114         if( fp != stdin && fp != stdout )
115             fclose(fp);
116         fp = NULL;
117         m_free(a); /* we can free our context now */
118     }
119
120     return rc;
121 }
122
123
124 /****************
125  * This is used to implement the block write mode.
126  * Block reading is done on a byte by byte basis in readbyte(),
127  * without a filter
128  */
129 static int
130 block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
131 {
132     block_filter_ctx_t *a = opaque;
133     size_t size = *ret_len;
134     int c, rc = 0;
135     char *p;
136
137     if( control == IOBUFCTRL_UNDERFLOW ) {
138         size_t n=0;
139
140         p = buf;
141         assert( size ); /* need a buffer */
142         if( a->eof ) /* don't read any further */
143             rc = -1;
144         while( !rc && size ) {
145             if( !a->size ) { /* get the length bytes */
146                 c = iobuf_get(chain);
147                 a->size = c << 8;
148                 c = iobuf_get(chain);
149                 a->size |= c;
150                 if( c == -1 ) {
151                     log_error("block_filter: error reading length info\n");
152                     rc = G10ERR_READ_FILE;
153                 }
154                 if( !a->size ) {
155                     a->eof = 1;
156                     if( !n )
157                         rc = -1;
158                     break;
159                 }
160             }
161
162             for(; !rc && size && a->size; size--, a->size-- ) {
163                 if( (c=iobuf_get(chain)) == -1 ) {
164                     log_error("block_filter %p: read error (size=%lu,a->size=%lu)\n",
165                                 a,  (ulong)size, (ulong)a->size);
166                     rc = G10ERR_READ_FILE;
167                 }
168                 else {
169                     *p++ = c;
170                     n++;
171                 }
172             }
173         }
174         *ret_len = n;
175     }
176     else if( control == IOBUFCTRL_FLUSH ) {
177         size_t avail, n;
178
179         for(p=buf; !rc && size; ) {
180             n = size;
181             avail = a->size - a->count;
182             if( !avail ) {
183                 if( n > a->size ) {
184                     iobuf_put( chain, (a->size >> 8) & 0xff );
185                     iobuf_put( chain, a->size & 0xff );
186                     avail = a->size;
187                     a->count = 0;
188                 }
189                 else {
190                     iobuf_put( chain, (n >> 8) & 0xff );
191                     iobuf_put( chain, n & 0xff );
192                     avail = n;
193                     a->count = a->size - n;
194                 }
195             }
196             if( n > avail )
197                 n = avail;
198             if( iobuf_write(chain, p, n ) )
199                 rc = G10ERR_WRITE_FILE;
200             a->count += n;
201             p += n;
202             size -= n;
203         }
204     }
205     else if( control == IOBUFCTRL_INIT ) {
206         if( DBG_IOBUF )
207             log_debug("init block_filter %p\n", a );
208         if( a->usage == 1 )
209             a->count = a->size = 0;
210         else
211             a->count = a->size; /* force first length bytes */
212         a->eof = 0;
213     }
214     else if( control == IOBUFCTRL_DESC ) {
215         *(char**)buf = "block_filter";
216     }
217     else if( control == IOBUFCTRL_FREE ) {
218         if( a->usage == 2 ) { /* write the end markers */
219             iobuf_writebyte(chain, 0);
220             iobuf_writebyte(chain, 0);
221         }
222         else if( a->size ) {
223             log_error("block_filter: pending bytes!\n");
224         }
225         if( DBG_IOBUF )
226             log_debug("free block_filter %p\n", a );
227         m_free(a); /* we can free our context now */
228     }
229
230     return rc;
231 }
232
233
234
235 /****************
236  * Allocate a new io buffer, with no function assigned.
237  * Usage is the desired usage: 1 for input, 2 for output, 3 for temp buffer
238  * BUFSIZE is a suggested buffer size.
239  */
240 IOBUF
241 iobuf_alloc(int usage, size_t bufsize)
242 {
243     IOBUF a;
244     static int number=0;
245
246     a = m_alloc_clear(sizeof *a);
247     a->usage = usage;
248     a->d.buf = m_alloc( bufsize );
249     a->d.size = bufsize;
250     a->no = ++number;
251     a->subno = 0;
252     a->opaque = NULL;
253     return a;
254 }
255
256
257 int
258 iobuf_close( IOBUF a )
259 {
260     IOBUF a2;
261     size_t dummy_len;
262     int rc=0;
263
264     for( ; a; a = a2 ) {
265         a2 = a->chain;
266         if( a->usage == 2 && (rc=iobuf_flush(a)) )
267             log_error("iobuf_flush failed on close: %s\n", g10_errstr(rc));
268
269         if( DBG_IOBUF )
270             log_debug("iobuf-%d.%d: close '%s'\n", a->no, a->subno, a->desc );
271         if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE,
272                                          a->chain, NULL, &dummy_len)) )
273             log_error("IOBUFCTRL_FREE failed on close: %s\n", g10_errstr(rc) );
274         m_free(a->recorder.buf);
275         m_free(a->d.buf);
276         m_free(a);
277     }
278     return rc;
279 }
280
281 int
282 iobuf_cancel( IOBUF a )
283 {
284     const char *s;
285
286     if( a && a->usage == 2 ) {
287         s = iobuf_get_fname(a);
288         if( s && *s )
289             remove(s);  /* remove the file. Fixme: this will fail for MSDOZE*/
290     }                   /* because the file is still open */
291     return iobuf_close(a);
292 }
293
294
295 /****************
296  * create a temporary iobuf, which can be used to collect stuff
297  * in an iobuf and later be written by iobuf_write_temp() to another
298  * iobuf.
299  */
300 IOBUF
301 iobuf_temp()
302 {
303     IOBUF a;
304
305     a = iobuf_alloc(3, 8192 );
306
307     return a;
308 }
309
310
311 /****************
312  * Create a head iobuf for reading from a file
313  * returns: NULL if an error occures and sets errno
314  */
315 IOBUF
316 iobuf_open( const char *fname )
317 {
318     IOBUF a;
319     FILE *fp;
320     file_filter_ctx_t *fcx;
321     size_t len;
322
323     if( !fname ) {
324         fp = stdin; /* fixme: set binary mode for msdoze */
325         fname = "[stdin]";
326     }
327     else if( !(fp = fopen(fname, "rb")) )
328         return NULL;
329     a = iobuf_alloc(1, 8192 );
330     fcx = m_alloc( sizeof *fcx + strlen(fname) );
331     fcx->fp = fp;
332     strcpy(fcx->fname, fname );
333     a->filter = file_filter;
334     a->filter_ov = fcx;
335     file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
336     file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
337     if( DBG_IOBUF )
338         log_debug("iobuf-%d.%d: open '%s'\n", a->no, a->subno, fname );
339
340     return a;
341 }
342
343 /****************
344  * create a iobuf for writing to a file; the file will be created.
345  */
346 IOBUF
347 iobuf_create( const char *fname )
348 {
349     IOBUF a;
350     FILE *fp;
351     file_filter_ctx_t *fcx;
352     size_t len;
353
354     if( !fname ) {
355         fp = stdout;
356         fname = "[stdout]";
357     }
358     else if( !(fp = fopen(fname, "wb")) )
359         return NULL;
360     a = iobuf_alloc(2, 8192 );
361     fcx = m_alloc( sizeof *fcx + strlen(fname) );
362     fcx->fp = fp;
363     strcpy(fcx->fname, fname );
364     a->filter = file_filter;
365     a->filter_ov = fcx;
366     file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
367     file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
368     if( DBG_IOBUF )
369         log_debug("iobuf-%d.%d: create '%s'\n", a->no, a->subno, a->desc );
370
371     return a;
372 }
373
374 /****************
375  * Register an i/o filter.
376  */
377 int
378 iobuf_push_filter( IOBUF a,
379                    int (*f)(void *opaque, int control,
380                    IOBUF chain, byte *buf, size_t *len), void *ov )
381 {
382     IOBUF b;
383     size_t dummy_len=0;
384     int rc=0;
385
386     if( a->usage == 2 && (rc=iobuf_flush(a)) )
387         return rc;
388     /* make a copy of the current stream, so that
389      * A is the new stream and B the original one.
390      * The contents of the buffers are transferred to the
391      * new stream.
392      */
393     b = m_alloc(sizeof *b);
394     memcpy(b, a, sizeof *b );
395     /* remove the filter stuff from the new stream */
396     a->filter = NULL;
397     a->filter_ov = NULL;
398     if( a->usage == 2 ) { /* allocate a fresh buffer for the original stream */
399         b->d.buf = m_alloc( a->d.size );
400         b->d.len = 0;
401         b->d.start = 0;
402     }
403     else { /* allocate a fresh buffer for the new stream */
404         a->d.buf = m_alloc( a->d.size );
405         a->d.len = 0;
406         a->d.start = 0;
407     }
408     /* disable nlimit for the new stream */
409     a->nlimit = a->nbytes = 0;
410     /* disable recorder for the original stream */
411     b->recorder.buf = NULL;
412     /* make a link from the new stream to the original stream */
413     a->chain = b;
414     a->opaque = b->opaque;
415
416     /* setup the function on the new stream */
417     a->filter = f;
418     a->filter_ov = ov;
419
420     a->subno = b->subno + 1;
421     f( ov, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &dummy_len );
422
423     if( DBG_IOBUF ) {
424         log_debug("iobuf-%d.%d: push '%s'\n", a->no, a->subno, a->desc );
425         for(b=a; b; b = b->chain )
426             log_debug("\tchain: %d.%d '%s'\n", b->no, b->subno, b->desc );
427     }
428
429     /* now we can initialize the new function if we have one */
430     if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_INIT, a->chain,
431                        NULL, &dummy_len)) )
432         log_error("IOBUFCTRL_INIT failed: %s\n", g10_errstr(rc) );
433     return rc;
434 }
435
436 /****************
437  * Remove an i/o filter.
438  */
439 int
440 iobuf_pop_filter( IOBUF a, int (*f)(void *opaque, int control,
441                       IOBUF chain, byte *buf, size_t *len), void *ov )
442 {
443     IOBUF b;
444     size_t dummy_len=0;
445     int rc=0;
446
447     if( DBG_IOBUF )
448         log_debug("iobuf-%d.%d: pop '%s'\n", a->no, a->subno, a->desc );
449     if( !a->filter ) { /* this is simple */
450         b = a->chain;
451         assert(b);
452         m_free(a->d.buf);
453         memcpy(a,b, sizeof *a);
454         m_free(b);
455         return 0;
456     }
457     for(b=a ; b; b = b->chain )
458         if( b->filter == f && (!ov || b->filter_ov == ov) )
459             break;
460     if( !b )
461         log_bug("iobuf_pop_filter(): filter function not found\n");
462
463     /* flush this stream if it is an output stream */
464     if( a->usage == 2 && (rc=iobuf_flush(b)) ) {
465         log_error("iobuf_flush failed in pop_filter: %s\n", g10_errstr(rc));
466         return rc;
467     }
468     /* and tell the filter to free it self */
469     if( (rc = b->filter(b->filter_ov, IOBUFCTRL_FREE, b->chain,
470                        NULL, &dummy_len)) ) {
471         log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
472         return rc;
473     }
474
475     /* and look how to remove it */
476     if( a == b && !b->chain )
477         log_bug("can't remove the last filter from the chain\n");
478     else if( a == b ) { /* remove the first iobuf from the chain */
479         /* everything from b is copied to a. This is save because
480          * a flush has been done on the to be removed entry
481          */
482         b = a->chain;
483         m_free(a->d.buf);
484         memcpy(a,b, sizeof *a);
485         m_free(b);
486     }
487     else if( !b->chain ) { /* remove the last iobuf from the chain */
488         log_bug("Ohh jeee, trying to a head filter\n");
489     }
490     else {  /* remove an intermediate iobuf from the chain */
491         log_bug("Ohh jeee, trying to remove an intermediate filter\n");
492     }
493
494     return rc;
495 }
496
497
498
499 /****************
500  * read underflow: read more bytes into the buffer and return
501  * the first byte or -1 on EOF.
502  */
503 static int
504 underflow(IOBUF a)
505 {
506     size_t len;
507     int rc;
508
509   /*log_debug("iobuf-%d.%d: underflow: start=%lu len=%lu\n",
510                 a->no, a->subno, (ulong)a->d.start, (ulong)a->d.len );*/
511     assert( a->d.start == a->d.len );
512     if( a->usage == 3 )
513         return -1; /* EOF because a temp buffer can't do an underflow */
514     if( a->filter_eof ) {
515         if( DBG_IOBUF )
516             log_debug("iobuf-%d.%d: filter eof\n", a->no, a->subno );
517         return -1;
518     }
519
520     if( a->filter ) {
521         len = a->d.size;
522         rc = a->filter( a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
523                         a->d.buf, &len );
524         if( a->usage == 1 && rc == -1 ) { /* EOF: we can remove the filter */
525             size_t dummy_len;
526
527             /* and tell the filter to free it self */
528             if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain,
529                                NULL, &dummy_len)) )
530                 log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
531             a->filter = NULL;
532             a->desc = NULL;
533             a->filter_ov = NULL;
534             a->filter_eof = 1;
535         }
536
537         if( !len )
538             return -1;
539         a->d.len = len;
540         a->d.start = 0;
541         return a->d.buf[a->d.start++];
542     }
543     else
544         return -1;  /* no filter; return EOF */
545 }
546
547
548 void
549 iobuf_clear_eof(IOBUF a)
550 {
551     assert(a->usage == 1);
552
553     if( a->filter )
554         log_info("iobuf-%d.%d: clear_eof '%s' with enabled filter\n", a->no, a->subno, a->desc );
555     if( !a->filter_eof )
556         log_info("iobuf-%d.%d: clear_eof '%s' with no EOF pending\n", a->no, a->subno, a->desc );
557     iobuf_pop_filter(a, NULL, NULL);
558 }
559
560
561 int
562 iobuf_flush(IOBUF a)
563 {
564     size_t len;
565     int rc;
566
567     /*log_debug("iobuf-%d.%d: flush\n", a->no, a->subno );*/
568     if( a->usage == 3 )
569         log_bug("temp buffer too short\n");
570     else if( a->usage != 2 )
571         log_bug("flush on non-output iobuf\n");
572     else if( !a->filter )
573         log_bug("iobuf_flush: no filter\n");
574     len = a->d.len;
575     rc = a->filter( a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len );
576     if( !rc && len != a->d.len ) {
577         log_info("iobuf_flush did not write all!\n");
578         rc = G10ERR_WRITE_FILE;
579     }
580     a->d.len = 0;
581
582     return rc;
583 }
584
585
586 /****************
587  * Read a byte from the iobuf; returns -1 on EOF
588  */
589 int
590 iobuf_readbyte(IOBUF a)
591 {
592     int c;
593
594     if( a->nlimit && a->nbytes >= a->nlimit )
595         return -1; /* forced EOF */
596
597     if( a->d.start < a->d.len ) {
598         c = a->d.buf[a->d.start++];
599     }
600     else if( (c=underflow(a)) == -1 )
601         return -1; /* EOF */
602
603     a->nbytes++;
604
605     if( a->recorder.buf ) {
606         if( a->recorder.len >= a->recorder.size ) {
607             a->recorder.size += 500;
608             a->recorder.buf = m_realloc( a->recorder.buf, a->recorder.size );
609         }
610         ((byte*)a->recorder.buf)[a->recorder.len++] = c;
611     }
612     return c;
613 }
614
615
616 int
617 iobuf_writebyte(IOBUF a, unsigned c)
618 {
619     if( a->d.len == a->d.size )
620         if( iobuf_flush(a) )
621             return -1;
622
623     assert( a->d.len < a->d.size );
624     a->d.buf[a->d.len++] = c;
625     return 0;
626 }
627
628
629 int
630 iobuf_write(IOBUF a, byte *buf, unsigned buflen )
631 {
632     for( ; buflen; buflen--, buf++ )
633         if( iobuf_writebyte(a, *buf) )
634             return -1;
635     return 0;
636 }
637
638 int
639 iobuf_writestr(IOBUF a, const char *buf )
640 {
641     for( ; *buf; buf++ )
642         if( iobuf_writebyte(a, *buf) )
643             return -1;
644     return 0;
645 }
646
647
648
649 /****************
650  * copy the contents of TEMP to A.
651  */
652 int
653 iobuf_write_temp( IOBUF a, IOBUF temp )
654 {
655     return iobuf_write(a, temp->d.buf, temp->d.len );
656 }
657
658 /****************
659  * copy the contents of the temp io stream to BUFFER.
660  */
661 size_t
662 iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen )
663 {
664     size_t n = a->d.len;
665
666     if( n > buflen )
667         n = buflen;
668     memcpy( buffer, a->d.buf, n );
669     return n;
670 }
671
672
673 /****************
674  * Set a limit, how much bytes may be read from the input stream A.
675  * Setting the limit to 0 disables this feature.
676  */
677 void
678 iobuf_set_limit( IOBUF a, unsigned long nlimit )
679 {
680     a->nlimit = nlimit;
681     a->nbytes = 0;
682 }
683
684
685
686 void
687 iobuf_start_recorder( IOBUF a )
688 {
689     m_free(a->recorder.buf);
690     a->recorder.size = 500;
691     a->recorder.buf = m_alloc(a->recorder.size);
692     a->recorder.len = 0;
693 }
694
695 void
696 iobuf_push_recorder( IOBUF a, int c )
697 {
698     if( a->recorder.buf ) {
699         if( a->recorder.len >= a->recorder.size ) {
700             a->recorder.size += 500;
701             a->recorder.buf = m_realloc( a->recorder.buf, a->recorder.size );
702         }
703         ((byte*)a->recorder.buf)[a->recorder.len++] = c;
704     }
705 }
706
707
708 char *
709 iobuf_stop_recorder( IOBUF a, size_t *n )
710 {
711     char *p;
712     if( !a->recorder.buf )
713         log_bug("iobuf_recorder not started\n");
714     p = a->recorder.buf;
715     if( n )
716         *n = a->recorder.len;
717     a->recorder.buf = NULL;
718     return p;
719 }
720
721
722 /****************
723  * Return the length of an open file
724  */
725 u32
726 iobuf_get_filelength( IOBUF a )
727 {
728     struct stat st;
729
730     for( ; a; a = a->chain )
731         if( !a->chain && a->filter == file_filter ) {
732             file_filter_ctx_t *b = a->filter_ov;
733             FILE *fp = b->fp;
734
735             if( !fstat(fileno(fp), &st) )
736                 return st.st_size;
737             log_error("fstat() failed: %s\n", strerror(errno) );
738             break;
739         }
740
741     return 0;
742 }
743
744 /****************
745  * Retrieve the filename
746  */
747 const char *
748 iobuf_get_fname( IOBUF a )
749 {
750     struct stat st;
751
752     for( ; a; a = a->chain )
753         if( !a->chain && a->filter == file_filter ) {
754             file_filter_ctx_t *b = a->filter_ov;
755             return b->fname;
756         }
757
758     return NULL;
759 }
760
761 /****************
762  * Start the block write mode, see rfc1991.new for details.
763  * A value of 0 for N stops this mode (flushes and writes
764  * the end marker)
765  */
766 void
767 iobuf_set_block_mode( IOBUF a, size_t n )
768 {
769     block_filter_ctx_t *ctx = m_alloc_clear( sizeof *ctx );
770
771     assert( a->usage == 1 || a->usage == 2 );
772     ctx->usage = a->usage;
773     if( !n ) {
774         iobuf_pop_filter(a, block_filter, NULL );
775     }
776     else {
777         ctx->size = n; /* only needed for usage 2 */
778         iobuf_push_filter(a, block_filter, ctx );
779     }
780 }
781
782
783 /****************
784  * checks wether the stream is in block mode
785  */
786 int
787 iobuf_in_block_mode( IOBUF a )
788 {
789     for(; a; a = a->chain )
790         if( a->filter == block_filter )
791             return 1; /* yes */
792     return 0; /* no */
793 }
794
795
796