*** empty log message ***
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "xalloc.h"
36
37 #if USE_UNLOCKED_IO
38 # include "unlocked-io.h"
39 #endif
40
41 #define STREQ(a,b) ((a) == (b) || ((a) && (b) && *(a) == *(b) \
42                                    && strcmp(a, b) == 0))
43
44 /* Initialize a tokenbuffer. */
45
46 void
47 init_tokenbuffer (token_buffer *tokenbuffer)
48 {
49   tokenbuffer->size = 0;
50   tokenbuffer->buffer = NULL;
51 }
52
53 /* Read a token from STREAM into TOKENBUFFER.
54    A token is delimited by any of the N_DELIM bytes in DELIM.
55    Upon return, the token is in tokenbuffer->buffer and
56    has a trailing '\0' instead of any original delimiter.
57    The function value is the length of the token not including
58    the final '\0'.  Upon EOF (i.e. on the call after the last
59    token is read) or error, return -1 without modifying tokenbuffer.
60    The EOF and error conditions may be distinguished in the caller
61    by testing ferror (STREAM).
62
63    This function works properly on lines containing NUL bytes
64    and on files do not end with a delimiter.  */
65
66 size_t
67 readtoken (FILE *stream,
68            const char *delim,
69            size_t n_delim,
70            token_buffer *tokenbuffer)
71 {
72   char *p;
73   int c;
74   size_t i, n;
75   static const char *saved_delim = NULL;
76   static char isdelim[256];
77   bool same_delimiters;
78
79   if (delim == NULL && saved_delim == NULL)
80     abort ();
81
82   same_delimiters = false;
83   if (delim != saved_delim && saved_delim != NULL)
84     {
85       same_delimiters = true;
86       for (i = 0; i < n_delim; i++)
87         {
88           if (delim[i] != saved_delim[i])
89             {
90               same_delimiters = false;
91               break;
92             }
93         }
94     }
95
96   if (!same_delimiters)
97     {
98       size_t j;
99       saved_delim = delim;
100       memset (isdelim, 0, sizeof isdelim);
101       for (j = 0; j < n_delim; j++)
102         {
103           unsigned char ch = delim[j];
104           isdelim[ch] = 1;
105         }
106     }
107
108   /* FIXME: don't fool with this caching.  Use strchr instead.  */
109   /* skip over any leading delimiters */
110   for (c = getc (stream); c >= 0 && isdelim[c]; c = getc (stream))
111     {
112       /* empty */
113     }
114
115   p = tokenbuffer->buffer;
116   n = tokenbuffer->size;
117   i = 0;
118   for (;;)
119     {
120       if (c < 0 && i == 0)
121         return -1;
122
123       if (i == n)
124         p = x2nrealloc (p, &n, sizeof *p);
125
126       if (c < 0)
127         {
128           p[i] = 0;
129           break;
130         }
131       if (isdelim[c])
132         {
133           p[i] = 0;
134           break;
135         }
136       p[i++] = c;
137       c = getc (stream);
138     }
139
140   tokenbuffer->buffer = p;
141   tokenbuffer->size = n;
142   return i;
143 }
144
145 /* Build a NULL-terminated array of pointers to tokens
146    read from STREAM.  Return the number of tokens read.
147    All storage is obtained through calls to xmalloc-like functions.
148
149    %%% Question: is it worth it to do a single
150    %%% realloc() of `tokens' just before returning? */
151
152 size_t
153 readtokens (FILE *stream,
154             size_t projected_n_tokens,
155             const char *delim,
156             size_t n_delim,
157             char ***tokens_out,
158             size_t **token_lengths)
159 {
160   token_buffer tb, *token = &tb;
161   char **tokens;
162   size_t *lengths;
163   size_t sz;
164   size_t n_tokens;
165
166   if (projected_n_tokens == 0)
167     projected_n_tokens = 64;
168   else
169     projected_n_tokens++;       /* add one for trailing NULL pointer */
170
171   sz = projected_n_tokens;
172   tokens = xnmalloc (sz, sizeof *tokens);
173   lengths = xnmalloc (sz, sizeof *lengths);
174
175   n_tokens = 0;
176   init_tokenbuffer (token);
177   for (;;)
178     {
179       char *tmp;
180       size_t token_length = readtoken (stream, delim, n_delim, token);
181       if (n_tokens >= sz)
182         {
183           tokens = x2nrealloc (tokens, &sz, sizeof *tokens);
184           lengths = xnrealloc (lengths, sz, sizeof *lengths);
185         }
186
187       if (token_length == (size_t) -1)
188         {
189           /* don't increment n_tokens for NULL entry */
190           tokens[n_tokens] = NULL;
191           lengths[n_tokens] = 0;
192           break;
193         }
194       tmp = xnmalloc (token_length + 1, sizeof *tmp);
195       lengths[n_tokens] = token_length;
196       tokens[n_tokens] = memcpy (tmp, token->buffer, token_length + 1);
197       n_tokens++;
198     }
199
200   free (token->buffer);
201   *tokens_out = tokens;
202   if (token_lengths != NULL)
203     *token_lengths = lengths;
204   return n_tokens;
205 }