1  /* Read a line from an nss_files database file.
       2     Copyright (C) 2020-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 <nss_files.h>
      20  
      21  #include <ctype.h>
      22  #include <errno.h>
      23  #include <string.h>
      24  
      25  int
      26  __nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset)
      27  {
      28    /* We need space for at least one character, the line terminator,
      29       and the NUL byte.  */
      30    if (len < 3)
      31      {
      32        *poffset = -1;
      33        __set_errno (ERANGE);
      34        return ERANGE;
      35      }
      36  
      37    while (true)
      38      {
      39        /* Keep original offset for retries.  */
      40        *poffset = __ftello64 (fp);
      41  
      42        buf[len - 1] = '\xff';        /* Marker to recognize truncation.  */
      43        if (__fgets_unlocked (buf, len, fp) == NULL)
      44          {
      45            if (__feof_unlocked (fp))
      46              {
      47                __set_errno (ENOENT);
      48                return ENOENT;
      49              }
      50            else
      51              {
      52                /* Any other error.  Do not return ERANGE in this case
      53                   because the caller would retry.  */
      54                if (errno == ERANGE)
      55                  __set_errno (EINVAL);
      56                return errno;
      57              }
      58          }
      59        else if (buf[len - 1] != '\xff')
      60          /* The buffer is too small.  Arrange for re-reading the same
      61             line on the next call.  */
      62          return __nss_readline_seek (fp, *poffset);
      63  
      64        /* __fgets_unlocked succeeded.  */
      65  
      66        /* Remove leading whitespace.  */
      67        char *p = buf;
      68        while (isspace (*p))
      69          ++p;
      70        if (*p == '\0' || *p == '#')
      71          /* Skip empty lines and comments.  */
      72          continue;
      73        if (p != buf)
      74          memmove (buf, p, strlen (p));
      75  
      76        /* Return line to the caller.  */
      77        return 0;
      78      }
      79  }
      80  libc_hidden_def (__nss_readline)
      81  
      82  int
      83  __nss_readline_seek (FILE *fp, off64_t offset)
      84  {
      85    if (offset < 0 /* __ftello64 failed.  */
      86        || __fseeko64 (fp, offset, SEEK_SET) < 0)
      87      {
      88        /* Without seeking support, it is not possible to
      89           re-read the same line, so this is a hard failure.  */
      90        fseterr_unlocked (fp);
      91        __set_errno (ESPIPE);
      92        return ESPIPE;
      93      }
      94    else
      95      {
      96        __set_errno (ERANGE);
      97        return ERANGE;
      98      }
      99  }