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