| 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 | |