files-parse.c 9.8 KB

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