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         isdelim[(unsigned char) delim[j]] = 1;
100     }
101
102   /* FIXME: don't fool with this caching.  Use strchr instead.  */
103   /* skip over any leading delimiters */
104   for (c = getc (stream); c >= 0 && isdelim[c]; c = getc (stream))
105     {
106       /* empty */
107     }
108
109   p = tokenbuffer->buffer;
110   n = tokenbuffer->size;
111   i = 0;
112   for (;;)
113     {
114       if (c < 0 && i == 0)
115         return -1;
116
117       if (i == n)
118         p = x2nrealloc (p, &n, sizeof *p);
119
120       if (c < 0)
121         {
122           p[i] = 0;
123           break;
124         }
125       if (isdelim[c])
126         {
127           p[i] = 0;
128           break;
129         }
130       p[i++] = c;
131       c = getc (stream);
132     }
133
134   tokenbuffer->buffer = p;
135   tokenbuffer->size = n;
136   return i;
137 }
138
139 /* Build a NULL-terminated array of pointers to tokens
140    read from STREAM.  Return the number of tokens read.
141    All storage is obtained through calls to xmalloc-like functions.
142
143    %%% Question: is it worth it to do a single
144    %%% realloc() of `tokens' just before returning? */
145
146 size_t
147 readtokens (FILE *stream,
148             size_t projected_n_tokens,
149             const char *delim,
150             size_t n_delim,
151             char ***tokens_out,
152             size_t **token_lengths)
153 {
154   token_buffer tb, *token = &tb;
155   char **tokens;
156   size_t *lengths;
157   size_t sz;
158   size_t n_tokens;
159
160   if (projected_n_tokens == 0)
161     projected_n_tokens = 64;
162   else
163     projected_n_tokens++;       /* add one for trailing NULL pointer */
164
165   sz = projected_n_tokens;
166   tokens = xnmalloc (sz, sizeof *tokens);
167   lengths = xnmalloc (sz, sizeof *lengths);
168
169   n_tokens = 0;
170   init_tokenbuffer (token);
171   for (;;)
172     {
173       char *tmp;
174       size_t token_length = readtoken (stream, delim, n_delim, token);
175       if (n_tokens >= sz)
176         {
177           tokens = x2nrealloc (tokens, &sz, sizeof *tokens);
178           lengths = xnrealloc (lengths, sz, sizeof *lengths);
179         }
180
181       if (token_length == (size_t) -1)
182         {
183           /* don't increment n_tokens for NULL entry */
184           tokens[n_tokens] = NULL;
185           lengths[n_tokens] = 0;
186           break;
187         }
188       tmp = xnmalloc (token_length + 1, sizeof *tmp);
189       lengths[n_tokens] = token_length;
190       tokens[n_tokens] = memcpy (tmp, token->buffer, token_length + 1);
191       n_tokens++;
192     }
193
194   free (token->buffer);
195   *tokens_out = tokens;
196   if (token_lengths != NULL)
197     *token_lengths = lengths;
198   return n_tokens;
199 }