~ubuntu-branches/ubuntu/utopic/nss-pam-ldapd/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/01-use-poll-instead-of-select.patch/common/tio.c

  • Committer: Package Import Robot
  • Author(s): Arthur de Jong
  • Date: 2012-10-14 23:00:00 UTC
  • Revision ID: package-import@ubuntu.com-20121014230000-tn63bv35wlebv45a
Tags: 0.8.10-3
* fix a problem in sed logic for commenting out disabled options
  (closes: #689296)
* support "EXTERNAL" SASL mechanism in debconf configuration (LP: #1063923)
  (the debconf template has been postponed to avoid having to update all
  translations for a relatively minor change)
* 01-use-poll-instead-of-select.patch: use poll() instead of select()
  for checking file descriptor activity to also correctly work if more
  than FD_SETSIZE files are already open (closes: #690319)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   tio.c - timed io functions
 
3
   This file is part of the nss-pam-ldapd library.
 
4
 
 
5
   Copyright (C) 2007, 2008, 2010, 2011, 2012 Arthur de Jong
 
6
 
 
7
   This library is free software; you can redistribute it and/or
 
8
   modify it under the terms of the GNU Lesser General Public
 
9
   License as published by the Free Software Foundation; either
 
10
   version 2.1 of the License, or (at your option) any later version.
 
11
 
 
12
   This library is distributed in the hope that it will be useful,
 
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
   Lesser General Public License for more details.
 
16
 
 
17
   You should have received a copy of the GNU Lesser General Public
 
18
   License along with this library; if not, write to the Free Software
 
19
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
20
   02110-1301 USA
 
21
*/
 
22
 
 
23
#include "config.h"
 
24
 
 
25
#ifdef HAVE_STDINT_H
 
26
#include <stdint.h>
 
27
#endif /* HAVE_STDINT_H */
 
28
#include <stdlib.h>
 
29
#include <unistd.h>
 
30
#include <sys/time.h>
 
31
#include <sys/types.h>
 
32
#include <sys/socket.h>
 
33
#include <errno.h>
 
34
#include <string.h>
 
35
#include <signal.h>
 
36
#include <stdio.h>
 
37
#include <limits.h>
 
38
 
 
39
#include "tio.h"
 
40
 
 
41
/* for platforms that don't have ETIME use ETIMEDOUT */
 
42
#ifndef ETIME
 
43
#define ETIME ETIMEDOUT
 
44
#endif /* ETIME */
 
45
 
 
46
/* structure that holds a buffer
 
47
   the buffer contains the data that is between the application and the
 
48
   file descriptor that is used for efficient transfer
 
49
   the buffer is built up as follows:
 
50
   |.....********......|
 
51
         ^start        ^size
 
52
         ^--len--^           */
 
53
struct tio_buffer {
 
54
  uint8_t *buffer;
 
55
  size_t size;      /* the size of the buffer */
 
56
  size_t maxsize;   /* the maximum size of the buffer */
 
57
  size_t start;     /* the start of the data (before start is unused) */
 
58
  size_t len;       /* size of the data (from the start) */
 
59
};
 
60
 
 
61
/* structure that holds all the state for files */
 
62
struct tio_fileinfo {
 
63
  int fd;
 
64
  struct tio_buffer readbuffer;
 
65
  struct tio_buffer writebuffer;
 
66
  struct timeval readtimeout;
 
67
  struct timeval writetimeout;
 
68
  int read_resettable; /* whether the tio_reset() function can be called */
 
69
#ifdef DEBUG_TIO_STATS
 
70
  /* this is used to collect statistics on the use of the streams
 
71
     and can be used to tune the buffer sizes */
 
72
  size_t byteswritten;
 
73
  size_t bytesread;
 
74
#endif /* DEBUG_TIO_STATS */
 
75
};
 
76
 
 
77
/* add the second timeval to the first modifing the first */
 
78
static inline void tio_tv_add(struct timeval *tv1, const struct timeval *tv2)
 
79
{
 
80
  /* BUG: we hope that this does not overflow */
 
81
  tv1->tv_usec+=tv2->tv_usec;
 
82
  if (tv1->tv_usec>=1000000)
 
83
  {
 
84
    tv1->tv_usec-=1000000;
 
85
    tv1->tv_sec+=1;
 
86
  }
 
87
  tv1->tv_sec+=tv2->tv_sec;
 
88
}
 
89
 
 
90
/* build a timeval for comparison to when the operation should be finished */
 
91
static inline void tio_tv_prepare(struct timeval *deadline, const struct timeval *timeout)
 
92
{
 
93
  if (gettimeofday(deadline,NULL))
 
94
  {
 
95
    /* just blank it in case of errors */
 
96
    deadline->tv_sec=0;
 
97
    deadline->tv_usec=0;
 
98
    return;
 
99
  }
 
100
  tio_tv_add(deadline,timeout);
 
101
}
 
102
 
 
103
/* update the timeval to the value that is remaining before deadline
 
104
   returns non-zero if there is no more time before the deadline */
 
105
static inline int tio_tv_remaining(struct timeval *tv, const struct timeval *deadline)
 
106
{
 
107
  /* get the current time */
 
108
  if (gettimeofday(tv,NULL))
 
109
  {
 
110
    /* 1 second default if gettimeofday() is broken */
 
111
    tv->tv_sec=1;
 
112
    tv->tv_usec=0;
 
113
    return 0;
 
114
  }
 
115
  /* check if we're too late */
 
116
  if ( (tv->tv_sec>deadline->tv_sec) ||
 
117
       ( (tv->tv_sec==deadline->tv_sec) && (tv->tv_usec>deadline->tv_usec) ) )
 
118
    return -1;
 
119
  /* update tv */
 
120
  tv->tv_sec=deadline->tv_sec-tv->tv_sec;
 
121
  if (tv->tv_usec<=deadline->tv_usec)
 
122
    tv->tv_usec=deadline->tv_usec-tv->tv_usec;
 
123
  else
 
124
  {
 
125
    tv->tv_sec--;
 
126
    tv->tv_usec=1000000+deadline->tv_usec-tv->tv_usec;
 
127
  }
 
128
  return 0;
 
129
}
 
130
 
 
131
/* open a new TFILE based on the file descriptor */
 
132
TFILE *tio_fdopen(int fd,struct timeval *readtimeout,struct timeval *writetimeout,
 
133
                  size_t initreadsize,size_t maxreadsize,
 
134
                  size_t initwritesize,size_t maxwritesize)
 
135
{
 
136
  struct tio_fileinfo *fp;
 
137
  fp=(struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
 
138
  if (fp==NULL)
 
139
    return NULL;
 
140
  fp->fd=fd;
 
141
  /* initialize read buffer */
 
142
  fp->readbuffer.buffer=(uint8_t *)malloc(initreadsize);
 
143
  if (fp->readbuffer.buffer==NULL)
 
144
  {
 
145
    free(fp);
 
146
    return NULL;
 
147
  }
 
148
  fp->readbuffer.size=initreadsize;
 
149
  fp->readbuffer.maxsize=maxreadsize;
 
150
  fp->readbuffer.start=0;
 
151
  fp->readbuffer.len=0;
 
152
  /* initialize write buffer */
 
153
  fp->writebuffer.buffer=(uint8_t *)malloc(initwritesize);
 
154
  if (fp->writebuffer.buffer==NULL)
 
155
  {
 
156
    free(fp->readbuffer.buffer);
 
157
    free(fp);
 
158
    return NULL;
 
159
  }
 
160
  fp->writebuffer.size=initwritesize;
 
161
  fp->writebuffer.maxsize=maxwritesize;
 
162
  fp->writebuffer.start=0;
 
163
  fp->writebuffer.len=0;
 
164
  /* initialize other attributes */
 
165
  fp->readtimeout.tv_sec=readtimeout->tv_sec;
 
166
  fp->readtimeout.tv_usec=readtimeout->tv_usec;
 
167
  fp->writetimeout.tv_sec=writetimeout->tv_sec;
 
168
  fp->writetimeout.tv_usec=writetimeout->tv_usec;
 
169
  fp->read_resettable=0;
 
170
#ifdef DEBUG_TIO_STATS
 
171
  fp->byteswritten=0;
 
172
  fp->bytesread=0;
 
173
#endif /* DEBUG_TIO_STATS */
 
174
  return fp;
 
175
}
 
176
 
 
177
/* wait for any activity on the specified file descriptor using
 
178
   the specified deadline */
 
179
static int tio_select(TFILE *fp, int readfd, const struct timeval *deadline)
 
180
{
 
181
  struct timeval tv;
 
182
  fd_set fdset;
 
183
  int rv;
 
184
  while (1)
 
185
  {
 
186
    /* prepare our filedescriptorset */
 
187
    FD_ZERO(&fdset);
 
188
    FD_SET(fp->fd,&fdset);
 
189
    /* figure out the time we need to wait */
 
190
    if (tio_tv_remaining(&tv,deadline))
 
191
    {
 
192
      errno=ETIME;
 
193
      return -1;
 
194
    }
 
195
    /* wait for activity */
 
196
    if (readfd)
 
197
    {
 
198
      /* santiy check for moving clock */
 
199
      if (tv.tv_sec>fp->readtimeout.tv_sec)
 
200
        tv.tv_sec=fp->readtimeout.tv_sec;
 
201
      rv=select(FD_SETSIZE,&fdset,NULL,NULL,&tv);
 
202
    }
 
203
    else
 
204
    {
 
205
      /* santiy check for moving clock */
 
206
      if (tv.tv_sec>fp->writetimeout.tv_sec)
 
207
        tv.tv_sec=fp->writetimeout.tv_sec;
 
208
      rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv);
 
209
    }
 
210
    if (rv>0)
 
211
      return 0; /* we have activity */
 
212
    else if (rv==0)
 
213
    {
 
214
      /* no file descriptors were available within the specified time */
 
215
      errno=ETIME;
 
216
      return -1;
 
217
    }
 
218
    else if (errno!=EINTR)
 
219
      /* some error ocurred */
 
220
      return -1;
 
221
    /* we just try again on EINTR */
 
222
  }
 
223
}
 
224
 
 
225
/* do a read on the file descriptor, returning the data in the buffer
 
226
   if no data was read in the specified time an error is returned */
 
227
int tio_read(TFILE *fp, void *buf, size_t count)
 
228
{
 
229
  struct timeval deadline;
 
230
  int rv;
 
231
  uint8_t *tmp;
 
232
  size_t newsz;
 
233
  size_t len;
 
234
  /* have a more convenient storage type for the buffer */
 
235
  uint8_t *ptr=(uint8_t *)buf;
 
236
  /* build a time by which we should be finished */
 
237
  tio_tv_prepare(&deadline,&(fp->readtimeout));
 
238
  /* loop until we have returned all the needed data */
 
239
  while (1)
 
240
  {
 
241
    /* check if we have enough data in the buffer */
 
242
    if (fp->readbuffer.len >= count)
 
243
    {
 
244
      if (count>0)
 
245
      {
 
246
        if (ptr!=NULL)
 
247
          memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,count);
 
248
        /* adjust buffer position */
 
249
        fp->readbuffer.start+=count;
 
250
        fp->readbuffer.len-=count;
 
251
      }
 
252
      return 0;
 
253
    }
 
254
    /* empty what we have and continue from there */
 
255
    if (fp->readbuffer.len>0)
 
256
    {
 
257
      if (ptr!=NULL)
 
258
      {
 
259
        memcpy(ptr,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
 
260
        ptr+=fp->readbuffer.len;
 
261
      }
 
262
      count-=fp->readbuffer.len;
 
263
      fp->readbuffer.start+=fp->readbuffer.len;
 
264
      fp->readbuffer.len=0;
 
265
    }
 
266
    /* after this point until the read fp->readbuffer.len is 0 */
 
267
    if (!fp->read_resettable)
 
268
    {
 
269
      /* the stream is not resettable, re-use the buffer */
 
270
      fp->readbuffer.start=0;
 
271
    }
 
272
    else if (fp->readbuffer.start>=(fp->readbuffer.size-4))
 
273
    {
 
274
      /* buffer is running empty, try to grow buffer */
 
275
      if (fp->readbuffer.size<fp->readbuffer.maxsize)
 
276
      {
 
277
        newsz=fp->readbuffer.size*2;
 
278
        if (newsz>fp->readbuffer.maxsize)
 
279
          newsz=fp->readbuffer.maxsize;
 
280
        tmp=realloc(fp->readbuffer.buffer,newsz);
 
281
        if (tmp!=NULL)
 
282
        {
 
283
          fp->readbuffer.buffer=tmp;
 
284
          fp->readbuffer.size=newsz;
 
285
        }
 
286
      }
 
287
      /* if buffer still does not contain enough room, clear resettable */
 
288
      if (fp->readbuffer.start>=(fp->readbuffer.size-4))
 
289
      {
 
290
        fp->readbuffer.start=0;
 
291
        fp->read_resettable=0;
 
292
      }
 
293
    }
 
294
    /* wait until we have input */
 
295
    if (tio_select(fp,1,&deadline))
 
296
      return -1;
 
297
    /* read the input in the buffer */
 
298
    len=fp->readbuffer.size-fp->readbuffer.start;
 
299
#ifdef SSIZE_MAX
 
300
    if (len>SSIZE_MAX)
 
301
      len=SSIZE_MAX;
 
302
#endif /* SSIZE_MAX */
 
303
    rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,len);
 
304
    /* check for errors */
 
305
    if (rv==0)
 
306
    {
 
307
      errno=ECONNRESET;
 
308
      return -1;
 
309
    }
 
310
    else if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
 
311
      return -1; /* something went wrong with the read */
 
312
    /* skip the read part in the buffer */
 
313
    fp->readbuffer.len=rv;
 
314
#ifdef DEBUG_TIO_STATS
 
315
    fp->bytesread+=rv;
 
316
#endif /* DEBUG_TIO_STATS */
 
317
  }
 
318
}
 
319
 
 
320
/* Read and discard the specified number of bytes from the stream. */
 
321
int tio_skip(TFILE *fp, size_t count)
 
322
{
 
323
  return tio_read(fp,NULL,count);
 
324
}
 
325
 
 
326
/* Read all available data from the stream and empty the read buffer. */
 
327
int tio_skipall(TFILE *fp)
 
328
{
 
329
  struct timeval tv;
 
330
  fd_set fdset;
 
331
  int rv;
 
332
  size_t len;
 
333
  /* clear the read buffer */
 
334
  fp->readbuffer.start=0;
 
335
  fp->readbuffer.len=0;
 
336
  fp->read_resettable=0;
 
337
  /* read until we can't read no more */
 
338
  len=fp->readbuffer.size;
 
339
#ifdef SSIZE_MAX
 
340
  if (len>SSIZE_MAX)
 
341
    len=SSIZE_MAX;
 
342
#endif /* SSIZE_MAX */
 
343
  while (1)
 
344
  {
 
345
    /* prepare our file descriptor set */
 
346
    FD_ZERO(&fdset);
 
347
    FD_SET(fp->fd,&fdset);
 
348
    /* prepare the time to wait */
 
349
    tv.tv_sec=0;
 
350
    tv.tv_usec=0;
 
351
    /* see if any data is available */
 
352
    rv=select(FD_SETSIZE,&fdset,NULL,NULL,&tv);
 
353
    if (rv==0)
 
354
      return 0; /* no file descriptor ready */
 
355
    if ((rv<0)&&((errno==EINTR)||(errno==EAGAIN)))
 
356
      continue; /* interrupted, try again */
 
357
    if (rv<0)
 
358
      return -1; /* something went wrong */
 
359
    /* read data from the stream */
 
360
    rv=read(fp->fd,fp->readbuffer.buffer,len);
 
361
    if (rv==0)
 
362
      return 0; /* end-of-file */
 
363
    if ((rv<0)&&(errno==EWOULDBLOCK))
 
364
      return 0; /* we've ready everything we can without blocking */
 
365
    if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))
 
366
      return -1; /* something went wrong with the read */
 
367
  }
 
368
}
 
369
 
 
370
/* the caller has assured us that we can write to the file descriptor
 
371
   and we give it a shot */
 
372
static int tio_writebuf(TFILE *fp)
 
373
{
 
374
  int rv;
 
375
  /* write the buffer */
 
376
#ifdef MSG_NOSIGNAL
 
377
  rv=send(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len,MSG_NOSIGNAL);
 
378
#else /* not MSG_NOSIGNAL */
 
379
  /* on platforms that cannot use send() with masked signals, we change the
 
380
     signal mask and change it back after the write (note that there is a
 
381
     race condition here) */
 
382
  struct sigaction act,oldact;
 
383
  /* set up sigaction */
 
384
  memset(&act,0,sizeof(struct sigaction));
 
385
  act.sa_sigaction=NULL;
 
386
  act.sa_handler=SIG_IGN;
 
387
  sigemptyset(&act.sa_mask);
 
388
  act.sa_flags=SA_RESTART;
 
389
  /* ignore SIGPIPE */
 
390
  if (sigaction(SIGPIPE,&act,&oldact)!=0)
 
391
    return -1; /* error setting signal handler */
 
392
  /* write the buffer */
 
393
  rv=write(fp->fd,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
 
394
  /* restore the old handler for SIGPIPE */
 
395
  if (sigaction(SIGPIPE,&oldact,NULL)!=0)
 
396
    return -1; /* error restoring signal handler */
 
397
#endif
 
398
  /* check for errors */
 
399
  if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)))
 
400
    return -1; /* something went wrong with the write */
 
401
  /* skip the written part in the buffer */
 
402
  if (rv>0)
 
403
  {
 
404
    fp->writebuffer.start+=rv;
 
405
    fp->writebuffer.len-=rv;
 
406
#ifdef DEBUG_TIO_STATS
 
407
    fp->byteswritten+=rv;
 
408
#endif /* DEBUG_TIO_STATS */
 
409
    /* reset start if len is 0 */
 
410
    if (fp->writebuffer.len==0)
 
411
      fp->writebuffer.start=0;
 
412
    /* move contents of the buffer to the front if it will save enough room */
 
413
    if (fp->writebuffer.start>=(fp->writebuffer.size/4))
 
414
    {
 
415
      memmove(fp->writebuffer.buffer,fp->writebuffer.buffer+fp->writebuffer.start,fp->writebuffer.len);
 
416
      fp->writebuffer.start=0;
 
417
    }
 
418
  }
 
419
  return 0;
 
420
}
 
421
 
 
422
/* write all the data in the buffer to the stream */
 
423
int tio_flush(TFILE *fp)
 
424
{
 
425
  struct timeval deadline;
 
426
  /* build a time by which we should be finished */
 
427
  tio_tv_prepare(&deadline,&(fp->writetimeout));
 
428
  /* loop until we have written our buffer */
 
429
  while (fp->writebuffer.len > 0)
 
430
  {
 
431
    /* wait until we can write */
 
432
    if (tio_select(fp,0,&deadline))
 
433
      return -1;
 
434
    /* write one block */
 
435
    if (tio_writebuf(fp))
 
436
      return -1;
 
437
  }
 
438
  return 0;
 
439
}
 
440
 
 
441
/* try a single write of data in the buffer if the file descriptor
 
442
   will accept data */
 
443
static int tio_flush_nonblock(TFILE *fp)
 
444
{
 
445
  struct timeval tv;
 
446
  fd_set fdset;
 
447
  int rv;
 
448
  /* prepare our filedescriptorset */
 
449
  FD_ZERO(&fdset);
 
450
  FD_SET(fp->fd,&fdset);
 
451
  /* set the timeout to 0 to poll */
 
452
  tv.tv_sec=0;
 
453
  tv.tv_usec=0;
 
454
  /* wait for activity */
 
455
  rv=select(FD_SETSIZE,NULL,&fdset,NULL,&tv);
 
456
  /* check if any file descriptors were ready (timeout) or we were
 
457
     interrupted */
 
458
  if ((rv==0)||((rv<0)&&(errno==EINTR)))
 
459
    return 0;
 
460
  /* any other errors? */
 
461
  if (rv<0)
 
462
    return -1;
 
463
  /* so file descriptor will accept writes */
 
464
  return tio_writebuf(fp);
 
465
}
 
466
 
 
467
int tio_write(TFILE *fp, const void *buf, size_t count)
 
468
{
 
469
  size_t fr;
 
470
  uint8_t *tmp;
 
471
  size_t newsz;
 
472
  const uint8_t *ptr=(const uint8_t *)buf;
 
473
  /* keep filling the buffer until we have bufferred everything */
 
474
  while (count>0)
 
475
  {
 
476
    /* figure out free size in buffer */
 
477
    fr=fp->writebuffer.size-(fp->writebuffer.start+fp->writebuffer.len);
 
478
    if (count <= fr)
 
479
    {
 
480
      /* the data fits in the buffer */
 
481
      memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,count);
 
482
      fp->writebuffer.len+=count;
 
483
      return 0;
 
484
    }
 
485
    else if (fr > 0)
 
486
    {
 
487
      /* fill the buffer with data that will fit */
 
488
      memcpy(fp->writebuffer.buffer+fp->writebuffer.start+fp->writebuffer.len,ptr,fr);
 
489
      fp->writebuffer.len+=fr;
 
490
      ptr+=fr;
 
491
      count-=fr;
 
492
    }
 
493
    /* try to flush some of the data that is in the buffer */
 
494
    if (tio_flush_nonblock(fp))
 
495
      return -1;
 
496
    /* if we have room now, try again */
 
497
    if (fp->writebuffer.size>(fp->writebuffer.start+fp->writebuffer.len))
 
498
      continue;
 
499
    /* try to grow the buffer */
 
500
    if (fp->writebuffer.size<fp->writebuffer.maxsize)
 
501
    {
 
502
      newsz=fp->writebuffer.size*2;
 
503
      if (newsz>fp->writebuffer.maxsize)
 
504
        newsz=fp->writebuffer.maxsize;
 
505
      tmp=realloc(fp->writebuffer.buffer,newsz);
 
506
      if (tmp!=NULL)
 
507
      {
 
508
        fp->writebuffer.buffer=tmp;
 
509
        fp->writebuffer.size=newsz;
 
510
        continue; /* try again */
 
511
      }
 
512
    }
 
513
    /* write the buffer to the stream */
 
514
    if (tio_flush(fp))
 
515
      return -1;
 
516
  }
 
517
  return 0;
 
518
}
 
519
 
 
520
int tio_close(TFILE *fp)
 
521
{
 
522
  int retv;
 
523
  /* write any buffered data */
 
524
  retv=tio_flush(fp);
 
525
#ifdef DEBUG_TIO_STATS
 
526
  /* dump statistics to stderr */
 
527
  fprintf(stderr,"DEBUG_TIO_STATS READ=%d WRITTEN=%d\n",fp->bytesread,fp->byteswritten);
 
528
#endif /* DEBUG_TIO_STATS */
 
529
  /* close file descriptor */
 
530
  if (close(fp->fd))
 
531
    retv=-1;
 
532
  /* free any allocated buffers */
 
533
  free(fp->readbuffer.buffer);
 
534
  free(fp->writebuffer.buffer);
 
535
  /* free the tio struct itself */
 
536
  free(fp);
 
537
  /* return the result of the earlier operations */
 
538
  return retv;
 
539
}
 
540
 
 
541
void tio_mark(TFILE *fp)
 
542
{
 
543
  /* move any data in the buffer to the start of the buffer */
 
544
  if ((fp->readbuffer.start>0)&&(fp->readbuffer.len>0))
 
545
  {
 
546
    memmove(fp->readbuffer.buffer,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.len);
 
547
    fp->readbuffer.start=0;
 
548
  }
 
549
  /* mark the stream as resettable */
 
550
  fp->read_resettable=1;
 
551
}
 
552
 
 
553
int tio_reset(TFILE *fp)
 
554
{
 
555
  /* check if the stream is (still) resettable */
 
556
  if (!fp->read_resettable)
 
557
    return -1;
 
558
  /* reset the buffer */
 
559
  fp->readbuffer.len+=fp->readbuffer.start;
 
560
  fp->readbuffer.start=0;
 
561
  return 0;
 
562
}