| 1 | /* Common code for file-based database parsers in nss_files module. | 
|---|
| 2 | Copyright (C) 1996-2020 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, see | 
|---|
| 17 | <https://www.gnu.org/licenses/>.  */ | 
|---|
| 18 |  | 
|---|
| 19 | #include <ctype.h> | 
|---|
| 20 | #include <errno.h> | 
|---|
| 21 | #include <string.h> | 
|---|
| 22 | #include <stdlib.h> | 
|---|
| 23 | #include <stdint.h> | 
|---|
| 24 | #include <nss_files.h> | 
|---|
| 25 |  | 
|---|
| 26 | /* These symbols are defined by the including source file: | 
|---|
| 27 |  | 
|---|
| 28 | ENTNAME -- database name of the structure and functions (hostent, pwent). | 
|---|
| 29 | STRUCTURE -- struct name, define only if not ENTNAME (passwd, group). | 
|---|
| 30 | DATABASE -- string of the database file's name ("hosts", "passwd"). | 
|---|
| 31 |  | 
|---|
| 32 | ENTDATA -- if defined, `struct ENTDATA' is used by the parser to store | 
|---|
| 33 | things pointed to by the resultant `struct STRUCTURE'. | 
|---|
| 34 |  | 
|---|
| 35 | NEED_H_ERRNO - defined iff an arg `int *herrnop' is used. | 
|---|
| 36 |  | 
|---|
| 37 | EXTRA_ARGS -- defined iff extra parameters must be passed to the parser | 
|---|
| 38 | EXTRA_ARGS_DECL -- declaration for these extra parameters | 
|---|
| 39 | EXTRA_ARGS_VALUE -- values to be passed for these parameters | 
|---|
| 40 |  | 
|---|
| 41 | Also see files-XXX.c.  */ | 
|---|
| 42 |  | 
|---|
| 43 | #ifndef EXTRA_ARGS | 
|---|
| 44 | # define | 
|---|
| 45 | # define | 
|---|
| 46 | # define | 
|---|
| 47 | #endif | 
|---|
| 48 |  | 
|---|
| 49 | #define CONCAT(a,b) CONCAT1(a,b) | 
|---|
| 50 | #define CONCAT1(a,b) a##b | 
|---|
| 51 |  | 
|---|
| 52 | #ifndef STRUCTURE | 
|---|
| 53 | # define STRUCTURE ENTNAME | 
|---|
| 54 | #endif | 
|---|
| 55 |  | 
|---|
| 56 |  | 
|---|
| 57 | struct parser_data | 
|---|
| 58 | { | 
|---|
| 59 | #ifdef ENTDATA | 
|---|
| 60 | struct ENTDATA entdata; | 
|---|
| 61 | # define ENTDATA_DECL(data) struct ENTDATA *const entdata = &data->entdata; | 
|---|
| 62 | #else | 
|---|
| 63 | # define ENTDATA_DECL(data) | 
|---|
| 64 | #endif | 
|---|
| 65 | char linebuffer[0]; | 
|---|
| 66 | }; | 
|---|
| 67 |  | 
|---|
| 68 | #ifdef ENTDATA | 
|---|
| 69 | /* The function can't be exported, because the entdata structure | 
|---|
| 70 | is defined only in files-foo.c.  */ | 
|---|
| 71 | # define parser_stclass static | 
|---|
| 72 | # define nss_files_parse_hidden_def(name) | 
|---|
| 73 | #else | 
|---|
| 74 | /* Export the line parser function so it can be used in nss_db.  */ | 
|---|
| 75 | # define parser_stclass /* Global */ | 
|---|
| 76 | # define parse_line CONCAT(_nss_files_parse_,ENTNAME) | 
|---|
| 77 | # if IS_IN (libc) | 
|---|
| 78 | /* We are defining one of the functions that actually lives in libc | 
|---|
| 79 | because it is used to implement fget*ent and suchlike.  */ | 
|---|
| 80 | #  define nss_files_parse_hidden_def(name) libc_hidden_def (name) | 
|---|
| 81 | # else | 
|---|
| 82 | #  define nss_files_parse_hidden_def(name) libnss_files_hidden_def (name) | 
|---|
| 83 | # endif | 
|---|
| 84 | #endif | 
|---|
| 85 |  | 
|---|
| 86 |  | 
|---|
| 87 | #ifdef EXTERN_PARSER | 
|---|
| 88 |  | 
|---|
| 89 | /* The parser is defined in a different module.  */ | 
|---|
| 90 | extern int parse_line (char *line, void *result, | 
|---|
| 91 | struct parser_data *data, size_t datalen, int *errnop | 
|---|
| 92 | EXTRA_ARGS_DECL); | 
|---|
| 93 |  | 
|---|
| 94 | # define LINE_PARSER(EOLSET, BODY) /* Do nothing */ | 
|---|
| 95 |  | 
|---|
| 96 | #else | 
|---|
| 97 |  | 
|---|
| 98 | /* Define a line parsing function.  */ | 
|---|
| 99 |  | 
|---|
| 100 | # define LINE_PARSER(EOLSET, BODY)					      \ | 
|---|
| 101 | parser_stclass int							      \ | 
|---|
| 102 | parse_line (char *line, void *generic_result,				      \ | 
|---|
| 103 | struct parser_data *data, size_t datalen, int *errnop	      \ | 
|---|
| 104 | EXTRA_ARGS_DECL)						      \ | 
|---|
| 105 | {									      \ | 
|---|
| 106 | struct STRUCTURE *result = generic_result;				      \ | 
|---|
| 107 | ENTDATA_DECL (data)							      \ | 
|---|
| 108 | BUFFER_PREPARE							      \ | 
|---|
| 109 | char *p = strpbrk (line, EOLSET "\n");				      \ | 
|---|
| 110 | if (p != NULL)							      \ | 
|---|
| 111 | *p = '\0';								      \ | 
|---|
| 112 | BODY;									      \ | 
|---|
| 113 | TRAILING_LIST_PARSER;							      \ | 
|---|
| 114 | return 1;								      \ | 
|---|
| 115 | }									      \ | 
|---|
| 116 | nss_files_parse_hidden_def (parse_line) | 
|---|
| 117 |  | 
|---|
| 118 |  | 
|---|
| 119 | # define STRING_FIELD(variable, terminator_p, swallow)			      \ | 
|---|
| 120 | {									      \ | 
|---|
| 121 | variable = line;							      \ | 
|---|
| 122 | while (*line != '\0' && !terminator_p (*line))			      \ | 
|---|
| 123 | ++line;								      \ | 
|---|
| 124 | if (*line != '\0')							      \ | 
|---|
| 125 | {									      \ | 
|---|
| 126 | *line = '\0';							      \ | 
|---|
| 127 | do								      \ | 
|---|
| 128 | ++line;							      \ | 
|---|
| 129 | while (swallow && terminator_p (*line));			      \ | 
|---|
| 130 | }									      \ | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | # define STRING_LIST(variable, terminator_c) \ | 
|---|
| 134 | {									      \ | 
|---|
| 135 | char **list = parse_list (&line, buf_start, buf_end, terminator_c,	      \ | 
|---|
| 136 | errnop);					      \ | 
|---|
| 137 | if (list)								      \ | 
|---|
| 138 | variable = list;							      \ | 
|---|
| 139 | else								      \ | 
|---|
| 140 | return -1;		/* -1 indicates we ran out of space.  */      \ | 
|---|
| 141 | \ | 
|---|
| 142 | /* Determine the new end of the buffer.  */				      \ | 
|---|
| 143 | while (*list != NULL)						      \ | 
|---|
| 144 | ++list;								      \ | 
|---|
| 145 | buf_start = (char *) (list + 1);					      \ | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | /* Helper function.  */ | 
|---|
| 149 | static inline uint32_t | 
|---|
| 150 | __attribute__ ((always_inline)) | 
|---|
| 151 | strtou32 (const char *nptr, char **endptr, int base) | 
|---|
| 152 | { | 
|---|
| 153 | unsigned long int val = strtoul (nptr, endptr, base); | 
|---|
| 154 |  | 
|---|
| 155 | /* Match the 32-bit behavior on 64-bit platforms.  */ | 
|---|
| 156 | if (sizeof (long int) > 4 && val > 0xffffffff) | 
|---|
| 157 | val = 0xffffffff; | 
|---|
| 158 |  | 
|---|
| 159 | return val; | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 | # define INT_FIELD(variable, terminator_p, swallow, base, convert)	      \ | 
|---|
| 163 | {									      \ | 
|---|
| 164 | char *endp;								      \ | 
|---|
| 165 | variable = convert (strtou32 (line, &endp, base));			      \ | 
|---|
| 166 | if (endp == line)							      \ | 
|---|
| 167 | return 0;								      \ | 
|---|
| 168 | else if (terminator_p (*endp))					      \ | 
|---|
| 169 | do								      \ | 
|---|
| 170 | ++endp;								      \ | 
|---|
| 171 | while (swallow && terminator_p (*endp));				      \ | 
|---|
| 172 | else if (*endp != '\0')						      \ | 
|---|
| 173 | return 0;								      \ | 
|---|
| 174 | line = endp;							      \ | 
|---|
| 175 | } | 
|---|
| 176 |  | 
|---|
| 177 | # define INT_FIELD_MAYBE_NULL(variable, terminator_p, swallow, base, convert, default)	      \ | 
|---|
| 178 | {									      \ | 
|---|
| 179 | char *endp;								      \ | 
|---|
| 180 | if (*line == '\0')							      \ | 
|---|
| 181 | /* We expect some more input, so don't allow the string to end here. */ \ | 
|---|
| 182 | return 0;								      \ | 
|---|
| 183 | variable = convert (strtou32 (line, &endp, base));			      \ | 
|---|
| 184 | if (endp == line)							      \ | 
|---|
| 185 | variable = default;						      \ | 
|---|
| 186 | if (terminator_p (*endp))						      \ | 
|---|
| 187 | do								      \ | 
|---|
| 188 | ++endp;								      \ | 
|---|
| 189 | while (swallow && terminator_p (*endp));				      \ | 
|---|
| 190 | else if (*endp != '\0')						      \ | 
|---|
| 191 | return 0;								      \ | 
|---|
| 192 | line = endp;							      \ | 
|---|
| 193 | } | 
|---|
| 194 |  | 
|---|
| 195 | # define ISCOLON(c) ((c) == ':') | 
|---|
| 196 |  | 
|---|
| 197 |  | 
|---|
| 198 | # ifndef TRAILING_LIST_MEMBER | 
|---|
| 199 | #  define BUFFER_PREPARE /* Nothing to do.  */ | 
|---|
| 200 | #  define TRAILING_LIST_PARSER /* Nothing to do.  */ | 
|---|
| 201 | # else | 
|---|
| 202 |  | 
|---|
| 203 | # define BUFFER_PREPARE \ | 
|---|
| 204 | char *buf_start = NULL;						      \ | 
|---|
| 205 | char *buf_end = (char *) data + datalen;				      \ | 
|---|
| 206 | if (line >= data->linebuffer && line < buf_end)			      \ | 
|---|
| 207 | /* Find the end of the line buffer, we will use the space in	      \ | 
|---|
| 208 | DATA after it for storing the vector of pointers.  */		      \ | 
|---|
| 209 | buf_start = strchr (line, '\0') + 1;				      \ | 
|---|
| 210 | else									      \ | 
|---|
| 211 | /* LINE does not point within DATA->linebuffer, so that space is	      \ | 
|---|
| 212 | not being used for scratch space right now.  We can use all of	      \ | 
|---|
| 213 | it for the pointer vector storage.  */				      \ | 
|---|
| 214 | buf_start = data->linebuffer;					      \ | 
|---|
| 215 |  | 
|---|
| 216 | #  define TRAILING_LIST_PARSER \ | 
|---|
| 217 | {									      \ | 
|---|
| 218 | if (buf_start == NULL)						      \ | 
|---|
| 219 | {									      \ | 
|---|
| 220 | if (line >= data->linebuffer && line < buf_end)			      \ | 
|---|
| 221 | /* Find the end of the line buffer, we will use the space in	      \ | 
|---|
| 222 | DATA after it for storing the vector of pointers.  */	      \ | 
|---|
| 223 | buf_start = strchr (line, '\0') + 1;				      \ | 
|---|
| 224 | else								      \ | 
|---|
| 225 | /* LINE does not point within DATA->linebuffer, so that space is      \ | 
|---|
| 226 | not being used for scratch space right now.  We can use all of     \ | 
|---|
| 227 | it for the pointer vector storage.  */			      \ | 
|---|
| 228 | buf_start = data->linebuffer;					      \ | 
|---|
| 229 | }									      \ | 
|---|
| 230 | \ | 
|---|
| 231 | char **list = parse_list (&line, buf_start, buf_end, '\0', errnop);	      \ | 
|---|
| 232 | if (list)								      \ | 
|---|
| 233 | result->TRAILING_LIST_MEMBER = list;				      \ | 
|---|
| 234 | else									      \ | 
|---|
| 235 | return -1;		/* -1 indicates we ran out of space.  */	      \ | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | static inline char ** | 
|---|
| 239 | __attribute ((always_inline)) | 
|---|
| 240 | parse_list (char **linep, char *eol, char *buf_end, int terminator_c, | 
|---|
| 241 | int *errnop) | 
|---|
| 242 | { | 
|---|
| 243 | char *line = *linep; | 
|---|
| 244 | char **list, **p; | 
|---|
| 245 |  | 
|---|
| 246 | /* Adjust the pointer so it is aligned for storing pointers.  */ | 
|---|
| 247 | eol += __alignof__ (char *) - 1; | 
|---|
| 248 | eol -= (eol - (char *) 0) % __alignof__ (char *); | 
|---|
| 249 | /* We will start the storage here for the vector of pointers.  */ | 
|---|
| 250 | list = (char **) eol; | 
|---|
| 251 |  | 
|---|
| 252 | p = list; | 
|---|
| 253 | while (1) | 
|---|
| 254 | { | 
|---|
| 255 | if ((char *) (p + 2) > buf_end) | 
|---|
| 256 | { | 
|---|
| 257 | /* We cannot fit another pointer in the buffer.  */ | 
|---|
| 258 | *errnop = ERANGE; | 
|---|
| 259 | return NULL; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | if (*line == '\0') | 
|---|
| 263 | break; | 
|---|
| 264 | if (*line == terminator_c) | 
|---|
| 265 | { | 
|---|
| 266 | ++line; | 
|---|
| 267 | break; | 
|---|
| 268 | } | 
|---|
| 269 |  | 
|---|
| 270 | /* Skip leading white space.  This might not be portable but useful.  */ | 
|---|
| 271 | while (isspace (*line)) | 
|---|
| 272 | ++line; | 
|---|
| 273 |  | 
|---|
| 274 | char *elt = line; | 
|---|
| 275 | while (1) | 
|---|
| 276 | { | 
|---|
| 277 | if (*line == '\0' || *line == terminator_c | 
|---|
| 278 | || TRAILING_LIST_SEPARATOR_P (*line)) | 
|---|
| 279 | { | 
|---|
| 280 | /* End of the next entry.  */ | 
|---|
| 281 | if (line > elt) | 
|---|
| 282 | /* We really found some data.  */ | 
|---|
| 283 | *p++ = elt; | 
|---|
| 284 |  | 
|---|
| 285 | /* Terminate string if necessary.  */ | 
|---|
| 286 | if (*line != '\0') | 
|---|
| 287 | { | 
|---|
| 288 | char endc = *line; | 
|---|
| 289 | *line++ = '\0'; | 
|---|
| 290 | if (endc == terminator_c) | 
|---|
| 291 | goto out; | 
|---|
| 292 | } | 
|---|
| 293 | break; | 
|---|
| 294 | } | 
|---|
| 295 | ++line; | 
|---|
| 296 | } | 
|---|
| 297 | } | 
|---|
| 298 | out: | 
|---|
| 299 | *p = NULL; | 
|---|
| 300 | *linep = line; | 
|---|
| 301 |  | 
|---|
| 302 | return list; | 
|---|
| 303 | } | 
|---|
| 304 |  | 
|---|
| 305 | # endif	/* TRAILING_LIST_MEMBER */ | 
|---|
| 306 | #endif	/* EXTERN_PARSER */ | 
|---|
| 307 |  | 
|---|
| 308 |  | 
|---|
| 309 | #define LOOKUP_NAME(nameelt, aliaselt)					      \ | 
|---|
| 310 | {									      \ | 
|---|
| 311 | char **ap;								      \ | 
|---|
| 312 | if (! strcmp (name, result->nameelt))					      \ | 
|---|
| 313 | break;								      \ | 
|---|
| 314 | for (ap = result->aliaselt; *ap; ++ap)				      \ | 
|---|
| 315 | if (! strcmp (name, *ap))						      \ | 
|---|
| 316 | break;								      \ | 
|---|
| 317 | if (*ap)								      \ | 
|---|
| 318 | break;								      \ | 
|---|
| 319 | } | 
|---|
| 320 |  | 
|---|
| 321 | #define LOOKUP_NAME_CASE(nameelt, aliaselt)				      \ | 
|---|
| 322 | {									      \ | 
|---|
| 323 | char **ap;								      \ | 
|---|
| 324 | if (! __strcasecmp (name, result->nameelt))				      \ | 
|---|
| 325 | break;								      \ | 
|---|
| 326 | for (ap = result->aliaselt; *ap; ++ap)				      \ | 
|---|
| 327 | if (! __strcasecmp (name, *ap))					      \ | 
|---|
| 328 | break;								      \ | 
|---|
| 329 | if (*ap)								      \ | 
|---|
| 330 | break;								      \ | 
|---|
| 331 | } | 
|---|
| 332 |  | 
|---|
| 333 |  | 
|---|
| 334 | /* This is defined by db-*.c to include "../nss_db/db-XXX.c" instead.  */ | 
|---|
| 335 | #ifndef GENERIC | 
|---|
| 336 | # define GENERIC "files-XXX.c" | 
|---|
| 337 | #endif | 
|---|
| 338 |  | 
|---|