Remove K&R cruft.
[gnulib.git] / lib / readtokens.c
1 /* readtokens.c  -- Functions for reading tokens from an input stream.
2    Copyright (C) 1990-1991, 1999, 2001, 2003 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    To Do:
25      - To allow '\0' as a delimiter, I will have to change
26        interfaces to permit specification of delimiter-string
27        length.
28    */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include "readtokens.h"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "unlocked-io.h"
41 #include "xalloc.h"
42
43 #define STREQ(a,b) ((a) == (b) || ((a) && (b) && *(a) == *(b) \
44                                    && strcmp(a, b) == 0))
45
46 /* Initialize a tokenbuffer. */
47
48 void
49 init_tokenbuffer (token_buffer *tokenbuffer)
50 {
51   tokenbuffer->size = INITIAL_TOKEN_LENGTH;
52   tokenbuffer->buffer = xmalloc (INITIAL_TOKEN_LENGTH);
53 }
54
55 /* Read a token from `stream' into `tokenbuffer'.
56    Upon return, the token is in tokenbuffer->buffer and
57    has a trailing '\0' instead of the original delimiter.
58    The function value is the length of the token not including
59    the final '\0'.  When EOF is reached (i.e. on the call
60    after the last token is read), -1 is returned and tokenbuffer
61    isn't modified.
62
63    This function will work properly on lines containing NUL bytes
64    and on files that aren't newline-terminated.  */
65
66 long
67 readtoken (FILE *stream,
68            const char *delim,
69            int n_delim,
70            token_buffer *tokenbuffer)
71 {
72   char *p;
73   int c, i, n;
74   static const char *saved_delim = NULL;
75   static char isdelim[256];
76   int same_delimiters;
77
78   if (delim == NULL && saved_delim == NULL)
79     abort ();
80
81   same_delimiters = 0;
82   if (delim != saved_delim && saved_delim != NULL)
83     {
84       same_delimiters = 1;
85       for (i = 0; i < n_delim; i++)
86         {
87           if (delim[i] != saved_delim[i])
88             {
89               same_delimiters = 0;
90               break;
91             }
92         }
93     }
94
95   if (!same_delimiters)
96     {
97       const char *t;
98       unsigned int j;
99       saved_delim = delim;
100       for (j = 0; j < sizeof (isdelim); j++)
101         isdelim[j] = 0;
102       for (t = delim; *t; t++)
103         isdelim[(unsigned char) *t] = 1;
104     }
105
106   p = tokenbuffer->buffer;
107   n = tokenbuffer->size;
108   i = 0;
109
110   /* FIXME: don't fool with this caching BS.  Use strchr instead.  */
111   /* skip over any leading delimiters */
112   for (c = getc (stream); c >= 0 && isdelim[c]; c = getc (stream))
113     {
114       /* empty */
115     }
116
117   for (;;)
118     {
119       if (i >= n)
120         {
121           n = 3 * (n / 2 + 1);
122           p = xrealloc (p, (unsigned int) n);
123         }
124       if (c < 0)
125         {
126           if (i == 0)
127             return (-1);
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 /* Return a NULL-terminated array of pointers to tokens
146    read from `stream.'  The number of tokens is returned
147    as the value of the function.
148    All storage is obtained through calls to malloc();
149
150    %%% Question: is it worth it to do a single
151    %%% realloc() of `tokens' just before returning? */
152
153 int
154 readtokens (FILE *stream,
155             int projected_n_tokens,
156             const char *delim,
157             int n_delim,
158             char ***tokens_out,
159             long **token_lengths)
160 {
161   token_buffer tb, *token = &tb;
162   int token_length;
163   char **tokens;
164   long *lengths;
165   int sz;
166   int n_tokens;
167
168   n_tokens = 0;
169   if (projected_n_tokens > 0)
170     projected_n_tokens++;       /* add one for trailing NULL pointer */
171   else
172     projected_n_tokens = 64;
173   sz = projected_n_tokens;
174   tokens = xmalloc (sz * sizeof (char *));
175   lengths = xmalloc (sz * sizeof (long));
176
177   init_tokenbuffer (token);
178   for (;;)
179     {
180       char *tmp;
181       token_length = readtoken (stream, delim, n_delim, token);
182       if (n_tokens >= sz)
183         {
184           sz *= 2;
185           tokens = xrealloc (tokens, sz * sizeof (char *));
186           lengths = xrealloc (lengths, sz * sizeof (long));
187         }
188
189       if (token_length < 0)
190         {
191           /* don't increment n_tokens for NULL entry */
192           tokens[n_tokens] = NULL;
193           lengths[n_tokens] = -1;
194           break;
195         }
196       tmp = xmalloc ((token_length + 1) * sizeof (char));
197       lengths[n_tokens] = token_length;
198       tokens[n_tokens] = strncpy (tmp, token->buffer,
199                                   (unsigned) (token_length + 1));
200       n_tokens++;
201     }
202
203   free (token->buffer);
204   *tokens_out = tokens;
205   if (token_lengths != NULL)
206     *token_lengths = lengths;
207   return n_tokens;
208 }