Tizen 2.0 Release
[external/tizen-coreutils.git] / lib / readtokens.c
1 /* readtokens.c  -- Functions for reading tokens from an input stream.
2
3    Copyright (C) 1990-1991, 1999-2004, 2006 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19    Written by Jim Meyering. */
20
21 /* This almost supercedes xreadline stuff -- using delim="\n"
22    gives the same functionality, except that these functions
23    would never return empty lines. */
24
25 #include <config.h>
26
27 #include "readtokens.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdbool.h>
33
34 #include "xalloc.h"
35
36 #if USE_UNLOCKED_IO
37 # include "unlocked-io.h"
38 #endif
39
40 #define STREQ(a,b) ((a) == (b) || ((a) && (b) && *(a) == *(b) \
41                                    && strcmp(a, b) == 0))
42
43 /* Initialize a tokenbuffer. */
44
45 void
46 init_tokenbuffer (token_buffer *tokenbuffer)
47 {
48   tokenbuffer->size = 0;
49   tokenbuffer->buffer = NULL;
50 }
51
52 /* Read a token from STREAM into TOKENBUFFER.
53    A token is delimited by any of the N_DELIM bytes in DELIM.
54    Upon return, the token is in tokenbuffer->buffer and
55    has a trailing '\0' instead of any original delimiter.
56    The function value is the length of the token not including
57    the final '\0'.  Upon EOF (i.e. on the call after the last
58    token is read) or error, return -1 without modifying tokenbuffer.
59    The EOF and error conditions may be distinguished in the caller
60    by testing ferror (STREAM).
61
62    This function works properly on lines containing NUL bytes
63    and on files do not end with a delimiter.  */
64
65 size_t
66 readtoken (FILE *stream,
67            const char *delim,
68            size_t n_delim,
69            token_buffer *tokenbuffer)
70 {
71   char *p;
72   int c;
73   size_t i, n;
74   static const char *saved_delim = NULL;
75   static char isdelim[256];
76   bool same_delimiters;
77
78   if (delim == NULL && saved_delim == NULL)
79     abort ();
80
81   same_delimiters = false;
82   if (delim != saved_delim && saved_delim != NULL)
83     {
84       same_delimiters = true;
85       for (i = 0; i < n_delim; i++)
86         {
87           if (delim[i] != saved_delim[i])
88             {
89               same_delimiters = false;
90               break;
91             }
92         }
93     }
94
95   if (!same_delimiters)
96     {
97       size_t j;
98       saved_delim = delim;
99       memset (isdelim, 0, sizeof isdelim);
100       for (j = 0; j < n_delim; j++)
101         {
102           unsigned char ch = delim[j];
103           isdelim[ch] = 1;
104         }
105     }
106
107   /* FIXME: don't fool with this caching.  Use strchr instead.  */
108   /* skip over any leading delimiters */
109   for (c = getc (stream); c >= 0 && isdelim[c]; c = getc (stream))
110     {
111       /* empty */
112     }
113
114   p = tokenbuffer->buffer;
115   n = tokenbuffer->size;
116   i = 0;
117   for (;;)
118     {
119       if (c < 0 && i == 0)
120         return -1;
121
122       if (i == n)
123         p = x2nrealloc (p, &n, sizeof *p);
124
125       if (c < 0)
126         {
127           p[i] = 0;
128           break;
129         }
130       if (isdelim[c])
131         {
132           p[i] = 0;
133           break;
134         }
135       p[i++] = c;
136       c = getc (stream);
137     }
138
139   tokenbuffer->buffer = p;
140   tokenbuffer->size = n;
141   return i;
142 }
143
144 /* Build a NULL-terminated array of pointers to tokens
145    read from STREAM.  Return the number of tokens read.
146    All storage is obtained through calls to xmalloc-like functions.
147
148    %%% Question: is it worth it to do a single
149    %%% realloc() of `tokens' just before returning? */
150
151 size_t
152 readtokens (FILE *stream,
153             size_t projected_n_tokens,
154             const char *delim,
155             size_t n_delim,
156             char ***tokens_out,
157             size_t **token_lengths)
158 {
159   token_buffer tb, *token = &tb;
160   char **tokens;
161   size_t *lengths;
162   size_t sz;
163   size_t n_tokens;
164
165   if (projected_n_tokens == 0)
166     projected_n_tokens = 64;
167   else
168     projected_n_tokens++;       /* add one for trailing NULL pointer */
169
170   sz = projected_n_tokens;
171   tokens = xnmalloc (sz, sizeof *tokens);
172   lengths = xnmalloc (sz, sizeof *lengths);
173
174   n_tokens = 0;
175   init_tokenbuffer (token);
176   for (;;)
177     {
178       char *tmp;
179       size_t token_length = readtoken (stream, delim, n_delim, token);
180       if (n_tokens >= sz)
181         {
182           tokens = x2nrealloc (tokens, &sz, sizeof *tokens);
183           lengths = xnrealloc (lengths, sz, sizeof *lengths);
184         }
185
186       if (token_length == (size_t) -1)
187         {
188           /* don't increment n_tokens for NULL entry */
189           tokens[n_tokens] = NULL;
190           lengths[n_tokens] = 0;
191           break;
192         }
193       tmp = xnmalloc (token_length + 1, sizeof *tmp);
194       lengths[n_tokens] = token_length;
195       tokens[n_tokens] = memcpy (tmp, token->buffer, token_length + 1);
196       n_tokens++;
197     }
198
199   free (token->buffer);
200   *tokens_out = tokens;
201   if (token_lengths != NULL)
202     *token_lengths = lengths;
203   return n_tokens;
204 }