fdwalk should return 0 on an empty directory
[kopensolaris-gnu/glibc.git] / nss / nss_files / files-parse.c
1 /* Common code for file-based database parsers in nss_files module.
2    Copyright (C) 1996-2000, 2003, 2004 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library 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 GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24
25 /* These symbols are defined by the including source file:
26
27    ENTNAME -- database name of the structure and functions (hostent, pwent).
28    STRUCTURE -- struct name, define only if not ENTNAME (passwd, group).
29    DATABASE -- string of the database file's name ("hosts", "passwd").
30
31    ENTDATA -- if defined, `struct ENTDATA' is used by the parser to store
32               things pointed to by the resultant `struct STRUCTURE'.
33
34    NEED_H_ERRNO - defined iff an arg `int *herrnop' is used.
35
36    EXTRA_ARGS -- defined iff extra parameters must be passed to the parser
37    EXTRA_ARGS_DECL -- declaration for these extra parameters
38    EXTRA_ARGS_VALUE -- values to be passed for these parameters
39
40    Also see files-XXX.c.  */
41
42 #ifndef EXTRA_ARGS
43 # define EXTRA_ARGS
44 # define EXTRA_ARGS_DECL
45 # define EXTRA_ARGS_VALUE
46 #endif
47
48 #define CONCAT(a,b) CONCAT1(a,b)
49 #define CONCAT1(a,b) a##b
50
51 #ifndef STRUCTURE
52 # define STRUCTURE ENTNAME
53 #endif
54
55
56 struct parser_data
57   {
58 #ifdef ENTDATA
59     struct ENTDATA entdata;
60 # define ENTDATA_DECL(data) struct ENTDATA *const entdata = &data->entdata;
61 #else
62 # define ENTDATA_DECL(data)
63 #endif
64     char linebuffer[0];
65   };
66
67 #ifdef ENTDATA
68 /* The function can't be exported, because the entdata structure
69    is defined only in files-foo.c.  */
70 # define parser_stclass static
71 # define nss_files_parse_hidden_def(name)
72 #else
73 /* Export the line parser function so it can be used in nss_db.  */
74 # define parser_stclass /* Global */
75 # define parse_line CONCAT(_nss_files_parse_,ENTNAME)
76 # ifdef IS_IN_libc
77 /* We are defining one of the functions that actually lives in libc
78    because it is used to implement fget*ent and suchlike.  */
79 #  define nss_files_parse_hidden_def(name) libc_hidden_def (name)
80 # else
81 #  define nss_files_parse_hidden_def(name) libnss_files_hidden_def (name)
82 # endif
83 #endif
84
85
86 #ifdef EXTERN_PARSER
87
88 /* The parser is defined in a different module.  */
89 extern int parse_line (char *line, struct STRUCTURE *result,
90                        struct parser_data *data, size_t datalen, int *errnop
91                        EXTRA_ARGS_DECL);
92
93 # define LINE_PARSER(EOLSET, BODY) /* Do nothing */
94
95 #else
96
97 /* Define a line parsing function.  */
98
99 # define LINE_PARSER(EOLSET, BODY)                                            \
100 parser_stclass int                                                            \
101 parse_line (char *line, struct STRUCTURE *result,                             \
102             struct parser_data *data, size_t datalen, int *errnop             \
103             EXTRA_ARGS_DECL)                                                  \
104 {                                                                             \
105   ENTDATA_DECL (data)                                                         \
106   char *p = strpbrk (line, EOLSET "\n");                                      \
107   if (p != NULL)                                                              \
108     *p = '\0';                                                                \
109   BODY;                                                                       \
110   TRAILING_LIST_PARSER;                                                       \
111   return 1;                                                                   \
112 }                                                                             \
113 nss_files_parse_hidden_def (parse_line)
114
115
116 # define STRING_FIELD(variable, terminator_p, swallow)                        \
117   {                                                                           \
118     variable = line;                                                          \
119     while (*line != '\0' && !terminator_p (*line))                            \
120       ++line;                                                                 \
121     if (*line != '\0')                                                        \
122       {                                                                       \
123         *line = '\0';                                                         \
124         do                                                                    \
125           ++line;                                                             \
126         while (swallow && terminator_p (*line));                              \
127       }                                                                       \
128   }
129
130 # define INT_FIELD(variable, terminator_p, swallow, base, convert)            \
131   {                                                                           \
132     char *endp;                                                               \
133     variable = convert (strtoul (line, &endp, base));                         \
134     if (endp == line)                                                         \
135       return 0;                                                               \
136     else if (terminator_p (*endp))                                            \
137       do                                                                      \
138         ++endp;                                                               \
139       while (swallow && terminator_p (*endp));                                \
140     else if (*endp != '\0')                                                   \
141       return 0;                                                               \
142     line = endp;                                                              \
143   }
144
145 # define INT_FIELD_MAYBE_NULL(variable, terminator_p, swallow, base, convert, default)        \
146   {                                                                           \
147     char *endp;                                                               \
148     if (*line == '\0')                                                        \
149       /* We expect some more input, so don't allow the string to end here. */ \
150       return 0;                                                               \
151     variable = convert (strtoul (line, &endp, base));                         \
152     if (endp == line)                                                         \
153       variable = default;                                                     \
154     if (terminator_p (*endp))                                                 \
155       do                                                                      \
156         ++endp;                                                               \
157       while (swallow && terminator_p (*endp));                                \
158     else if (*endp != '\0')                                                   \
159       return 0;                                                               \
160     line = endp;                                                              \
161   }
162
163 # define ISCOLON(c) ((c) == ':')
164
165
166 # ifndef TRAILING_LIST_MEMBER
167 #  define TRAILING_LIST_PARSER /* Nothing to do.  */
168 # else
169
170 #  define TRAILING_LIST_PARSER                                                \
171 {                                                                             \
172   char **list = parse_list (line, data, datalen, errnop);                     \
173   if (list)                                                                   \
174     result->TRAILING_LIST_MEMBER = list;                                      \
175   else                                                                        \
176     return -1;          /* -1 indicates we ran out of space.  */              \
177 }
178
179 static inline char **
180 __attribute ((always_inline))
181 parse_list (char *line, struct parser_data *data, size_t datalen, int *errnop)
182 {
183   char *eol, **list, **p;
184
185   if (line >= data->linebuffer && line < (char *) data + datalen)
186     /* Find the end of the line buffer, we will use the space in DATA after
187        it for storing the vector of pointers.  */
188     eol = strchr (line, '\0') + 1;
189   else
190     /* LINE does not point within DATA->linebuffer, so that space is
191        not being used for scratch space right now.  We can use all of
192        it for the pointer vector storage.  */
193     eol = data->linebuffer;
194   /* Adjust the pointer so it is aligned for storing pointers.  */
195   eol += __alignof__ (char *) - 1;
196   eol -= (eol - (char *) 0) % __alignof__ (char *);
197   /* We will start the storage here for the vector of pointers.  */
198   list = (char **) eol;
199
200   p = list;
201   while (1)
202     {
203       char *elt;
204
205       if ((size_t) ((char *) &p[1] - (char *) data) > datalen)
206         {
207           /* We cannot fit another pointer in the buffer.  */
208           *errnop = ERANGE;
209           return NULL;
210         }
211       if (*line == '\0')
212         break;
213
214       /* Skip leading white space.  This might not be portable but useful.  */
215       while (isspace (*line))
216         ++line;
217
218       elt = line;
219       while (1)
220         {
221           if (*line == '\0' || TRAILING_LIST_SEPARATOR_P (*line))
222             {
223               /* End of the next entry.  */
224               if (line > elt)
225                 /* We really found some data.  */
226                 *p++ = elt;
227
228               /* Terminate string if necessary.  */
229               if (*line != '\0')
230                 *line++ = '\0';
231               break;
232             }
233           ++line;
234         }
235     }
236   *p = NULL;
237
238   return list;
239 }
240
241 # endif /* TRAILING_LIST_MEMBER */
242 #endif  /* EXTERN_PARSER */
243
244
245 #define LOOKUP_NAME(nameelt, aliaselt)                                        \
246 {                                                                             \
247   char **ap;                                                                  \
248   if (! strcmp (name, result->nameelt))                                       \
249     break;                                                                    \
250   for (ap = result->aliaselt; *ap; ++ap)                                      \
251     if (! strcmp (name, *ap))                                                 \
252       break;                                                                  \
253   if (*ap)                                                                    \
254     break;                                                                    \
255 }
256
257 #define LOOKUP_NAME_CASE(nameelt, aliaselt)                                   \
258 {                                                                             \
259   char **ap;                                                                  \
260   if (! __strcasecmp (name, result->nameelt))                                 \
261     break;                                                                    \
262   for (ap = result->aliaselt; *ap; ++ap)                                      \
263     if (! __strcasecmp (name, *ap))                                           \
264       break;                                                                  \
265   if (*ap)                                                                    \
266     break;                                                                    \
267 }
268
269
270 /* This is defined by db-*.c to include "../nss_db/db-XXX.c" instead.  */
271 #ifndef GENERIC
272 # define GENERIC "files-XXX.c"
273 #endif