scandir.c

来自「<Win2k系统编程>源码.次数为国人自编,内容丰富,还是不错的.」· C语言 代码 · 共 1,259 行 · 第 1/3 页

C
1,259
字号


/****************************** Module Header *******************************
* Module Name: SCANDIR.C
*
* Scan a directory tree and build a sorted list of filenames within that
* tree.
*
* Functions:
*
* dir_buildlist()
* dir_delete()
* dir_isfile()
* dir_firstitem()
* dir_nextitem()
* dir_findnextfile()
* dir_getrelname()
* dir_getfullname()
* dir_getroot_list()
* dir_getroot_item()
* dir_freerelname()
* dir_freefullname()
* dir_freeroot_list()
* dir_freerootitem()
* dir_getopenname()
* dir_freeopenname()
* dir_openfile()
* dir_closefile()
* dir_filesize()
* dir_startcopy()
* dir_endcopy()
* dir_copy()
* dir_finalelem()
* dir_cleardirect()
* dir_adddirect()
* dir_addfile()
* dir_scan()
* dir_isvaliddir()
* dir_isvalidfile()
* dir_fileinit()
* dir_dirinit()
* dir_getpathsize()
* dir_findnextfile()
*
* Comments:
*
* The call dir_buildlist takes a pathname and returns a handle. Subsequent
* calls to dir_firstitem and dir_nextitem return handles to
* items within the list, from which you can get the name of the
* file (relative to the original pathname, or complete), and filesize.
*
* The list can be either built entirely during the build call, or
* built one directory at a time as required by dir_nextitem calls. This
* option affects only relative performance, and is taken as a
* recommendation only (ie some of the time we will ignore the flag).
*
* The list is ordered alphabetically (case-insensitive using lstrcmpi).
* within any one directory, we list filenames before going on
* to subdirectory contents.
*
* All memory is allocated from a gmem_* heap hHeap declared
* and initialised elsewhere.
*
* The caller gets handles to two things: a DIRLIST, representing the
* entire list of filenames, and a DIRITEM: one item within the list.
*
* From the DIRITEM he can get the filename (including or excluding the
* tree root passed to dir_build*) - and also he can get to the next
* DIRITEM.
*
* We permit lazy building of the tree (usually so the caller can keep
* the user-interface up-to-date as we go along). In this case,
* we need to store information about how far we have scanned and
* what is next to do. We need to scan an entire directory at a time and then
* sort it so we can return files in the correct order.
*
* We scan an entire directory and store it in a DIRECT struct. This contains
* a list of DIRITEMs for the files in the current directory, and a list of
* DIRECTs for the subdirectories (possible un-scanned).
*
* dir_nextitem will use the list functions to get the next DIRITEM on the list.
* When the end of the list is reached, it will use the backpointer back to the
* DIRECT struct to find the next directory to scan.
*
****************************************************************************/

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <direct.h>

#include "gutils.h"
#include "list.h"
#include "scandir.h"
#include "windiff.h"
#include "wdiffrc.h"

/*
 * Hold name and information about a given file (one ITEM in a DIRectory)
 * caller's DIRITEM handle is a pointer to one of these structures
 */
struct diritem {
        LPSTR name;             /* ptr to filename (final element only) */
        long size;              /* filesize */
        struct direct FAR * direct; /* containing directory */
        LPSTR localname;        /* name of temp copy of file */
        BOOL bLocalIsTemp;      /* true if localname is tempfile.
                                 */
};


/* DIRECT: Hold state about directory and current position in list of filenames.
 */
typedef struct direct {
        LPSTR relname;          /* name of dir relative to DIRLIST root */
        DIRLIST head;           /* back ptr (to get fullname) */
        struct direct FAR * parent; /* parent directory (NULL if above tree root)*/

        BOOL bScanned;          /* TRUE if scanned */
        LIST diritems;          /* list of DIRITEMs for files in cur. dir */
        LIST directs;           /* list of DIRECTs for child dirs */

        int pos;                /* where are we begin, files, dirs */
        struct direct FAR * curdir; /* subdir being scanned (ptr to list element)*/
} FAR * DIRECT;

/* Values for direct.pos */
#define DL_FILES        1       /* reading files from the diritems */
#define DL_DIRS         2       /* in the dirs: List_Next on curdir */


/*
 * The DIRLIST handle returned from a build function is in fact
 * a pointer to one of these
 */
struct dirlist {

        char rootname[256];        /* name of root of tree */
        BOOL bFile;             /* TRUE if root of tree is file, not dir */
        DIRECT dot;             /* dir  for '.' - for tree root dir */
};

extern BOOL bAbort;             /* from windiff.c (read only here). */


/* ------ memory allocation ---------------------------------------------*/

/* All memory is allocated from a heap created by the application */
extern HANDLE hHeap;

/*-- forward declaration of internal functions ---------------------------*/

LPSTR dir_finalelem(LPSTR path);
void dir_cleardirect(DIRECT dir);
void dir_adddirect(DIRECT dir, LPSTR path);
void dir_addfile(DIRECT dir, LPSTR path, DWORD size);
void dir_scan(DIRECT dir, BOOL bRecurse);
BOOL dir_isvaliddir(LPSTR path);
BOOL dir_isvalidfile(LPSTR path);
void dir_fileinit(DIRITEM pfile, DIRECT dir, LPSTR path, long size);
void dir_dirinit(DIRECT dir, DIRLIST head, DIRECT parent, LPSTR name);
long dir_getpathsize(LPSTR path);
DIRITEM dir_findnextfile(DIRLIST dl, DIRECT curdir);



/***************************************************************************
 * Function: dir_buildlist
 *
 * Purpose:
 *
 * Build a list of filenames
 *
 * Optionally build the list on demand, in which case we scan the
 * entire directory but don't recurse into subdirs until needed
 *
 */

DIRLIST
dir_buildlist(LPSTR path, BOOL bOnDemand)
{
        DIRLIST dl;
        BOOL bFile;
        
        /* first check if the path is valid */
        if (dir_isvaliddir(path)) {
                bFile = FALSE;
        } else if (dir_isvalidfile(path)) {
                bFile = TRUE;
        } else {
                /* not valid */
                return(NULL);
        }


        /* alloc and init the DIRLIST head */

        dl = (DIRLIST) gmem_get(hHeap, sizeof(struct dirlist));
        memset(dl, 0, sizeof(struct dirlist));

        /* convert the pathname to an absolute path */

        _fullpath(dl->rootname, path, sizeof(dl->rootname));

        dl->bFile = bFile;
        /* make a '.' directory for the current directory -
         * all files and subdirs will be listed from here
         */
        dl->dot = (DIRECT) gmem_get(hHeap, sizeof(struct direct));
        dir_dirinit(dl->dot, dl, NULL, ".");

        /* were we given a file or a directory ? */
        if (bFile) {
                /* its a file. create a single file entry
                 * and set the state accordingly
                 */
                dl->dot->bScanned = TRUE;

                dir_addfile(dl->dot, dir_finalelem(path),
                                dir_getpathsize(path));

                return(dl);
        }

        /* scan the root directory and return. if we are asked
         * to scan the whole thing, this will cause a recursive
         * scan all the way down the tree
         */
        dir_scan(dl->dot, (!bOnDemand) );

        return(dl);
} /* dir_buildlist */

/***************************************************************************
 * Function: dir_delete
 *
 * Purpose:
 *
 * Free up the DIRLIST and all associated memory 
 */
void
dir_delete(DIRLIST dl)
{
        if (dl == NULL) {
                return;
        }
        dir_cleardirect(dl->dot);
        gmem_free(hHeap, (LPSTR) dl->dot, sizeof(struct direct));


        gmem_free(hHeap, (LPSTR) dl, sizeof(struct dirlist));
}



/***************************************************************************
 * Function: dir_isfile
 *
 * Purpose:
 *
 * Was the original build request a file or a directory ? 
 */
BOOL
dir_isfile(DIRLIST dl)
{
        if (dl == NULL) {
                return(FALSE);
        }

        return(dl->bFile);
}

/***************************************************************************
 * Function: dir_firstitem
 *
 * Purpose:
 *
 * Return the first file in the list, or NULL if no files found.
 * Returns a DIRITEM. This can be used to get filename, size and chcksum.
 * If there are no files in the root, we recurse down until we find a file.
 */
DIRITEM
dir_firstitem(DIRLIST dl)
{
        if (dl == NULL) {
                return(NULL);
        }
        /*
         * reset the state to indicate that no files have been read yet
         */
        dl->dot->pos = DL_FILES;
        dl->dot->curdir = NULL;

        /* now get the next filename */
        return(dir_findnextfile(dl, dl->dot));
} /* dir_firstitem */


/***************************************************************************
 * Function:dir_nextitem
 *
 * Purpose:
 *
 * Get the next filename after the one given.
 *
 * The List_Next function can give us the next element on the list of files.
 * If this is null, we need to go back to the DIRECT and find the
 * next list of files to traverse (in the next subdir).
 *
 * After scanning all the subdirs, return to the parent to scan further
 * dirs that are peers of this, if there are any. If we have reached the end of
 * the tree (no more dirs in dl->dot to scan), return NULL.
 *
 * Don't recurse to lower levels unless fDeep is TRUE
 */
DIRITEM
dir_nextitem(DIRLIST dl, DIRITEM cur, BOOL fDeep)
{
        DIRITEM next;

        if ((dl == NULL) || (cur == NULL)) {
                return(NULL);
        }
        if (bAbort) return NULL;  /* user requested abort */

        if ( (next = List_Next(cur)) != NULL) {
                /* there was another file on this list */
                return(next);
        }
        if (!fDeep) return NULL;

        /* get the head of the next list of filenames from the directory */
        cur->direct->pos = DL_DIRS;
        cur->direct->curdir = NULL;
        return(dir_findnextfile(dl, cur->direct));
} /* dir_nextitem */

/***************************************************************************
 * Function: dir_findnextfile
 *
 * Purpose:
 *
 * Gets the next file in the directory
 */
DIRITEM
dir_findnextfile(DIRLIST dl, DIRECT curdir)
{
        DIRITEM curfile;

        if ((dl == NULL) || (curdir == NULL)) {
                return(NULL);
        }

        /* scan the subdir if necessary */
        if (!curdir->bScanned) {
                dir_scan(curdir, FALSE);
        }

        /* have we already read the files in this directory ? */
        if (curdir->pos == DL_FILES) {
                /* no - return head of file list */
                curfile = (DIRITEM) List_First(curdir->diritems);
                if (curfile != NULL) {
                        return(curfile);
                }

                /* no more files - try the subdirs */
                curdir->pos = DL_DIRS;
        }

        /* try the next subdir on the list, if any */
        /* is this the first or the next */
        if (curdir->curdir == NULL) {
                curdir->curdir = (DIRECT) List_First(curdir->directs);
        } else {
                curdir->curdir = (DIRECT) List_Next(curdir->curdir);
        }
        /* did we find a subdir ? */
        if (curdir->curdir == NULL) {

                /* no more dirs - go back to parent if there is one */
                if (curdir->parent == NULL) {
                        /* no parent - we have exhausted the tree */
                        return(NULL);
                }

                /* reset parent state to indicate this is the current
                 * directory - so that next gets the next after this.
                 * this ensures that multiple callers of dir_nextitem()
                 * to the same tree work.
                 */
                curdir->parent->pos = DL_DIRS;
                curdir->parent->curdir = curdir;

                return(dir_findnextfile(dl, curdir->parent));
        }

        /* there is a next directory - set it to the
         * beginning and get the first file from it
         */
        curdir->curdir->pos = DL_FILES;
        curdir->curdir->curdir = NULL;
        return(dir_findnextfile(dl, curdir->curdir));

} /* dir_findnextfile */


/*-- pathnames ----
 *
 * This module supports two types of pathnames, called relative and full.
 * Relative names are relative to the root passed in the initial call
 * to dir_build*, and full names include the tree root.
 *
 * Note that this is a different distinction to relative vs absolute
 * pathnames, since the tree root may still be either relative or absolute.
 *
 * Examples:
 *
 *  - if you called dir_buildlist("c:\")

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?