(root)/
coreutils-9.4/
lib/
getusershell.c
       1  /* getusershell.c -- Return names of valid user shells.
       2  
       3     Copyright (C) 1991, 1997, 2000-2001, 2003-2006, 2008-2023 Free Software
       4     Foundation, Inc.
       5  
       6     This program is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation, either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
      20  
      21  #include <config.h>
      22  
      23  /* Specification.  */
      24  #include <unistd.h>
      25  
      26  #ifndef SHELLS_FILE
      27  # ifndef __DJGPP__
      28  /* File containing a list of nonrestricted shells, one per line. */
      29  #  define SHELLS_FILE "/etc/shells"
      30  # else
      31  /* This is a horrible kludge.  Isn't there a better way?  */
      32  #  define SHELLS_FILE "/dev/env/DJDIR/etc/shells"
      33  # endif
      34  #endif
      35  
      36  #include <stdlib.h>
      37  #include <ctype.h>
      38  
      39  #include "stdio--.h"
      40  #include "xalloc.h"
      41  
      42  #if GNULIB_GETUSERSHELL_SINGLE_THREAD
      43  # include "unlocked-io.h"
      44  #endif
      45  
      46  static idx_t readname (char **, idx_t *, FILE *);
      47  
      48  #if ! defined ADDITIONAL_DEFAULT_SHELLS && defined __MSDOS__
      49  # define ADDITIONAL_DEFAULT_SHELLS \
      50    "c:/dos/command.com", "c:/windows/command.com", "c:/command.com",
      51  #else
      52  # define ADDITIONAL_DEFAULT_SHELLS /* empty */
      53  #endif
      54  
      55  /* List of shells to use if the shells file is missing. */
      56  static char const* const default_shells[] =
      57  {
      58    ADDITIONAL_DEFAULT_SHELLS
      59    "/bin/sh", "/bin/csh", "/usr/bin/sh", "/usr/bin/csh", NULL
      60  };
      61  
      62  /* Index of the next shell in 'default_shells' to return.
      63     0 means we are not using 'default_shells'. */
      64  static size_t default_index = 0;
      65  
      66  /* Input stream from the shells file. */
      67  static FILE *shellstream = NULL;
      68  
      69  /* Line of input from the shells file. */
      70  static char *line = NULL;
      71  
      72  /* Number of bytes allocated for 'line'. */
      73  static idx_t line_size = 0;
      74  
      75  /* Return an entry from the shells file, ignoring comment lines.
      76     If the file doesn't exist, use the list in DEFAULT_SHELLS (above).
      77     In any case, the returned string is in memory allocated through malloc.
      78     Return NULL if there are no more entries.  */
      79  
      80  char *
      81  getusershell (void)
      82  {
      83    if (default_index > 0)
      84      {
      85        if (default_shells[default_index])
      86          /* Not at the end of the list yet.  */
      87          return xstrdup (default_shells[default_index++]);
      88        return NULL;
      89      }
      90  
      91    if (shellstream == NULL)
      92      {
      93        shellstream = fopen (SHELLS_FILE, "r");
      94        if (shellstream == NULL)
      95          {
      96            /* No shells file.  Use the default list.  */
      97            default_index = 1;
      98            return xstrdup (default_shells[0]);
      99          }
     100      }
     101  
     102    while (readname (&line, &line_size, shellstream))
     103      {
     104        if (*line != '#')
     105          return line;
     106      }
     107    return NULL;                  /* End of file. */
     108  }
     109  
     110  /* Rewind the shells file. */
     111  
     112  void
     113  setusershell (void)
     114  {
     115    default_index = 0;
     116    if (shellstream)
     117      rewind (shellstream);
     118  }
     119  
     120  /* Close the shells file. */
     121  
     122  void
     123  endusershell (void)
     124  {
     125    if (shellstream)
     126      {
     127        fclose (shellstream);
     128        shellstream = NULL;
     129      }
     130  }
     131  
     132  /* Read a line from STREAM, removing any newline at the end.
     133     Place the result in *NAME, which is malloc'd
     134     and/or realloc'd as necessary and can start out NULL,
     135     and whose size is passed and returned in *SIZE.
     136  
     137     Return the number of bytes placed in *NAME
     138     if some nonempty sequence was found, otherwise 0.  */
     139  
     140  static idx_t
     141  readname (char **name, idx_t *size, FILE *stream)
     142  {
     143    int c;
     144    size_t name_index = 0;
     145  
     146    /* Skip blank space.  */
     147    while ((c = getc (stream)) != EOF && isspace (c))
     148      /* Do nothing. */ ;
     149  
     150    for (;;)
     151      {
     152        if (*size <= name_index)
     153          *name = xpalloc (*name, size, 1, -1, sizeof **name);
     154        if (c == EOF || isspace (c))
     155          break;
     156        (*name)[name_index++] = c;
     157        c = getc (stream);
     158      }
     159    (*name)[name_index] = '\0';
     160    return name_index;
     161  }
     162  
     163  #ifdef TEST
     164  int
     165  main (void)
     166  {
     167    char *s;
     168  
     169    while (s = getusershell ())
     170      puts (s);
     171    exit (0);
     172  }
     173  #endif