/* Parse a service line from nsswitch.conf. Copyright (c) 1996-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include #include /* Staging area during parsing. */ #define DYNARRAY_STRUCT action_list #define DYNARRAY_ELEMENT struct nss_action #define DYNARRAY_PREFIX action_list_ #include /* Skip whitespace in line[]. */ #define SKIP_WS() \ while (line[0] != '\0' && isspace (line[0])) \ ++line; /* Read the source names: `( ( "[" "!"? ( "=" )+ "]" )? )*' */ static bool nss_action_parse (const char *line, struct action_list *result) { while (1) { SKIP_WS (); if (line[0] == '\0') /* No more sources specified. */ return true; /* Read identifier. */ const char *name = line; while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[') ++line; if (name == line) return true; struct nss_action new_service = { .module = __nss_module_allocate (name, line - name), }; if (new_service.module == NULL) { /* Memory allocation error. */ action_list_mark_failed (result); return false; } nss_action_set_all (&new_service, NSS_ACTION_CONTINUE); nss_action_set (&new_service, NSS_STATUS_SUCCESS, NSS_ACTION_RETURN); nss_action_set (&new_service, NSS_STATUS_RETURN, NSS_ACTION_RETURN); SKIP_WS (); if (line[0] == '[') { /* Read criterions. */ /* Skip the '['. */ ++line; SKIP_WS (); do { int not; enum nss_status status; lookup_actions action; /* Grok ! before name to mean all statuses but that one. */ not = line[0] == '!'; if (not) ++line; /* Read status name. */ name = line; while (line[0] != '\0' && !isspace (line[0]) && line[0] != '=' && line[0] != ']') ++line; /* Compare with known statuses. */ if (line - name == 7) { if (__strncasecmp (name, "SUCCESS", 7) == 0) status = NSS_STATUS_SUCCESS; else if (__strncasecmp (name, "UNAVAIL", 7) == 0) status = NSS_STATUS_UNAVAIL; else return false; } else if (line - name == 8) { if (__strncasecmp (name, "NOTFOUND", 8) == 0) status = NSS_STATUS_NOTFOUND; else if (__strncasecmp (name, "TRYAGAIN", 8) == 0) status = NSS_STATUS_TRYAGAIN; else return false; } else return false; SKIP_WS (); if (line[0] != '=') return false; /* Skip the '='. */ ++line; SKIP_WS (); name = line; while (line[0] != '\0' && !isspace (line[0]) && line[0] != '=' && line[0] != ']') ++line; if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0) action = NSS_ACTION_RETURN; else if (line - name == 8 && __strncasecmp (name, "CONTINUE", 8) == 0) action = NSS_ACTION_CONTINUE; else if (line - name == 5 && __strncasecmp (name, "MERGE", 5) == 0) action = NSS_ACTION_MERGE; else return false; if (not) { /* Save the current action setting for this status, set them all to the given action, and reset this one. */ const lookup_actions save = nss_action_get (&new_service, status); nss_action_set_all (&new_service, action); nss_action_set (&new_service, status, save); } else nss_action_set (&new_service, status, action); SKIP_WS (); } while (line[0] != ']'); /* Skip the ']'. */ ++line; } action_list_add (result, new_service); } } nss_action_list __nss_action_parse (const char *line) { struct action_list list; action_list_init (&list); if (nss_action_parse (line, &list)) { size_t size; struct nss_action null_service = { .module = NULL, }; action_list_add (&list, null_service); size = action_list_size (&list); return __nss_action_allocate (action_list_begin (&list), size); } else if (action_list_has_failed (&list)) { /* Memory allocation error. */ __set_errno (ENOMEM); return NULL; } else { /* Parse error. */ __set_errno (EINVAL); return NULL; } }