| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 | /* Test the GNU extensions in glob which allow the user to provide callbacks   for the filesystem access functions.   Copyright (C) 2001-2002 Free Software Foundation, Inc.   This file is part of the GNU C Library.   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.   The GNU C Library is free software; you can redistribute it and/or   modify it under the terms of the GNU Lesser General Public   License as published by the Free Software Foundation; either   version 2.1 of the License, or (at your option) any later version.   The GNU C Library is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   Lesser General Public License for more details.   You should have received a copy of the GNU Lesser General Public   License along with the GNU C Library; if not, see   <http://www.gnu.org/licenses/>.  */#include <dirent.h>#include <errno.h>#include <error.h>#include <glob.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>// #define DEBUG#ifdef DEBUG# define PRINTF(fmt, args...) printf (fmt, ##args)#else# define PRINTF(fmt, args...)#endif#ifdef GLOB_ALTDIRFUNCstatic struct{  const char *name;  int level;  int type;} filesystem[] ={  { ".", 1, DT_DIR },  { "..", 1, DT_DIR },  { "file1lev1", 1, DT_REG },  { "file2lev1", 1, DT_UNKNOWN },  { "dir1lev1", 1, DT_UNKNOWN },    { ".", 2, DT_DIR },    { "..", 2, DT_DIR },    { "file1lev2", 2, DT_REG },    { "dir1lev2", 2, DT_DIR },      { ".", 3, DT_DIR },      { "..", 3, DT_DIR },    { "dir2lev2", 2, DT_DIR },      { ".", 3, DT_DIR },      { "..", 3, DT_DIR },      { ".foo", 3, DT_REG },      { "dir1lev3", 3, DT_DIR },        { ".", 4, DT_DIR },        { "..", 4, DT_DIR },        { "file1lev4", 4, DT_REG },      { "file1lev3", 3, DT_REG },      { "file2lev3", 3, DT_REG },    { "file2lev2", 2, DT_REG },    { "file3lev2", 2, DT_REG },    { "dir3lev2", 2, DT_DIR },      { ".", 3, DT_DIR },      { "..", 3, DT_DIR },      { "file3lev3", 3, DT_REG },      { "file4lev3", 3, DT_REG },  { "dir2lev1", 1, DT_DIR },    { ".", 2, DT_DIR },    { "..", 2, DT_DIR },    { "dir1lev2", 2, DT_UNKNOWN },      { ".", 3, DT_DIR },      { "..", 3, DT_DIR },      { ".foo", 3, DT_REG },      { ".dir", 3, DT_DIR },        { ".", 4, DT_DIR },        { "..", 4, DT_DIR },        { "hidden", 4, DT_REG }};#define nfiles (sizeof (filesystem) / sizeof (filesystem[0]))typedef struct{  int level;  int idx;  struct dirent d;  char room_for_dirent[NAME_MAX];} my_DIR;static long intfind_file (const char *s){  int level = 1;  long int idx = 0;  if (strcmp (s, ".") == 0)    return 0;  if (s[0] == '.' && s[1] == '/')    s += 2;  while (*s != '\0')    {      char *endp = strchrnul (s, '/');      PRINTF ("looking for %.*s, level %d\n", (int) (endp - s), s, level);      while (idx < nfiles && filesystem[idx].level >= level)	{	  if (filesystem[idx].level == level	      && memcmp (s, filesystem[idx].name, endp - s) == 0	      && filesystem[idx].name[endp - s] == '\0')	    break;	  ++idx;	}      if (idx == nfiles || filesystem[idx].level < level)	{	  errno = ENOENT;	  return -1;	}      if (*endp == '\0')	return idx + 1;      if (filesystem[idx].type != DT_DIR	  && (idx + 1 >= nfiles	      || filesystem[idx].level >= filesystem[idx + 1].level))	{	  errno = ENOTDIR;	  return -1;	}      ++idx;      s = endp + 1;      ++level;    }  errno = ENOENT;  return -1;}static void *my_opendir (const char *s){  long int idx = find_file (s);  my_DIR *dir;  if (idx == -1)    {      PRINTF ("my_opendir(\"%s\") == NULL\n", s);      return NULL;    }  dir = (my_DIR *) malloc (sizeof (my_DIR));  if (dir == NULL)    error (EXIT_FAILURE, errno, "cannot allocate directory handle");  dir->level = filesystem[idx].level;  dir->idx = idx;  PRINTF ("my_opendir(\"%s\") == { level: %d, idx: %ld }\n",	  s, filesystem[idx].level, idx);  return dir;}static struct dirent *my_readdir (void *gdir){  my_DIR *dir = gdir;  if (dir->idx == -1)    {      PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",	      dir->level, (long int) dir->idx);      return NULL;    }  while (dir->idx < nfiles && filesystem[dir->idx].level > dir->level)    ++dir->idx;  if (dir->idx == nfiles || filesystem[dir->idx].level < dir->level)    {      dir->idx = -1;      PRINTF ("my_readdir ({ level: %d, idx: %ld }) = NULL\n",	      dir->level, (long int) dir->idx);      return NULL;    }  dir->d.d_ino = dir->idx;#ifdef _DIRENT_HAVE_D_TYPE  dir->d.d_type = filesystem[dir->idx].type;#endif  strcpy (dir->d.d_name, filesystem[dir->idx].name);#ifdef _DIRENT_HAVE_D_TYPE  PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_type: %d, d_name: \"%s\" }\n",	  dir->level, (long int) dir->idx, dir->d.d_ino, dir->d.d_type,	  dir->d.d_name);#else  PRINTF ("my_readdir ({ level: %d, idx: %ld }) = { d_ino: %ld, d_name: \"%s\" }\n",	  dir->level, (long int) dir->idx, dir->d.d_ino,	  dir->d.d_name);#endif  ++dir->idx;  return &dir->d;}static voidmy_closedir (void *dir){  PRINTF ("my_closedir ()\n");  free (dir);}/* We use this function for lstat as well since we don't have any.  */static intmy_stat (const char *name, struct stat *st){  long int idx = find_file (name);  if (idx == -1)    {      PRINTF ("my_stat (\"%s\", ...) = -1 (%s)\n", name, strerror (errno));      return -1;    }  memset (st, '\0', sizeof (*st));  if (filesystem[idx].type == DT_UNKNOWN)    st->st_mode = DTTOIF (idx + 1 < nfiles			  && filesystem[idx].level < filesystem[idx + 1].level			  ? DT_DIR : DT_REG) | 0777;  else    st->st_mode = DTTOIF (filesystem[idx].type) | 0777;  PRINTF ("my_stat (\"%s\", { st_mode: %o }) = 0\n", name, st->st_mode);  return 0;}static const char *glob_errstring[] ={  [GLOB_NOSPACE] = "out of memory",  [GLOB_ABORTED] = "read error",  [GLOB_NOMATCH] = "no matches found"};#define nglob_errstring (sizeof (glob_errstring) / sizeof (glob_errstring[0]))static const char *flagstr (int flags){  const char *strs[] =  {    "GLOB_ERR", "GLOB_MARK", "GLOB_NOSORT", "GLOB_DOOFSS", "GLOB_NOCHECK",    "GLOB_APPEND", "GLOB_NOESCAPE", "GLOB_PERIOD", "GLOB_MAGCHAR",    "GLOB_ALTDIRFUNC", "GLOB_BRACE", "GLOB_NOMAGIC", "GLOB_TILDE",    "GLOB_ONLYDIR", "GLOB_TILDECHECK"  };#define nstrs (sizeof (strs) / sizeof (strs[0]))  static char buf[100];  char *cp = buf;  int cnt;  for (cnt = 0; cnt < nstrs; ++cnt)    if (flags & (1 << cnt))      {	flags &= ~(1 << cnt);	if (cp != buf)	  *cp++ = '|';	cp = stpcpy (cp, strs[cnt]);      }  if (flags != 0)    {      if (cp != buf)	*cp++ = '|';      sprintf (cp, "%#x", flags);    }  return buf;}static inttest_result (const char *fmt, int flags, glob_t *gl, const char *str[]){  size_t cnt;  int result = 0;  printf ("results for glob (\"%s\", %s)\n", fmt, flagstr (flags));  for (cnt = 0; cnt < gl->gl_pathc && str[cnt] != NULL; ++cnt)    {      int ok = strcmp (gl->gl_pathv[cnt], str[cnt]) == 0;      const char *errstr = "";      if (! ok)	{	  size_t inner;	  for (inner = 0; str[inner] != NULL; ++inner)	    if (strcmp (gl->gl_pathv[cnt], str[inner]) == 0)	      break;	  if (str[inner] == NULL)	    errstr =  ok ? "" : " *** WRONG";	  else	    errstr = ok ? "" : " * wrong position";	  result = 1;	}      printf ("  %s%s\n", gl->gl_pathv[cnt], errstr);    }  puts ("");  if (str[cnt] != NULL || cnt < gl->gl_pathc)    {      puts ("  *** incorrect number of entries");      result = 1;    }  return result;}intmain (void){  glob_t gl;  int errval;  int result = 0;  const char *fmt;  int flags;  memset (&gl, '\0', sizeof (gl));  gl.gl_closedir = my_closedir;  gl.gl_readdir = my_readdir;  gl.gl_opendir = my_opendir;  gl.gl_lstat = my_stat;  gl.gl_stat = my_stat;#define test(a, b, c...) \  fmt = a;								      \  flags = b;								      \  errval = glob (fmt, flags, NULL, &gl);				      \  if (errval != 0)							      \    {									      \      printf ("glob (\"%s\", %s) failed: %s\n", fmt, flagstr (flags),	      \	      errval >= 0 && errval < nglob_errstring			      \	      ? glob_errstring[errval] : "???");			      \      result = 1;							      \    }									      \  else									      \    result |= test_result (fmt, flags, &gl, (const char *[]) { c, NULL })  test ("*/*/*", GLOB_ALTDIRFUNC,	"dir1lev1/dir2lev2/dir1lev3",	"dir1lev1/dir2lev2/file1lev3",	"dir1lev1/dir2lev2/file2lev3",	"dir1lev1/dir3lev2/file3lev3",	"dir1lev1/dir3lev2/file4lev3");  test ("*/*/*", GLOB_ALTDIRFUNC | GLOB_PERIOD,	"dir1lev1/dir1lev2/.",	"dir1lev1/dir1lev2/..",	"dir1lev1/dir2lev2/.",	"dir1lev1/dir2lev2/..",	"dir1lev1/dir2lev2/.foo",	"dir1lev1/dir2lev2/dir1lev3",	"dir1lev1/dir2lev2/file1lev3",	"dir1lev1/dir2lev2/file2lev3",	"dir1lev1/dir3lev2/.",	"dir1lev1/dir3lev2/..",	"dir1lev1/dir3lev2/file3lev3",	"dir1lev1/dir3lev2/file4lev3",	"dir2lev1/dir1lev2/.",	"dir2lev1/dir1lev2/..",	"dir2lev1/dir1lev2/.dir",	"dir2lev1/dir1lev2/.foo");  test ("*/*/.*", GLOB_ALTDIRFUNC,	"dir1lev1/dir1lev2/.",	"dir1lev1/dir1lev2/..",	"dir1lev1/dir2lev2/.",	"dir1lev1/dir2lev2/..",	"dir1lev1/dir2lev2/.foo",	"dir1lev1/dir3lev2/.",	"dir1lev1/dir3lev2/..",	"dir2lev1/dir1lev2/.",	"dir2lev1/dir1lev2/..",	"dir2lev1/dir1lev2/.dir",	"dir2lev1/dir1lev2/.foo");  test ("*1*/*2*/.*", GLOB_ALTDIRFUNC,	"dir1lev1/dir1lev2/.",	"dir1lev1/dir1lev2/..",	"dir1lev1/dir2lev2/.",	"dir1lev1/dir2lev2/..",	"dir1lev1/dir2lev2/.foo",	"dir1lev1/dir3lev2/.",	"dir1lev1/dir3lev2/..",	"dir2lev1/dir1lev2/.",	"dir2lev1/dir1lev2/..",	"dir2lev1/dir1lev2/.dir",	"dir2lev1/dir1lev2/.foo");  test ("*1*/*1*/.*", GLOB_ALTDIRFUNC,	"dir1lev1/dir1lev2/.",	"dir1lev1/dir1lev2/..",	"dir2lev1/dir1lev2/.",	"dir2lev1/dir1lev2/..",	"dir2lev1/dir1lev2/.dir",	"dir2lev1/dir1lev2/.foo");  globfree (&gl);  return result;}#elseint main(void) { return 0; }#endif
 |