2002-08-09 Jakub Jelinek <jakub@redhat.com>
[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, 1997, 1998, 1999, 2000 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 inline
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 # define nss_files_parse_hidden_def(name) libc_hidden_def (name)
77 #endif
78
79
80 #ifdef EXTERN_PARSER
81
82 /* The parser is defined in a different module.  */
83 extern int parse_line (char *line, struct STRUCTURE *result,
84                        struct parser_data *data, size_t datalen, int *errnop
85                        EXTRA_ARGS_DECL);
86
87 # define LINE_PARSER(EOLSET, BODY) /* Do nothing */
88
89 #else
90
91 /* Define a line parsing function.  */
92
93 # define LINE_PARSER(EOLSET, BODY)                                            \
94 parser_stclass int                                                            \
95 parse_line (char *line, struct STRUCTURE *result,                             \
96             struct parser_data *data, size_t datalen, int *errnop             \
97             EXTRA_ARGS_DECL)                                                  \
98 {                                                                             \
99   ENTDATA_DECL (data)                                                         \
100   char *p = strpbrk (line, EOLSET "\n");                                      \
101   if (p != NULL)                                                              \
102     *p = '\0';                                                                \
103   BODY;                                                                       \
104   TRAILING_LIST_PARSER;                                                       \
105   return 1;                                                                   \
106 }                                                                             \
107 nss_files_parse_hidden_def (parse_line)
108
109
110 # define STRING_FIELD(variable, terminator_p, swallow)                        \
111   {                                                                           \
112     variable = line;                                                          \
113     while (*line != '\0' && !terminator_p (*line))                            \
114       ++line;                                                                 \
115     if (*line != '\0')                                                        \
116       {                                                                       \
117         *line = '\0';                                                         \
118         do                                                                    \
119           ++line;                                                             \
120         while (swallow && terminator_p (*line));                              \
121       }                                                                       \
122   }
123
124 # define INT_FIELD(variable, terminator_p, swallow, base, convert)            \
125   {                                                                           \
126     char *endp;                                                               \
127     variable = convert (strtoul (line, &endp, base));                         \
128     if (endp == line)                                                         \
129       return 0;                                                               \
130     else if (terminator_p (*endp))                                            \
131       do                                                                      \
132         ++endp;                                                               \
133       while (swallow && terminator_p (*endp));                                \
134     else if (*endp != '\0')                                                   \
135       return 0;                                                               \
136     line = endp;                                                              \
137   }
138
139 # define INT_FIELD_MAYBE_NULL(variable, terminator_p, swallow, base, convert, default)        \
140   {                                                                           \
141     char *endp;                                                               \
142     if (*line == '\0')                                                        \
143       /* We expect some more input, so don't allow the string to end here. */ \
144       return 0;                                                               \
145     variable = convert (strtoul (line, &endp, base));                         \
146     if (endp == line)                                                         \
147       variable = default;                                                     \
148     if (terminator_p (*endp))                                                 \
149       do                                                                      \
150         ++endp;                                                               \
151       while (swallow && terminator_p (*endp));                                \
152     else if (*endp != '\0')                                                   \
153       return 0;                                                               \
154     line = endp;                                                              \
155   }
156
157 # define ISCOLON(c) ((c) == ':')
158
159
160 # ifndef TRAILING_LIST_MEMBER
161 #  define TRAILING_LIST_PARSER /* Nothing to do.  */
162 # else
163
164 #  define TRAILING_LIST_PARSER                                                \
165 {                                                                             \
166   char **list = parse_list (line, data, datalen, errnop);                     \
167   if (list)                                                                   \
168     result->TRAILING_LIST_MEMBER = list;                                      \
169   else                                                                        \
170     return -1;          /* -1 indicates we ran out of space.  */              \
171 }
172
173 static inline char **
174 parse_list (char *line, struct parser_data *data, size_t datalen, int *errnop)
175 {
176   char *eol, **list, **p;
177
178   if (line >= data->linebuffer && line < (char *) data + datalen)
179     /* Find the end of the line buffer, we will use the space in DATA after
180        it for storing the vector of pointers.  */
181     eol = strchr (line, '\0') + 1;
182   else
183     /* LINE does not point within DATA->linebuffer, so that space is
184        not being used for scratch space right now.  We can use all of
185        it for the pointer vector storage.  */
186     eol = data->linebuffer;
187   /* Adjust the pointer so it is aligned for storing pointers.  */
188   eol += __alignof__ (char *) - 1;
189   eol -= (eol - (char *) 0) % __alignof__ (char *);
190   /* We will start the storage here for the vector of pointers.  */
191   list = (char **) eol;
192
193   p = list;
194   while (1)
195     {
196       char *elt;
197
198       if ((size_t) ((char *) &p[1] - (char *) data) > datalen)
199         {
200           /* We cannot fit another pointer in the buffer.  */
201           *errnop = ERANGE;
202           return NULL;
203         }
204       if (*line == '\0')
205         break;
206
207       /* Skip leading white space.  This might not be portable but useful.  */
208       while (isspace (*line))
209         ++line;
210
211       elt = line;
212       while (1)
213         {
214           if (*line == '\0' || TRAILING_LIST_SEPARATOR_P (*line))
215             {
216               /* End of the next entry.  */
217               if (line > elt)
218                 /* We really found some data.  */
219                 *p++ = elt;
220
221               /* Terminate string if necessary.  */
222               if (*line != '\0')
223                 *line++ = '\0';
224               break;
225             }
226           ++line;
227         }
228     }
229   *p = NULL;
230
231   return list;
232 }
233
234 # endif /* TRAILING_LIST_MEMBER */
235 #endif  /* EXTERN_PARSER */
236
237
238 #define LOOKUP_NAME(nameelt, aliaselt)                                        \
239 {                                                                             \
240   char **ap;                                                                  \
241   if (! strcmp (name, result->nameelt))                                       \
242     break;                                                                    \
243   for (ap = result->aliaselt; *ap; ++ap)                                      \
244     if (! strcmp (name, *ap))                                                 \
245       break;                                                                  \
246   if (*ap)                                                                    \
247     break;                                                                    \
248 }
249
250 #define LOOKUP_NAME_CASE(nameelt, aliaselt)                                   \
251 {                                                                             \
252   char **ap;                                                                  \
253   if (! __strcasecmp (name, result->nameelt))                                 \
254     break;                                                                    \
255   for (ap = result->aliaselt; *ap; ++ap)                                      \
256     if (! __strcasecmp (name, *ap))                                           \
257       break;                                                                  \
258   if (*ap)                                                                    \
259     break;                                                                    \
260 }
261
262
263 /* This is defined by db-*.c to include "../nss_db/db-XXX.c" instead.  */
264 #ifndef GENERIC
265 # define GENERIC "files-XXX.c"
266 #endif