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

cmd_find.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 "find" built-in command.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>

#include "sash.h"


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


#define     MAX_NAME_SIZE     (1024 * 10)


/*
 * Items that can be specified to restrict the output.
 */
static      BOOL        xdevFlag;
static      dev_t       xdevDevice;
static      long        fileSize;
static      const char *      filePattern;
static      const char *      fileType;


/*
 * Recursive routine to examine the files in a directory.
 */
static      void  examineDirectory(const char * path);
static      BOOL  testFile(const char * fullName, const struct stat * statBuf);



/*
 * Find files from the specified directory path.
 * This is limited to just printing their file names.
 */
void
do_find(int argc, const char ** argv)
{
      const char *      cp;
      const char *      path;
      struct stat statBuf;

      argc--;
      argv++;

      xdevFlag = FALSE;
      fileType = NULL;
      filePattern = NULL;
      fileSize = 0;

      if ((argc <= 0) || (**argv == '-'))
      {
            fprintf(stderr, "No path specified\n");

            return;
      }

      path = *argv++;
      argc--;

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

            if (strcmp(cp, "-xdev") == 0)
                  xdevFlag = TRUE;
            else if (strcmp(cp, "-type") == 0)
            {
                  if ((argc <= 0) || (**argv == '-'))
                  {
                        fprintf(stderr, "Missing type string\n");

                        return;
                  }

                  argc--;
                  fileType = *argv++;
            }
            else if (strcmp(cp, "-name") == 0)
            {
                  if ((argc <= 0) || (**argv == '-'))
                  {
                        fprintf(stderr, "Missing file name\n");

                        return;
                  }

                  argc--;
                  filePattern = *argv++;
            }
            else if (strcmp(cp, "-size") == 0)
            {
                  if ((argc <= 0) || (**argv == '-'))
                  {
                        fprintf(stderr, "Missing file size\n");

                        return;
                  }

                  argc--;
                  cp = *argv++;

                  fileSize = 0;

                  while (isDecimal(*cp))
                        fileSize = fileSize * 10 + (*cp++ - '0');

                  if (*cp || (fileSize < 0))
                  {
                        fprintf(stderr, "Bad file size specified\n");

                        return;
                  }
            }
            else
            {
                  if (*cp != '-')
                        fprintf(stderr, "Missing dash in option\n");
                  else
                        fprintf(stderr, "Unknown option\n");

                  return;
            }
      }

      /*
       * Get information about the path and make sure that it
       * is a directory.
       */
      if (stat(path, &statBuf) < 0)
      {
            fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
                  strerror(errno));

            return;
      }

      if (!S_ISDIR(statBuf.st_mode))
      {
            fprintf(stderr, "Path \"%s\" is not a directory\n", path);

            return;
      }

      /*
       * Remember the device that this directory is on in case we need it.
       */
      xdevDevice = statBuf.st_dev;

      /*
       * If the directory meets the specified criteria, then print it out.
       */
      if (testFile(path, &statBuf))
            printf("%s\n", path);

      /*
       * Now examine the files in the directory.
       */
      examineDirectory(path);
}


/*
 * Recursive routine to examine the files in a directory.
 */
static void
examineDirectory(const char * path)
{
      DIR *       dir;
      BOOL        needSlash;
      struct dirent *   entry;
      struct stat statBuf;
      char        fullName[MAX_NAME_SIZE];

      /*
       * Open the directory.
       */
      dir = opendir(path);

      if (dir == NULL)
      {
            fprintf(stderr, "Cannot read directory \"%s\": %s\n",
                  path, strerror(errno));

            return;
      }

      /*
       * See if a slash is needed.
       */
      needSlash = (*path && (path[strlen(path) - 1] != '/'));

      /*
       * Read all of the directory entries and check them,
       * except for the current and parent directory entries.
       */
      while (!intFlag && ((entry = readdir(dir)) != NULL))
      {
            if ((strcmp(entry->d_name, ".") == 0) ||
                  (strcmp(entry->d_name, "..") == 0))
            {
                  continue;
            }

            /*
             * Build the full path name.
             */
            strcpy(fullName, path);

            if (needSlash)
                  strcat(fullName, "/");

            strcat(fullName, entry->d_name);

            /*
             * Find out about this file.
             */
            if (LSTAT(fullName, &statBuf) < 0)
            {
                  fprintf(stderr, "Cannot stat \"%s\": %s\n",
                        fullName, strerror(errno));

                  continue;
            }

            /*
             * If this file matches the criteria that was
             * specified then print its name out.
             */
            if (testFile(fullName, &statBuf))
                  printf("%s\n", fullName);

            /*
             * If this is a directory and we are allowed to cross
             * mount points or the directory is still on the same
             * device, then examine it's files too.
             */
            if (S_ISDIR(statBuf.st_mode) &&
                  (!xdevFlag || (statBuf.st_dev == xdevDevice)))
            {
                  examineDirectory(fullName);
            }
      }

      closedir(dir);
}


/*
 * Test a file name having the specified status to see if it should
 * be acted on.  Returns TRUE if the file name has been selected.
 */
static BOOL
testFile(const char * fullName, const struct stat * statBuf)
{
      const char *      cp;
      const char *      entryName;
      BOOL        wantType;
      int         mode;

      mode = statBuf->st_mode;

      /*
       * Check the file type if it was specified.
       */
      if (fileType != NULL)
      {
            wantType = FALSE;

            for (cp = fileType; *cp; cp++)
            {
                  switch (*cp)
                  {
                        case 'f':
                              if (S_ISREG(mode))
                                    wantType = TRUE;
                              break;

                        case 'd':
                              if (S_ISDIR(mode))
                                    wantType = TRUE;

                              break;

                        case 'p':
                              if (S_ISFIFO(mode))
                                    wantType = TRUE;

                              break;

                        case 'c':
                              if (S_ISCHR(mode))
                                    wantType = TRUE;

                              break;

                        case 'b':
                              if (S_ISBLK(mode))
                                    wantType = TRUE;

                              break;

                        case 's':
                              if (S_ISSOCK(mode))
                                    wantType = TRUE;

                              break;

#ifdef      S_ISLNK
                        case 'l':
                              if (S_ISLNK(mode))
                                    wantType = TRUE;

                              break;
#endif
                        default:
                              break;
                  }
            }

            if (!wantType)
                  return FALSE;
      }

      /*
       * Check the file size if it was specified.
       * This check only lets regular files and directories through.
       */
      if (fileSize > 0)
      {
            if (!S_ISREG(mode) && !S_ISDIR(mode))
                  return FALSE;

            if (statBuf->st_size < fileSize)
                  return FALSE;
      }

      /*
       * Check the file name pattern if it was specified.
       */
      if (filePattern != NULL)
      {
            entryName = strrchr(fullName, '/');

            if (entryName)
                  entryName++;
            else
                  entryName = fullName;

            if (!match(entryName, filePattern))
                  return FALSE;
      }

      /*
       * This file name is wanted.
       */
      return TRUE;
}

/* END CODE */

Generated by  Doxygen 1.6.0   Back to index