1/* getline.c -- Replacement for GNU C library function getline
2
3Copyright (C) 1993, 1996, 2001-2002 Free Software Foundation, Inc.
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License as
7published by the Free Software Foundation; either version 2 of the
8License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful, but
11WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18USA. */
19
20/* Written by Jan Brittenson, bson@gnu.ai.mit.edu.  */
21
22/* Specification.  */
23#include "getline.h"
24
25#include <stddef.h>
26#include <stdio.h>
27#include <string.h>
28#include <assert.h>
29
30/* Always add at least this many bytes when extending the buffer.  */
31#define MIN_CHUNK 64
32
33/* Reads up to (and including) a TERMINATOR from STREAM into *LINEPTR + OFFSET
34   (and null-terminate it). *LINEPTR is a pointer returned from new [] (or
35   NULL), pointing to *N characters of space.  It is realloc'd as
36   necessary.  Returns the number of characters read (not including the
37   null terminator), or -1 on error or immediate EOF.
38   NOTE: There is another getstr() function declared in <curses.h>.  */
39
40static int
41getstr (char **lineptr, size_t *n, FILE *stream, char terminator, size_t offset)
42{
43  size_t nchars_avail;          /* Allocated but unused chars in *LINEPTR.  */
44  char *read_pos;               /* Where we're reading into *LINEPTR. */
45
46  if (!lineptr || !n || !stream)
47    return -1;
48
49  if (!*lineptr)
50    {
51      *n = MIN_CHUNK;
52      *lineptr = new char[*n];
53    }
54
55  nchars_avail = *n - offset;
56  read_pos = *lineptr + offset;
57
58  for (;;)
59    {
60      int c = getc (stream);
61
62      /* We always want at least one char left in the buffer, since we
63         always (unless we get an error while reading the first char)
64         NUL-terminate the line buffer.  */
65
66      assert (*n - nchars_avail == (size_t) (read_pos - *lineptr));
67      if (nchars_avail < 2)
68        {
69          if (*n > MIN_CHUNK)
70            *n *= 2;
71          else
72            *n += MIN_CHUNK;
73
74          nchars_avail = *n + *lineptr - read_pos;
75          char *new_line = new char[*n];
76          if (*lineptr)
77            {
78              memcpy (new_line, *lineptr, read_pos - *lineptr);
79              delete[] *lineptr;
80            }
81          *lineptr = new_line;
82          read_pos = *n - nchars_avail + *lineptr;
83          assert (*n - nchars_avail == (size_t) (read_pos - *lineptr));
84        }
85
86      if (c == EOF || ferror (stream))
87        {
88          /* Return partial line, if any.  */
89          if (read_pos == *lineptr)
90            return -1;
91          else
92            break;
93        }
94
95      *read_pos++ = c;
96      nchars_avail--;
97
98      if (c == terminator)
99        /* Return the line.  */
100        break;
101    }
102
103  /* Done - NUL terminate and return the number of chars read.  */
104  *read_pos = '\0';
105
106  return read_pos - (*lineptr + offset);
107}
108
109int
110get_line (char **lineptr, size_t *n, FILE *stream)
111{
112  return getstr (lineptr, n, stream, '\n', 0);
113}
114
115int
116get_delim (char **lineptr, size_t *n, int delimiter, FILE *stream)
117{
118  return getstr (lineptr, n, stream, delimiter, 0);
119}
120