Merge from coreutils.
[gnulib.git] / lib / readtokens.c
1 /* readtokens.c  -- Functions for reading tokens from an input stream.
2    Copyright (C) 1990-1991, 1999-2004 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18    Written by Jim Meyering. */
19
20 /* This almost supercedes xreadline stuff -- using delim="\n"
21    gives the same functionality, except that these functions
22    would never return empty lines. */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include "readtokens.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdbool.h>
34
35 #include "unlocked-io.h"
36 #include "xalloc.h"
37
38 #define STREQ(a,b) ((a) == (b) || ((a) && (b) && *(a) == *(b) \
39                                    && strcmp(a, b) == 0))
40
41 /* Initialize a tokenbuffer. */
42
43 void
44 init_tokenbuffer (token_buffer *tokenbuffer)
45 {
46   tokenbuffer->size = 0;
47   tokenbuffer->buffer = NULL;
48 }
49
50 /* Read a token from STREAM into TOKENBUFFER.
51    A token is delimited by any of the N_DELIM bytes in DELIM.
52    Upon return, the token is in tokenbuffer->buffer and
53    has a trailing '\0' instead of any original delimiter.
54    The function value is the length of the token not including
55    the final '\0'.  Upon EOF (i.e. on the call after the last
56    token is read) or error, return -1 without modifying tokenbuffer.
57    The EOF and error conditions may be distinguished in the caller
58    by testing ferror (STREAM).
59
60    This function works properly on lines containing NUL bytes
61    and on files do not end with a delimiter.  */
62
63 size_t
64 readtoken (FILE *stream,
65            const char *delim,
66            size_t n_delim,
67            token_buffer *tokenbuffer)
68 {
69   char *p;
70   int c;
71   size_t i, n;
72   static const char *saved_delim = NULL;
73   static char isdelim[256];
74   bool same_delimiters;
75
76   if (delim == NULL && saved_delim == NULL)
77     abort ();
78
79   same_delimiters = false;
80   if (delim != saved_delim && saved_delim != NULL)
81     {
82       same_delimiters = true;
83       for (i = 0; i < n_delim; i++)
84         {
85           if (delim[i] != saved_delim[i])
86             {
87               same_delimiters = false;
88               break;
89             }
90         }
91     }
92
93   if (!same_delimiters)
94     {
95       size_t j;
96       saved_delim = delim;
97       memset (isdelim, 0, sizeof isdelim);
98       for (j = 0; j < n_delim; j++)
99         {
100           unsigned char ch = delim[j];
101           isdelim[ch] = 1;
102         }
103     }
104
105   /* FIXME: don't fool with this caching.  Use strchr instead.  */
106   /* skip over any leading delimiters */
107   for (c = getc (stream); c >= 0 && isdelim[c]; c = getc (stream))
108     {
109       /* empty */
110     }
111
112   p = tokenbuffer->buffer;
113   n = tokenbuffer->size;
114   i = 0;
115   for (;;)
116     {
117       if (c < 0 && i == 0)
118         return -1;
119
120       if (i == n)
121         p = x2nrealloc (p, &n, sizeof *p);
122
123       if (c < 0)
124         {
125           p[i] = 0;
126           break;
127         }
128       if (isdelim[c])
129         {
130           p[i] = 0;
131           break;
132         }
133       p[i++] = c;
134       c = getc (stream);
135     }
136
137   tokenbuffer->buffer = p;
138   tokenbuffer->size = n;
139   return i;
140 }
141
142 /* Build a NULL-terminated array of pointers to tokens
143    read from STREAM.  Return the number of tokens read.
144    All storage is obtained through calls to xmalloc-like functions.
145
146    %%% Question: is it worth it to do a single
147    %%% realloc() of `tokens' just before returning? */
148
149 size_t
150 readtokens (FILE *stream,
151             size_t projected_n_tokens,
152             const char *delim,
153             size_t n_delim,
154             char ***tokens_out,
155             size_t **token_lengths)
156 {
157   token_buffer tb, *token = &tb;
158   char **tokens;
159   size_t *lengths;
160   size_t sz;
161   size_t n_tokens;
162
163   if (projected_n_tokens == 0)
164     projected_n_tokens = 64;
165   else
166     projected_n_tokens++;       /* add one for trailing NULL pointer */
167
168   sz = projected_n_tokens;
169   tokens = xnmalloc (sz, sizeof *tokens);
170   lengths = xnmalloc (sz, sizeof *lengths);
171
172   n_tokens = 0;
173   init_tokenbuffer (token);
174   for (;;)
175     {
176       char *tmp;
177       size_t token_length = readtoken (stream, delim, n_delim, token);
178       if (n_tokens >= sz)
179         {
180           tokens = x2nrealloc (tokens, &sz, sizeof *tokens);
181           lengths = xnrealloc (lengths, sz, sizeof *lengths);
182         }
183
184       if (token_length == (size_t) -1)
185         {
186           /* don't increment n_tokens for NULL entry */
187           tokens[n_tokens] = NULL;
188           lengths[n_tokens] = 0;
189           break;
190         }
191       tmp = xnmalloc (token_length + 1, sizeof *tmp);
192       lengths[n_tokens] = token_length;
193       tokens[n_tokens] = memcpy (tmp, token->buffer, token_length + 1);
194       n_tokens++;
195     }
196
197   free (token->buffer);
198   *tokens_out = tokens;
199   if (token_lengths != NULL)
200     *token_lengths = lengths;
201   return n_tokens;
202 }