update FSF address in copyright
[gnulib.git] / lib / readtokens.c
1 /* readtokens.c  -- Functions for reading tokens from an input stream.
2    Copyright (C) 1990-1991 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 void *xmalloc ();
53 void *xrealloc ();
54
55 #define STREQ(a,b) ((a) == (b) || ((a) && (b) && *(a) == *(b) \
56                                    && strcmp(a, b) == 0))
57
58 /* Initialize a tokenbuffer. */
59
60 void
61 init_tokenbuffer (tokenbuffer)
62      token_buffer *tokenbuffer;
63 {
64   tokenbuffer->size = INITIAL_TOKEN_LENGTH;
65   tokenbuffer->buffer = ((char *) xmalloc (INITIAL_TOKEN_LENGTH));
66 }
67
68 /* Read a token from `stream' into `tokenbuffer'.
69    Upon return, the token is in tokenbuffer->buffer and
70    has a trailing '\0' instead of the original delimiter.
71    The function value is the length of the token not including
72    the final '\0'.  When EOF is reached (i.e. on the call
73    after the last token is read), -1 is returned and tokenbuffer
74    isn't modified.
75
76    This function will work properly on lines containing NUL bytes
77    and on files that aren't newline-terminated.  */
78
79 long
80 readtoken (stream, delim, n_delim, tokenbuffer)
81      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 (stream, projected_n_tokens, delim, n_delim,
168             tokens_out, token_lengths)
169      FILE *stream;
170      int projected_n_tokens;
171      const char *delim;
172      int n_delim;
173      char ***tokens_out;
174      long **token_lengths;
175 {
176   token_buffer tb, *token = &tb;
177   int token_length;
178   char **tokens;
179   long *lengths;
180   int sz;
181   int n_tokens;
182
183   n_tokens = 0;
184   if (projected_n_tokens > 0)
185     projected_n_tokens++;       /* add one for trailing NULL pointer */
186   else
187     projected_n_tokens = 64;
188   sz = projected_n_tokens;
189   tokens = (char **) xmalloc (sz * sizeof (char *));
190   lengths = (long *) xmalloc (sz * sizeof (long));
191
192   init_tokenbuffer (token);
193   for (;;)
194     {
195       char *tmp;
196       token_length = readtoken (stream, delim, n_delim, token);
197       if (n_tokens >= sz)
198         {
199           sz *= 2;
200           tokens = (char **) xrealloc (tokens, sz * sizeof (char *));
201           lengths = (long *) xrealloc (lengths, sz * sizeof (long));
202         }
203
204       if (token_length < 0)
205         {
206           /* don't increment n_tokens for NULL entry */
207           tokens[n_tokens] = NULL;
208           lengths[n_tokens] = -1;
209           break;
210         }
211       tmp = (char *) xmalloc ((token_length + 1) * sizeof (char));
212       lengths[n_tokens] = token_length;
213       tokens[n_tokens] = strncpy (tmp, token->buffer,
214                                   (unsigned) (token_length + 1));
215       n_tokens++;
216     }
217
218   free (token->buffer);
219   *tokens_out = tokens;
220   if (token_lengths != NULL)
221     *token_lengths = lengths;
222   return n_tokens;
223 }