Logo Search packages:      
Sourcecode: sash version File versions  Download package

cmd_ls.c

/*
 * Copyright (c) 2002 by David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * The "ls" built-in command.
 */

#include "sash.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>


#define     LISTSIZE    8192


#ifdef      S_ISLNK
#define     LSTAT lstat
#else
#define     LSTAT stat
#endif


/*
 * Flags for the LS command.
 */
#define     LSF_LONG    0x01
#define     LSF_DIR           0x02
#define     LSF_INODE   0x04
#define     LSF_MULT    0x08
#define     LSF_FLAG    0x10
#define     LSF_COLUMN  0x20


/*
 * Data holding list of files.
 */
static      char **     list;
static      int   listSize;
static      int   listUsed;


/*
 * Local procedures.
 */
static      void  listFile(
      const char *            name,
      const struct stat *     statBuf,
      int               flags,
      int               width
);

static      BOOL  addListName(const char * fileName);
static      void  listAllFiles(int flags, int displayWidth);
static      void  clearListNames(void);


void
do_ls(int argc, const char ** argv)
{
      const char *      cp;
      const char *      name;
      int         flags;
      int         i;
      int         displayWidth;
      BOOL        endSlash;
      DIR *       dirp;
      struct dirent *   dp;
      char        fullName[PATH_LEN];
      struct      stat  statBuf;

      static const char *     def[] = {"."};

      /*
       * Reset for a new listing run.
       */
      clearListNames();

      displayWidth = 0;
      flags = 0;

      /*
       * Handle options.
       */
      argc--;
      argv++;

      while ((argc > 0) && (**argv == '-'))
      {
            cp = *argv++ + 1;
            argc--;

            while (*cp) switch (*cp++)
            {
                  case 'l':   flags |= LSF_LONG; break;
                  case 'd':   flags |= LSF_DIR; break;
                  case 'i':   flags |= LSF_INODE; break;
                  case 'F':   flags |= LSF_FLAG; break;
                  case 'C':   flags |= LSF_COLUMN; break;

                  default:
                        fprintf(stderr, "Unknown option -%c\n", cp[-1]);

                        return;
            }
      }

      /*
       * If long listing is specified then turn off column listing.
       */
      if (flags & LSF_LONG)
            flags &= ~LSF_COLUMN;

      /*
       * If column listing is specified then calculate the maximum
       * width available for the columns of file names.
       * This is settable using the COLS environment variable.
       */
      if (flags & LSF_COLUMN)
      {
            name = getenv("COLS");

            if (name)
                  displayWidth = atoi(name);

            if (displayWidth <= 0)
                  displayWidth = 80;
      }

      /*
       * If no arguments are given set up to show the current directory.
       */
      if (argc <= 0)
      {
            argc = 1;
            argv = def;
      }

      if (argc > 1)
            flags |= LSF_MULT;

      /*
       * Make one pass over the file names to collect together
       * all of the files which are not directories.
       * We will process them all as one list.
       */
      for (i = 0; i < argc; i++)
      {
            if ((flags & LSF_DIR) || !isDirectory(argv[i]))
            {
                  if (!addListName(argv[i]))
                        return;
            }
      }

      /*
       * List those file names, and then clear the list.
       */
      listAllFiles(flags, displayWidth);
      clearListNames();

      /*
       * If directories were being listed as themselves, then we are done.
       */
      if (flags & LSF_DIR)
            return;

      /*
       * Now iterate over the file names processing the directories.
       */
      while (!intFlag && (argc-- > 0))
      {
            name = *argv++;
            endSlash = (*name && (name[strlen(name) - 1] == '/'));

            if (LSTAT(name, &statBuf) < 0)
            {
                  perror(name);

                  continue;
            }

            /*
             * If this file name is not a directory, then ignore it.
             */
            if (!S_ISDIR(statBuf.st_mode))
                  continue;

            /*
             * Collect all the files in the directory.
             */
            dirp = opendir(name);

            if (dirp == NULL)
            {
                  perror(name);

                  continue;
            }

            if (flags & LSF_MULT)
                  printf("\n%s:\n", name);

            while (!intFlag && ((dp = readdir(dirp)) != NULL))
            {
                  fullName[0] = '\0';

                  if ((*name != '.') || (name[1] != '\0'))
                  {
                        strcpy(fullName, name);

                        if (!endSlash)
                              strcat(fullName, "/");
                  }

                  strcat(fullName, dp->d_name);

                  /*
                   * Save the file name in the list.
                   */
                  if (!addListName(fullName))
                  {
                        closedir(dirp);

                        return;
                  }
            }

            closedir(dirp);

            /*
             * List the files we collected in this directory,
             * and then clear the list.
             */
            listAllFiles(flags, displayWidth);
            clearListNames();
      }
}


/*
 * List all of the files in the current list of files.
 * The files are displayed according to the specified flags,
 * in the specified display width.
 */
static void
listAllFiles(int flags, int displayWidth)
{
      const char *      name;
      const char *      cp;
      int         fileWidth;
      int         column;
      int         len;
      int         i;
      struct stat statBuf;

      /*
       * Initialise width data until we need it.
       */
      fileWidth = 0;
      column = 0;

      /*
       * Sort the files in the list.
       */
      qsort((void *) list, listUsed, sizeof(char *), nameSort);

      /*
       * If we are showing the files in columns then calculate the
       * maximum width of all of the file names, taking into account
       * various factors.
       */
      if (flags & LSF_COLUMN)
      {
            for (i = 0; i < listUsed; i++)
            {
                  len = strlen(list[i]);

                  if (fileWidth < len)
                        fileWidth = len;
            }

            if (flags & LSF_FLAG)
                  fileWidth++;

            if (flags & LSF_INODE)
                  fileWidth += 8;

            fileWidth += 2;
      }

      /*
       * Now list the fileNames.
       */
      for (i = 0; i < listUsed; i++)
      {
            name = list[i];

            if (LSTAT(name, &statBuf) < 0)
            {
                  perror(name);

                  continue;
            }

            cp = strrchr(name, '/');

            if (cp)
                  cp++;
            else
                  cp = name;

            /*
             * List the file in the next column or at the end
             * of a line depending on the width left.
             */
            if (column + fileWidth * 2 >= displayWidth)
            {
                  listFile(cp, &statBuf, flags, 0);
                  column = 0;
            }
            else
            {
                  listFile(cp, &statBuf, flags, fileWidth);
                  column += fileWidth;
            }
      }

      /*
       * Terminate the last file name if necessary.
       */
      if (column > 0)
            fputc('\n', stdout);
}


/*
 * Do a listing of a particular file name according to the flags.
 * The output is shown within the specified width if it is nonzero,
 * or on its own line if the width is zero.
 */
static void
listFile(
      const char *            name,
      const struct stat *     statBuf,
      int               flags,
      int               width
)
{
      char *            cp;
      struct passwd *   pwd;
      struct group *    grp;
      int         len;
      int         mode;
      int         flagChar;
      int         usedWidth;
      char        buf[PATH_LEN];
      static      char  userName[12];
      static      int   userId;
      static      BOOL  userIdKnown;
      static      char  groupName[12];
      static      int   groupId;
      static      BOOL  groupIdKnown;

      mode = statBuf->st_mode;

      /*
       * Initialise buffers for use.
       */
      cp = buf;
      buf[0] = '\0';
      flagChar = '\0';

      /*
       * Show the inode number if requested.
       */
      if (flags & LSF_INODE)
      {
            sprintf(cp, "%7ld ", statBuf->st_ino);
            cp += strlen(cp);
      }

      /*
       * Create the long status line if requested.
       */
      if (flags & LSF_LONG)
      {
            strcpy(cp, modeString(mode));
            cp += strlen(cp);

            sprintf(cp, "%3d ", statBuf->st_nlink);
            cp += strlen(cp);

            if (!userIdKnown || (statBuf->st_uid != userId))
            {
                  pwd = getpwuid(statBuf->st_uid);

                  if (pwd)
                        strcpy(userName, pwd->pw_name);
                  else
                        sprintf(userName, "%d", statBuf->st_uid);

                  userId = statBuf->st_uid;
                  userIdKnown = TRUE;
            }

            sprintf(cp, "%-8s ", userName);
            cp += strlen(cp);

            if (!groupIdKnown || (statBuf->st_gid != groupId))
            {
                  grp = getgrgid(statBuf->st_gid);

                  if (grp)
                        strcpy(groupName, grp->gr_name);
                  else
                        sprintf(groupName, "%d", statBuf->st_gid);

                  groupId = statBuf->st_gid;
                  groupIdKnown = TRUE;
            }

            sprintf(cp, "%-8s ", groupName);
            cp += strlen(cp);

            if (S_ISBLK(mode) || S_ISCHR(mode))
            {
                  sprintf(cp, "%3lu, %3lu ",
                        ((unsigned long) statBuf->st_rdev) >> 8,
                        ((unsigned long) statBuf->st_rdev) & 0xff);
            }
            else
                  sprintf(cp, "%8ld ", statBuf->st_size);

            cp += strlen(cp);

            sprintf(cp, " %-12s ", timeString(statBuf->st_mtime));
      }

      /*
       * Set the special character if the file is a directory or
       * symbolic link or executable and the display was requested.
       */
      if (flags & LSF_FLAG)
      {
            if (S_ISDIR(mode))
                  flagChar = '/';
#ifdef S_ISLNK
            else if (S_ISLNK(mode))
                  flagChar = '@';
#endif
            else if ((mode & 0111) != 0)
                  flagChar = '*';
      }

      /*
       * Print the status info followed by the file name.
       */
      fputs(buf, stdout);
      fputs(name, stdout);

      if (flagChar)
            fputc(flagChar, stdout);

      /*
       * Calculate the width used so far.
       */
      usedWidth = strlen(buf) + strlen(name);

      if (flagChar)
            usedWidth++;

      /*
       * Show where a symbolic link points.
       */
#ifdef      S_ISLNK
      if ((flags & LSF_LONG) && S_ISLNK(mode))
      {
            len = readlink(name, buf, PATH_LEN - 1);

            if (len >= 0)
            {
                  buf[len] = '\0';
                  printf(" -> %s", buf);
            }

            usedWidth += strlen(buf) + 4;
      }
#endif

      /*
       * If no width was given then just end the line with a newline.
       */
      if (width == 0)
      {
            fputc('\n', stdout);

            return;
      }

      /*
       * There is a width given.
       * Print as many spaces as it takes to reach that width.
       */
      while (usedWidth++ < width)
            fputc(' ', stdout);
}


/*
 * Save a file name to the end of the static list, reallocating if necessary.
 * The file name is copied into allocated memory owned by the list.
 * Returns TRUE on success.
 */
static BOOL
addListName(const char * fileName)
{
      char **     newList;

      /*
       * Reallocate the list if necessary.
       */
      if (listUsed >= listSize)
      {
            newList = realloc(list,
                  ((sizeof(char **)) * (listSize + LISTSIZE)));

            if (newList == NULL)
            {
                  fprintf(stderr, "No memory for file name buffer\n");

                  return FALSE;
            }

            list = newList;
            listSize += LISTSIZE;
      }

      /*
       * Copy the file name into the next entry.
       */
      list[listUsed] = strdup(fileName);

      if (list[listUsed] == NULL)
      {
            fprintf(stderr, "No memory for file name\n");

            return FALSE;
      }

      /*
       * Increment the amount of space used.
       */
      listUsed++;

      return TRUE;
}


/*
 * Free all of the names from the list of file names.
 */
static void
clearListNames(void)
{
      while (listUsed > 0)
      {
            listUsed--;

            free(list[listUsed]);
      }
}

/* END CODE */

Generated by  Doxygen 1.6.0   Back to index