		/* Copyright 2000-2004 The Apache Software Foundation
		 *
		 * Licensed under the Apache License, Version 2.0 (the "License");
		 * you may not use this file except in compliance with the License.
		 * You may obtain a copy of the License at
		 *
		 *     http://www.apache.org/licenses/LICENSE-2.0
		 *
		 * Unless required by applicable law or agreed to in writing, software
		 * distributed under the License is distributed on an "AS IS" BASIS,
		 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
		 * See the License for the specific language governing permissions and
		 * limitations under the License.
		 */
		
		#include "apr_arch_file_io.h"
		#include "apr_strings.h"
		#include "apr_portable.h"
		#if APR_HAVE_SYS_SYSLIMITS_H
		#include <sys/syslimits.h>
		#endif
		#if APR_HAVE_LIMITS_H
		#include <limits.h>
		#endif
		
		static apr_status_t dir_cleanup(void *thedir)
           6    {
           6        apr_dir_t *dir = thedir;
           6        if (closedir(dir->dirstruct) == 0) {
           6            return APR_SUCCESS;
		    }
		    else {
      ######            return errno;
		    }
		} 
		
		#define PATH_SEPARATOR '/'
		
		/* Remove trailing separators that don't affect the meaning of PATH. */
		static const char *path_canonicalize (const char *path, apr_pool_t *pool)
           2    {
		    /* At some point this could eliminate redundant components.  For
		     * now, it just makes sure there is no trailing slash. */
           2        apr_size_t len = strlen (path);
           2        apr_size_t orig_len = len;
		    
           2        while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
      ######            len--;
		    
           2        if (len != orig_len)
      ######            return apr_pstrndup (pool, path, len);
		    else
           2            return path;
		}
		
		/* Remove one component off the end of PATH. */
		static char *path_remove_last_component (const char *path, apr_pool_t *pool)
           2    {
           2        const char *newpath = path_canonicalize (path, pool);
           2        int i;
		    
          10        for (i = (strlen(newpath) - 1); i >= 0; i--) {
          10            if (path[i] == PATH_SEPARATOR)
           2                break;
		    }
		
           2        return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
		}
		
		apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname, 
		                          apr_pool_t *pool)
           7    {
		    /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct 
		     * dirent is declared with enough storage for the name.  On other
		     * platforms (e.g., Solaris 8 for Intel), d_name is declared as a
		     * one-byte array.  Note: gcc evaluates this at compile time.
		     */
           7        apr_size_t dirent_size = 
		        (sizeof((*new)->entry->d_name) > 1 ? 
           7             sizeof(struct dirent) : sizeof (struct dirent) + 255);
		
           7        (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
		
           7        (*new)->pool = pool;
           7        (*new)->dirname = apr_pstrdup(pool, dirname);
           7        (*new)->dirstruct = opendir(dirname);
           7        (*new)->entry = apr_pcalloc(pool, dirent_size);
		
           7        if ((*new)->dirstruct == NULL) {
           1            return errno;
		    }    
		    else {
           6            apr_pool_cleanup_register((*new)->pool, (void *)(*new), dir_cleanup,
			                          apr_pool_cleanup_null);
           6            return APR_SUCCESS;
		    }
		}
		
		apr_status_t apr_dir_close(apr_dir_t *thedir)
           6    {
           6        return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
		}
		
		#ifdef DIRENT_TYPE
		static apr_filetype_e filetype_from_dirent_type(int type)
          14    {
          14        switch (type) {
		    case DT_REG:
      ######            return APR_REG;
		    case DT_DIR:
      ######            return APR_DIR;
		    case DT_LNK:
      ######            return APR_LNK;
		    case DT_CHR:
      ######            return APR_CHR;
		    case DT_BLK:
      ######            return APR_BLK;
		#if defined(DT_FIFO)
		    case DT_FIFO:
      ######            return APR_PIPE;
		#endif
		#if !defined(BEOS) && defined(DT_SOCK)
		    case DT_SOCK:
      ######            return APR_SOCK;
		#endif
		    default:
          14            return APR_UNKFILE;
		    }
		}
		#endif
		
		apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
		                          apr_dir_t *thedir)
          17    {
          17        apr_status_t ret = 0;
		#ifdef DIRENT_TYPE
          17        apr_filetype_e type;
		#endif
		#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
		                    && !defined(READDIR_IS_THREAD_SAFE)
          17        struct dirent *retent;
		
          17        ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
		
		    /* Avoid the Linux problem where at end-of-directory thedir->entry
		     * is set to NULL, but ret = APR_SUCCESS.
		     */
          17        if(!ret && thedir->entry != retent)
           3            ret = APR_ENOENT;
		
		    /* Solaris is a bit strange, if there are no more entries in the
		     * directory, it returns EINVAL.  Since this is against POSIX, we
		     * hack around the problem here.  EINVAL is possible from other
		     * readdir implementations, but only if the result buffer is too small.
		     * since we control the size of that buffer, we should never have
		     * that problem.
		     */
          17        if (ret == EINVAL) {
      ######            ret = ENOENT;
		    }
		#else
		    /* We're about to call a non-thread-safe readdir() that may
		       possibly set `errno', and the logic below actually cares about
		       errno after the call.  Therefore we need to clear errno first. */
		    errno = 0;
		    thedir->entry = readdir(thedir->dirstruct);
		    if (thedir->entry == NULL) {
		        /* If NULL was returned, this can NEVER be a success. Can it?! */
		        if (errno == APR_SUCCESS) {
		            ret = APR_ENOENT;
		        }
		        else
		            ret = errno;
		    }
		#endif
		
		    /* No valid bit flag to test here - do we want one? */
          17        finfo->fname = NULL;
		
          17        if (ret) {
           3            finfo->valid = 0;
           3            return ret;
		    }
		
		#ifdef DIRENT_TYPE
          14        type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
          14        if (type != APR_UNKFILE) {
      ######            wanted &= ~APR_FINFO_TYPE;
		    }
		#endif
		#ifdef DIRENT_INODE
          14        if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
          14            wanted &= ~APR_FINFO_INODE;
		    }
		#endif
		
          14        wanted &= ~APR_FINFO_NAME;
		
          14        if (wanted)
		    {
           2            char fspec[APR_PATH_MAX];
           2            int off;
           2            apr_cpystrn(fspec, thedir->dirname, sizeof(fspec));
           2            off = strlen(fspec);
           2            if ((fspec[off - 1] != '/') && (off + 1 < sizeof(fspec)))
           2                fspec[off++] = '/';
           2            apr_cpystrn(fspec + off, thedir->entry->d_name, sizeof(fspec) - off);
           2            ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
		        /* We passed a stack name that will disappear */
           2            finfo->fname = NULL;
		    }
		
          14        if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
           2            wanted &= ~finfo->valid;
		    }
		    else {
		        /* We don't bail because we fail to stat, when we are only -required-
		         * to readdir... but the result will be APR_INCOMPLETE
		         */
          12            finfo->pool = thedir->pool;
          12            finfo->valid = 0;
		#ifdef DIRENT_TYPE
          12            if (type != APR_UNKFILE) {
      ######                finfo->filetype = type;
      ######                finfo->valid |= APR_FINFO_TYPE;
		        }
		#endif
		#ifdef DIRENT_INODE
          12            if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
          12                finfo->inode = thedir->entry->DIRENT_INODE;
          12                finfo->valid |= APR_FINFO_INODE;
		        }
		#endif
		    }
		
          14        finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
          14        finfo->valid |= APR_FINFO_NAME;
		
          14        if (wanted)
      ######            return APR_INCOMPLETE;
		
          14        return APR_SUCCESS;
		}
		
		apr_status_t apr_dir_rewind(apr_dir_t *thedir)
           1    {
           1        rewinddir(thedir->dirstruct);
           1        return APR_SUCCESS;
		}
		
		apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm, 
		                          apr_pool_t *pool)
          10    {
          10        mode_t mode = apr_unix_perms2mode(perm);
		
          10        if (mkdir(path, mode) == 0) {
           7            return APR_SUCCESS;
		    }
		    else {
           3            return errno;
		    }
		}
		
		apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
		                                           apr_pool_t *pool) 
           3    {
           3        apr_status_t apr_err = 0;
		    
           3        apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
		    
           3        if (apr_err == EEXIST) /* It's OK if PATH exists */
      ######            return APR_SUCCESS;
		    
           3        if (apr_err == ENOENT) { /* Missing an intermediate dir */
           2            char *dir;
		        
           2            dir = path_remove_last_component(path, pool);
           2            apr_err = apr_dir_make_recursive(dir, perm, pool);
		        
           2            if (!apr_err) 
           2                apr_err = apr_dir_make (path, perm, pool);
		    }
		
           3        return apr_err;
		}
		
		apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
          10    {
          10        if (rmdir(path) == 0) {
           7            return APR_SUCCESS;
		    }
		    else {
           3            return errno;
		    }
		}
		
		apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
      ######    {
      ######        if (dir == NULL) {
      ######            return APR_ENODIR;
		    }
      ######        *thedir = dir->dirstruct;
      ######        return APR_SUCCESS;
		}
		
		apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
		                          apr_pool_t *pool)
      ######    {
      ######        if ((*dir) == NULL) {
      ######            (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
      ######            (*dir)->pool = pool;
		    }
      ######        (*dir)->dirstruct = thedir;
      ######        return APR_SUCCESS;
		}
		
		  
