1/* getndelim2 - Read a line from a stream, stopping at one of 2 delimiters,
2   with bounded memory allocation.
3
4   Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003 Free Software
5   Foundation, Inc.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10   any later version.
11
12   This program 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
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software Foundation,
19   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21/* Originally written by Jan Brittenson, bson@gnu.ai.mit.edu.  */
22
23#if HAVE_CONFIG_H
24# include <config.h>
25#endif
26
27/* Specification.  */
28#include "getndelim2.h"
29
30#if STDC_HEADERS
31# include <stdlib.h>
32#else
33char *malloc (), *realloc ();
34#endif
35
36#include "unlocked-io.h"
37
38/* Always add at least this many bytes when extending the buffer.  */
39#define MIN_CHUNK 64
40
41ssize_t
42getndelim2 (char **lineptr, size_t *linesize, size_t nmax,
43	    FILE *stream, int delim1, int delim2, size_t offset)
44{
45  size_t nbytes_avail;		/* Allocated but unused chars in *LINEPTR.  */
46  char *read_pos;		/* Where we're reading into *LINEPTR. */
47
48  if (!lineptr || !linesize || !nmax || !stream)
49    return -1;
50
51  if (!*lineptr)
52    {
53      size_t newlinesize = MIN_CHUNK;
54
55      if (newlinesize > nmax)
56	newlinesize = nmax;
57
58      *linesize = newlinesize;
59      *lineptr = malloc (*linesize);
60      if (!*lineptr)
61	return -1;
62    }
63
64  if (*linesize < offset)
65    return -1;
66
67  nbytes_avail = *linesize - offset;
68  read_pos = *lineptr + offset;
69
70  if (nbytes_avail == 0 && *linesize >= nmax)
71    return -1;
72
73  for (;;)
74    {
75      /* Here always *lineptr + *linesize == read_pos + nbytes_avail.  */
76
77      register int c = getc (stream);
78
79      /* We always want at least one char left in the buffer, since we
80	 always (unless we get an error while reading the first char)
81	 NUL-terminate the line buffer.  */
82
83      if (nbytes_avail < 2 && *linesize < nmax)
84	{
85	  size_t newlinesize =
86	    (*linesize > MIN_CHUNK ? 2 * *linesize : *linesize + MIN_CHUNK);
87
88	  if (newlinesize > nmax)
89	    newlinesize = nmax;
90
91	  if (newlinesize > *linesize)
92	    {
93	      *linesize = newlinesize;
94	      nbytes_avail = *linesize + *lineptr - read_pos;
95	      *lineptr = realloc (*lineptr, *linesize);
96	      if (!*lineptr)
97		return -1;
98	      read_pos = *linesize - nbytes_avail + *lineptr;
99	    }
100	}
101
102      if (c == EOF || ferror (stream))
103	{
104	  /* Return partial line, if any.  */
105	  if (read_pos == *lineptr)
106	    return -1;
107	  else
108	    break;
109	}
110
111      if (nbytes_avail >= 2)
112	{
113	  *read_pos++ = c;
114	  nbytes_avail--;
115	}
116
117      if (c == delim1 || (delim2 && c == delim2))
118	/* Return the line.  */
119	break;
120    }
121
122  /* Done - NUL terminate and return the number of chars read.
123     At this point we know that nbytes_avail >= 1.  */
124  *read_pos = '\0';
125
126  return read_pos - (*lineptr + offset);
127}
128