(root)/
glibc-2.38/
nss/
nss_files/
files-parse.c
       1  /* Common code for file-based database parsers in nss_files module.
       2     Copyright (C) 1996-2023 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 EXTRA_ARGS
      45  # define EXTRA_ARGS_DECL
      46  # define EXTRA_ARGS_VALUE
      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  # define nss_files_parse_hidden_def(name) libc_hidden_def (name)
      78  #endif
      79  
      80  
      81  #ifdef EXTERN_PARSER
      82  
      83  /* The parser is defined in a different module.  */
      84  extern int parse_line (char *line, void *result,
      85  		       struct parser_data *data, size_t datalen, int *errnop
      86  		       EXTRA_ARGS_DECL);
      87  
      88  # define LINE_PARSER(EOLSET, BODY) /* Do nothing */
      89  
      90  #else
      91  
      92  /* Define a line parsing function.  */
      93  
      94  # define LINE_PARSER(EOLSET, BODY)					      \
      95  parser_stclass int							      \
      96  parse_line (char *line, void *generic_result,				      \
      97  	    struct parser_data *data, size_t datalen, int *errnop	      \
      98  	    EXTRA_ARGS_DECL)						      \
      99  {									      \
     100    struct STRUCTURE *result = generic_result;				      \
     101    ENTDATA_DECL (data)							      \
     102    BUFFER_PREPARE							      \
     103    char *p = strpbrk (line, EOLSET "\n");				      \
     104    if (p != NULL)							      \
     105      *p = '\0';								      \
     106    BODY;									      \
     107    TRAILING_LIST_PARSER;							      \
     108    return 1;								      \
     109  }									      \
     110  nss_files_parse_hidden_def (parse_line)
     111  
     112  
     113  # define STRING_FIELD(variable, terminator_p, swallow)			      \
     114    {									      \
     115      variable = line;							      \
     116      while (*line != '\0' && !terminator_p (*line))			      \
     117        ++line;								      \
     118      if (*line != '\0')							      \
     119        {									      \
     120  	*line = '\0';							      \
     121  	do								      \
     122  	  ++line;							      \
     123  	while (swallow && terminator_p (*line));			      \
     124        }									      \
     125    }
     126  
     127  # define STRING_LIST(variable, terminator_c) \
     128    {									      \
     129      char **list = parse_list (&line, buf_start, buf_end, terminator_c,	      \
     130  			      errnop);					      \
     131      if (list)								      \
     132        variable = list;							      \
     133      else								      \
     134        return -1;		/* -1 indicates we ran out of space.  */      \
     135  									      \
     136      /* Determine the new end of the buffer.  */				      \
     137      while (*list != NULL)						      \
     138        ++list;								      \
     139      buf_start = (char *) (list + 1);					      \
     140    }
     141  
     142  /* Helper function.  */
     143  static inline uint32_t
     144  __attribute__ ((always_inline))
     145  strtou32 (const char *nptr, char **endptr, int base)
     146  {
     147    unsigned long int val = strtoul (nptr, endptr, base);
     148  
     149    /* Match the 32-bit behavior on 64-bit platforms.  */
     150    if (sizeof (long int) > 4 && val > 0xffffffff)
     151      val = 0xffffffff;
     152  
     153    return val;
     154  }
     155  
     156  # define INT_FIELD(variable, terminator_p, swallow, base, convert)	      \
     157    {									      \
     158      char *endp;								      \
     159      variable = convert (strtou32 (line, &endp, base));			      \
     160      if (endp == line)							      \
     161        return 0;								      \
     162      else if (terminator_p (*endp))					      \
     163        do								      \
     164  	++endp;								      \
     165        while (swallow && terminator_p (*endp));				      \
     166      else if (*endp != '\0')						      \
     167        return 0;								      \
     168      line = endp;							      \
     169    }
     170  
     171  # define INT_FIELD_MAYBE_NULL(variable, terminator_p, swallow, base, convert, default)	      \
     172    {									      \
     173      char *endp;								      \
     174      if (*line == '\0')							      \
     175        /* We expect some more input, so don't allow the string to end here. */ \
     176        return 0;								      \
     177      variable = convert (strtou32 (line, &endp, base));			      \
     178      if (endp == line)							      \
     179        variable = default;						      \
     180      if (terminator_p (*endp))						      \
     181        do								      \
     182  	++endp;								      \
     183        while (swallow && terminator_p (*endp));				      \
     184      else if (*endp != '\0')						      \
     185        return 0;								      \
     186      line = endp;							      \
     187    }
     188  
     189  # define ISCOLON(c) ((c) == ':')
     190  
     191  
     192  # ifndef TRAILING_LIST_MEMBER
     193  #  define BUFFER_PREPARE /* Nothing to do.  */
     194  #  define TRAILING_LIST_PARSER /* Nothing to do.  */
     195  # else
     196  
     197  # define BUFFER_PREPARE \
     198    char *buf_start = NULL;						      \
     199    char *buf_end = (char *) data + datalen;				      \
     200    if (line >= data->linebuffer && line < buf_end)			      \
     201      /* Find the end of the line buffer, we will use the space in	      \
     202         DATA after it for storing the vector of pointers.  */		      \
     203      buf_start = strchr (line, '\0') + 1;				      \
     204    else									      \
     205      /* LINE does not point within DATA->linebuffer, so that space is	      \
     206         not being used for scratch space right now.  We can use all of	      \
     207         it for the pointer vector storage.  */				      \
     208      buf_start = data->linebuffer;					      \
     209  
     210  #  define TRAILING_LIST_PARSER \
     211  {									      \
     212    if (buf_start == NULL)						      \
     213      {									      \
     214        if (line >= data->linebuffer && line < buf_end)			      \
     215  	/* Find the end of the line buffer, we will use the space in	      \
     216  	   DATA after it for storing the vector of pointers.  */	      \
     217  	buf_start = strchr (line, '\0') + 1;				      \
     218        else								      \
     219  	/* LINE does not point within DATA->linebuffer, so that space is      \
     220  	   not being used for scratch space right now.  We can use all of     \
     221  	   it for the pointer vector storage.  */			      \
     222  	buf_start = data->linebuffer;					      \
     223      }									      \
     224  									      \
     225    char **list = parse_list (&line, buf_start, buf_end, '\0', errnop);	      \
     226    if (list)								      \
     227      result->TRAILING_LIST_MEMBER = list;				      \
     228    else									      \
     229      return -1;		/* -1 indicates we ran out of space.  */	      \
     230  }
     231  
     232  static inline char **
     233  __attribute ((always_inline))
     234  parse_list (char **linep, char *eol, char *buf_end, int terminator_c,
     235  	    int *errnop)
     236  {
     237    char *line = *linep;
     238    char **list, **p;
     239  
     240    /* Adjust the pointer so it is aligned for storing pointers.  */
     241    eol += __alignof__ (char *) - 1;
     242    eol -= ((uintptr_t) eol) % __alignof__ (char *);
     243    /* We will start the storage here for the vector of pointers.  */
     244    list = (char **) eol;
     245  
     246    p = list;
     247    while (1)
     248      {
     249        if ((char *) (p + 2) > buf_end)
     250  	{
     251  	  /* We cannot fit another pointer in the buffer.  */
     252  	  *errnop = ERANGE;
     253  	  return NULL;
     254  	}
     255  
     256        if (*line == '\0')
     257  	break;
     258        if (*line == terminator_c)
     259  	{
     260  	  ++line;
     261  	  break;
     262  	}
     263  
     264        /* Skip leading white space.  This might not be portable but useful.  */
     265        while (isspace (*line))
     266  	++line;
     267  
     268        char *elt = line;
     269        while (1)
     270  	{
     271  	  if (*line == '\0' || *line == terminator_c
     272  	      || TRAILING_LIST_SEPARATOR_P (*line))
     273  	    {
     274  	      /* End of the next entry.  */
     275  	      if (line > elt)
     276  		/* We really found some data.  */
     277  		*p++ = elt;
     278  
     279  	      /* Terminate string if necessary.  */
     280  	      if (*line != '\0')
     281  		{
     282  		  char endc = *line;
     283  		  *line++ = '\0';
     284  		  if (endc == terminator_c)
     285  		    goto out;
     286  		}
     287  	      break;
     288  	    }
     289  	  ++line;
     290  	}
     291      }
     292   out:
     293    *p = NULL;
     294    *linep = line;
     295  
     296    return list;
     297  }
     298  
     299  # endif	/* TRAILING_LIST_MEMBER */
     300  #endif	/* EXTERN_PARSER */
     301  
     302  
     303  #define LOOKUP_NAME(nameelt, aliaselt)					      \
     304  {									      \
     305    char **ap;								      \
     306    if (! strcmp (name, result->nameelt))					      \
     307      break;								      \
     308    for (ap = result->aliaselt; *ap; ++ap)				      \
     309      if (! strcmp (name, *ap))						      \
     310        break;								      \
     311    if (*ap)								      \
     312      break;								      \
     313  }
     314  
     315  #define LOOKUP_NAME_CASE(nameelt, aliaselt)				      \
     316  {									      \
     317    char **ap;								      \
     318    if (! __strcasecmp (name, result->nameelt))				      \
     319      break;								      \
     320    for (ap = result->aliaselt; *ap; ++ap)				      \
     321      if (! __strcasecmp (name, *ap))					      \
     322        break;								      \
     323    if (*ap)								      \
     324      break;								      \
     325  }
     326  
     327  
     328  /* This is defined by db-*.c to include "../nss_db/db-XXX.c" instead.  */
     329  #ifndef GENERIC
     330  # define GENERIC "files-XXX.c"
     331  #endif