## root/clib/dscan.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. dscan_findfirst
2. dscan_findnext
3. dscan_end
4. dscan_ll_debug
5. dscan_find
6. dscan_sub
7. dscan_add_sub
8. dscan_chop_path
9. dscan_match
10. dscan_umatch
11. dscan_set_flags
12. dscan_set_current_path
13. dscan_ll_delete
14. dscan_ll_delete_all
15. dscan_set_mvars
16. dscan_delete_mvars
17. dscan_ll_find
18. dscan_ll_add
19. dscan_header

    /* Directory scanning API.
       Rick Smereka, Copyright (C) 2001-2005.

       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program 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 General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program; if not, get a copy via the Internet at
       http://gnu.org/copyleft/gpl.html or write to the Free Software
       Foundation, Inc., 59 Temple Place, Suite 330, Boston,
       MA 02111-1307 USA

       This directory scanning module/API will traverse one or more directories
       from a specific entry point matching file/directory names. This API is
       multi-platform. It will compile and run in under any OS that supports
       the POSIX 1003.1 directory system calls. This includes 16bit DOS, 32bit
       Windows, QNX (both RtP and 4.x) and all versions of Linix/Unix. This API
       does not use recursion to traverse the directory tree and is not subject to
       stack limitations and excessive memory consumption typical with recursive
       algorithms.

       You can contact the author via email at rsmereka@future-lab.com

       Original version for Unix, QNX, DOS and Windows. Feb/2001, Rick Smereka

       Ported to Debian Linux. Nov/2002, Rick Smereka

       Changed all logging calls from 'log_file_date' to 'logman'.
       Mar/2004, Rick Smereka

       Modified function 'dscan_find' to use the 'lstat' function
       instead of 'stat'. Added detection of symbolic links
       API now returns 'SCAN_TYPE_SYMLINK' when a symbolic link is found.
       Added the global variable 'dscan_for_symlinks'. Added detection
       of the option 'l' for symbolic links. Symbolic link detection
       and return of this type is available only on the QNX/Linux/Unix
       platforms. Jan/2005, Rick Smereka */

    #include "stdhead.h"
    #include "dscan.h"

    #define DSCAN_SUB_DELIM '*'

    struct dscan_entry
       {
       DIR *dir;
       char *dir_path;
       char *subs;
       int sub_count;
       int sub_index;
       struct dscan_entry *next;
       };

    /* private function prototypes */

    static int dscan_find(char *);
    static int dscan_sub(void);
    static void dscan_add_sub(struct dscan_entry *, char *);
    static int dscan_chop_path(void);
    static int dscan_match(char *, char *);
    static int dscan_umatch(char *, char *);
    static int dscan_set_flags(char *);
    static int dscan_set_current_path(char *);
    static void dscan_ll_delete(char *);
    static void dscan_ll_delete_all(void);
    static int dscan_set_mvars(char *, char *);
    static void dscan_delete_mvars(void);
    static struct dscan_entry *dscan_ll_find(char *);
    static struct dscan_entry *dscan_ll_add(char *);
    static void dscan_header(char *);

    /* module globals */

    struct dscan_entry *dscan_list_head = NULL;
    char *dscan_base_path = NULL;
    char *dscan_current_path = NULL;
    char *dscan_fname = NULL;
    int dscan_for_files;
    int dscan_for_dirs;

    #ifdef OS_UNIX
    int dscan_for_symlinks;
    #endif

    int dscan_descend;

    int dscan_findfirst(char *path, char *fname, char *flags, char *pfname)
    {
       /* Find the first occurance of the file 'fname' within the 'path'.
          The 'flags' can have any combination of 'fdls' where 'f' is
          scan for files, 'd' is scan for directories, 'l' is scan for
          symbolic links (QNX/Linux/Unix only) and 's' is descend into
          sub-directories. If an item is found, its full path
          and name is loaded into 'pfname'. Function returns a type
          code (file, directory or symlink) upon success. Function returns
          'FALSE' otherwise. */

       char mname[] = "dscan_findfirst";
       int ret;

       dscan_header(mname);

       if (path == (char *)NULL || !strlen(path))
          {
          logman("%s:exit[0]null or empty[path]", mname);
          return(FALSE);
          }

       if (fname == (char *)NULL || !strlen(fname))
          {
          logman("%s:exit[0]null or empty[fname]", mname);
          return(FALSE);
          }

       if (flags == (char *)NULL || !strlen(flags))
          {
          logman("%s:exit[0]null or empty[flags]", mname);
          return(FALSE);
          }

       if (pfname == (char *)NULL)
          {
          logman("%s:exit[0]null[pfname]", mname);
          return(FALSE);
          }

       if (!isdirectory(path))
          {
          logman("%s:%s is not a directory", mname, path);
          return(FALSE);
          }

       logman("%s:path=%s[%d],fname=%s[%d],flags=%s[%d]",
                     mname, path, strlen(path), fname, strlen(fname),
                     flags, strlen(flags));
       dscan_delete_mvars();

       if (!dscan_set_mvars(path, fname))
          {
          logman("%s:exit[0]bad rc[0] from dscan_set_mvars", mname);
          return(FALSE);
          }

       if (!dscan_set_flags(flags))
          {
          logman("%s:exit[0]bad rc[0] from dscan_set_flags", mname);
          return(FALSE);
          }

       if (dscan_list_head != NULL)
          {
          logman("%s:link list already exists, deleting", mname);
          dscan_ll_delete_all();
          }

       if (!dscan_set_current_path(dscan_base_path))
          {
          logman("%s:exit[0]bad rc[0] from dscan_set_current_path", mname);
          return(FALSE);
          }

       ret = dscan_findnext(pfname);
       logman("%s:exit[%d]:pfname=%s[%d]", mname, ret, pfname,
                     strlen(pfname));
       return(ret);
    }

    int dscan_findnext(char *pfname)
    {
       /* Find the next occurance of the desired file/directory.
          'dscan_findfirst' must be called initially. Function
          returns a type code with the full path and item name
          in 'pfname' upon success. Function returns 'FALSE'
          otherwise. */

       char mname[] = "dscan_findnext";
       int ret;

       logman("%s:enter:cp=%s", mname, dscan_current_path);

       if (pfname == NULL)
          {
          logman("%s:exit[0]null[pfname]", mname);
          return(FALSE);
          }

       pfname[0] = EOS;

       while(TRUE)
          {
          /* find next occurance in current directory */

          if ((ret = dscan_find(pfname)) != FALSE)
             {
             logman("%s:exit[%d]:pfname=%s[%d]", mname,
                           ret, pfname, strlen(pfname));
             return(ret);
             }

          if (!dscan_descend)
             {
             logman("%s:exit[0]:no subs processed", mname);
             return(FALSE);
             }

          /* locate sub-directory or go up one level */

          if (!dscan_sub())
             {
             logman("%s:exit[0]:no more subs", mname);
             return(FALSE);
             }
          }

       logman("%s:exit[0]", mname);
       return(FALSE);
    }

    void dscan_end(void)
    {
       /* De-allocate all memory used by 'dscan'. */

       char mname[] = "dscan_end";

       dscan_header(mname);
       dscan_ll_delete_all();
       dscan_delete_mvars();
       logman("%s:exit", mname);
    }

    void dscan_ll_debug(void)
    {
       /* Output the current contents of the link list
          to the log file (if active). */

       struct dscan_entry *rov;
       char mname[] = "dscan_ll_debug";
       int cnt;

       logman("%s:enter", mname);
       rov = dscan_list_head;

       if (rov == NULL)
          {
          logman("%s:exit:link list empty", mname);
          return;
          }

       cnt = 1;

       while(rov != NULL)
          {
          logman("%s[%d]:path=%s[%d],sc=%d,si=%d",
                        mname, cnt, rov->dir_path, strlen(rov->dir_path),
                        rov->sub_count, rov->sub_index);

          if (rov->subs != NULL)
             logman("%s[%d]:subs=%s[%d]", mname, cnt, rov->subs,
                           strlen(rov->subs));

          rov = rov->next;
          cnt++;
          }

       logman("%s:normal exit", mname);
    }

    static int dscan_find(char *fname)
    {
       /* Find an item in the current path. Function returns
          a type code with the item name in 'fname' upon
          success, 'FALSE' otherwise. */

       struct dscan_entry *rov;
       struct dirent *direntry;
       struct stat stat_buf;
       char mname[] = "dscan_find";
       char *full_name;
       int len, type;

       logman("%s:enter:cp=%s", mname, dscan_current_path);

       // locate link list pointer to current path or add if not found

       if ((rov = dscan_ll_find(dscan_current_path)) == NULL)
          {
          logman("%s:rc[NULL] from dscan_ll_find, will add", mname);

          if ((rov = dscan_ll_add(dscan_current_path)) == NULL)
             {
             logman("%s:exit[0]bad rc[NULL] from dscan_ll_add[0]", mname);
             return(FALSE);
             }
          }

       // scan each item in directory

       while((direntry = readdir(rov->dir)) != NULL)
          {
          len = strlen(dscan_current_path) + strlen(direntry->d_name) + 2;

          if ((full_name = (char *)malloc(len)) == (char *)NULL)
             {
             logman("%s:exit[0]alloc fail[full_name]", mname);
             return(FALSE);
             }

          // concatenate full name

          len = strlen(dscan_current_path);

          if (dscan_current_path[len - 1] == PATH_SEP)
             sprintf(full_name, "%s%s", dscan_current_path, direntry->d_name);
          else
             sprintf(full_name, "%s%c%s", dscan_current_path, PATH_SEP,
                     direntry->d_name);

          // under QNX/Linux/Unix use 'lstat' otherwise use 'stat'

    #ifdef OS_UNIX
          if (lstat(full_name, &stat_buf) != 0)
    #else
          if (stat(full_name, &stat_buf) != 0)
    #endif
             {
             free(full_name);
             logman("%s:exit[0]bad rc from stat,full_name=%s",
                           mname, full_name);
             return(FALSE);
             }

          // get object type

          type = stat_buf.st_mode & S_IFMT;

          // process based on type

          switch(type)
             {
             case S_IFREG:
                // a regular file

                if (dscan_for_files)
                   if (dscan_match(dscan_fname, direntry->d_name))
                      {
                      strcpy(fname, full_name);
                      free(full_name);
                      logman("%s:exit[%d]:found file=%s[%d]", mname,
                             DSCAN_TYPE_FILE, fname, strlen(fname));
                      return(DSCAN_TYPE_FILE);
                      }

                break;

             case S_IFDIR:
                // a directory
                // don't process '.' and '..'

                if (!strcmp(direntry->d_name, "."))
                   {
                   logman("%s:found reference to current dir,ignoring", mname);
                   break;
                   }

                if (!strcmp(direntry->d_name, ".."))
                   {
                   logman("%s:found reference to parent dir,ignoring", mname);
                   break;
                   }

                if (dscan_descend)
                   dscan_add_sub(rov, direntry->d_name);

                if (dscan_for_dirs)
                   if (dscan_match(dscan_fname, direntry->d_name))
                      {
                      strcpy(fname, full_name);
                      free(full_name);
                      logman("%s:exit[%d]:found dir=%s[%d]", mname,
                             DSCAN_TYPE_DIR, fname, strlen(fname));
                      return(DSCAN_TYPE_DIR);
                      }

                break;

    #ifdef OS_UNIX
             case S_IFLNK:
                // a symbolic link

                if (dscan_for_symlinks)
                   if (dscan_match(dscan_fname, direntry->d_name))
                      {
                      strcpy(fname, full_name);
                      free(full_name);
                      logman("%s:exit[%d]:found symlink=%s[%d]", mname,
                             DSCAN_TYPE_SYMLINK, fname, strlen(fname));
                      return(DSCAN_TYPE_SYMLINK);
                      }

                break;
    #endif

             default:
                logman("%s:exit[FALSE]:%s[%d] is an unknown type",
                           mname, full_name, strlen(full_name));
                free(full_name);
                return(FALSE);
             };

          free(full_name);
          }

       logman("%s:exit[0]", mname);
       return(FALSE);
    }

    static int dscan_sub(void)
    {
       /* Locate a sub-directory of the current or go back up one
          level in the directory tree. Function returns 'TRUE'
          if another directory was found, 'FALSE' otheriwse. */

       struct dscan_entry *rov;
       char mname[] = "dscan_sub";
       char *p, *s;
       int len;

       dscan_header(mname);
       logman("%s:cp=%s", mname, dscan_current_path);

       if ((rov = dscan_ll_find(dscan_current_path)) == NULL)
          {
          logman("%s:exit[0]bad rc[NULL] from dscan_ll_find", mname);
          return(FALSE);
          }

       if (rov->sub_count)
          {
          rov->sub_index++;
          logman("%s:subs=%s,si=%d,sc=%d", mname, rov->subs, rov->sub_index,
                        rov->sub_count);

          if (rov->sub_index <= rov->sub_count)
             {
             len = ll_wordlen(rov->subs, rov->sub_index, DSCAN_SUB_DELIM);
             logman("%s:word[%d]len=%d", mname, rov->sub_index, len);

             if ((s = (char *)malloc(len + 1)) == NULL)
                {
                logman("%s:exit[0]:alloc fail[s]", mname);
                return(FALSE);
                }

             if (!ll_word(rov->subs, s, rov->sub_index, DSCAN_SUB_DELIM))
                {
                free(s);
                logman("%s:exit[0]:bad rc from ll_word", mname);
                return(FALSE);
                }

             len = len + strlen(dscan_current_path) + 2;

             if ((p = (char *)malloc(len)) == NULL)
                {
                free(s);
                logman("%s:exit[0]:alloc fail[p]", mname);
                return(FALSE);
                }

             len = strlen(dscan_current_path);

             if (dscan_current_path[len - 1] == PATH_SEP)
                sprintf(p, "%s%s", dscan_current_path, s);
             else
                sprintf(p, "%s%c%s", dscan_current_path, PATH_SEP, s);

             logman("%s:pick sub:new cp=%s[%d]", mname, p, strlen(p));
             free(s);

             if (!dscan_set_current_path(p))
                {
                free(p);
                logman("%s:exit[0]:bad rc[0] from dscan_set_current_path",
                              mname);
                return(FALSE);
                }

             free(p);
             logman("%s:exit[TRUE]", mname);
             return(TRUE);
             }
          }

       dscan_ll_delete(dscan_current_path);

       if (!strcmp(dscan_base_path, dscan_current_path))
          {
          dscan_end();
          logman("%s:exit[0]:back at base path, assuming done", mname);
          return(FALSE);
          }

       if (!dscan_chop_path())
          {
          logman("%s:exit[0]:bad rc[0] from dscan_chop_path", mname);
          return(FALSE);
          }

       logman("%s:exit[TRUE]", mname);
       return(TRUE);
    }

    static void dscan_add_sub(struct dscan_entry *rov, char *dname)
    {
       /* Add a directory name to the indicated entries sub-directory
          list. */

       char mname[] = "dscan_add_sub";
       char *outstr;
       int len;

       dscan_header(mname);

       if (rov == NULL || dname == NULL || !strlen(dname))
          {
          logman("%s:exit[0]parm error", mname);
          return;
          }

       logman("%s:cp=%s,dname=%s,sc=%d,si=%d", mname,
                     dscan_current_path, dname, rov->sub_count, rov->sub_index);

       /* if no subs present, add first one */

       if (rov->sub_count == 0)
          {
          if ((rov->subs = (char *)malloc(strlen(dname) + 1)) == NULL)
             return;

          strcpy(rov->subs, dname);
          rov->sub_count++;
          logman("%s:exit:add first sub name", mname);
          return;
          }

       /* add sub name to delimited list of sub names */

       len = strlen(rov->subs) + strlen(dname) + 2;

       if ((outstr = (char *)malloc(len)) == (char *)NULL)
          {
          logman("%s:exit:alloc fail[outstr]", mname);
          return;
          }

       rov->sub_count++;

       if (ll_wordput(rov->subs, outstr, dname, rov->sub_count, DSCAN_SUB_DELIM))
          {
          len = strlen(outstr);
          free(rov->subs);
          rov->subs = NULL;

          if ((rov->subs = (char *)malloc(len + 1)) == (char *)NULL)
             {
             logman("%s:exit:alloc fail[rov->subs]", mname);
             return;
             }

          strcpy(rov->subs, outstr);
          }
       else
          logman("%s:bad rc[0] from ll_wordput[%s]", mname, dname);

       free(outstr);
       logman("%s:normal exit", mname);
    }

    static int dscan_chop_path(void)
    {
       /* Remove one level from the current path. Function returns
          'TRUE' upon success, 'FALSE' otherwise. */

       char mname[] = "dscan_chop_path";
       char *p;
       int nwords, len;

       dscan_header(mname);
       nwords = ll_words(dscan_current_path, PATH_SEP);
       len = strlen(dscan_current_path);

       if ((p = (char *)malloc(len + 1)) == NULL)
          {
          logman("%s:exit[0]alloc fail[p]", mname);
          return(FALSE);
          }

       if (!ll_worddel(dscan_current_path, p, nwords, PATH_SEP))
          {
          free(p);
          logman("%s:exit[0]bad rc from ll_worddel", mname);
          return(FALSE);
          }

       len = strlen(p);

       /* detect back at drive root */

    #ifdef OS_WIN32
       /* windoze where a drive ID is present */

       if (len == 2 && p[1] == ':')
          {
          p[2] = PATH_SEP;
          p[3] = EOS;
          }
    #endif

       if (!len)
          {
          /* any platform where the path is reduced to an empty string */

          p[0] = PATH_SEP;
          p[1] = EOS;
          }

       logman("%s:reduce from current,new cp=%s[%d]", mname, p, strlen(p));

       if (!dscan_set_current_path(p))
          {
          free(p);
          logman("%s:exit[0]bad rc[0] from dscan_set_current_path", mname);
          return(FALSE);
          }

       free(p);
       logman("%s:exit[TRUE]:new cp=%s[%d]", mname, dscan_current_path,
                     strlen(dscan_current_path));
       return(TRUE);
    }

    static int dscan_match(char *pat, char *fname)
    {
       /* Match a file name to a pattern. Function returns
          'TRUE' if a match was found, 'FALSE' otherwise. */

       char mname[] = "dscan_match";
       int ret;

       dscan_header(mname);

       /* for Windoze and DOS, compare case insensitive */

    #ifdef OS_WIN32
       ret = dscan_umatch(pat, fname);
       logman("%s:exit[%d]", mname, ret);
       return(ret);
    #endif

    #ifdef OS_DOS
       ret = dscan_umatch(pat, fname);
       logman("%s:exit[%d]", mname, ret);
       return(ret);
    #endif

       if (pat == NULL || !strlen(pat))
          {
          logman("%s:exit[0]:null or empty[pat]", mname);
          return(FALSE);
          }

       if (fname == NULL || !strlen(fname))
          {
          logman("%s:exit[0]:null or empty[fname]", mname);
          return(FALSE);
          }

       logman("%s:pat=%s[%d],fname=%s[%d]", mname, pat, strlen(pat),
                     fname, strlen(fname));
       ret = pmatch(pat, fname);
       logman("%s:exit[%d]", mname, ret);
       return(ret);
    }

    static int dscan_umatch(char *pat, char *fname)
    {
       /* Match a file name to a pattern case insensitive.
          Function returns 'TRUE' if a match is found,
          'FALSE' otherwise. */

       char mname[] = "dscan_umatch";
       int ret;
       char *upat, *ufname;

       dscan_header(mname);

       if (pat == NULL || !strlen(pat))
          {
          logman("%s:exit[0]:null or empty[pat]", mname);
          return(FALSE);
          }

       if (fname == NULL || !strlen(fname))
          {
          logman("%s:exit[0]:null or empty[fname]", mname);
          return(FALSE);
          }

       if ((upat = initstring(pat)) == NULL)
          {
          logman("%s:exit[0]:alloc fail[upat]", mname);
          return(FALSE);
          }

       if ((ufname = initstring(fname)) == NULL)
          {
          free(upat);
          logman("%s:exit[0]:alloc fail[ufname]", mname);
          return(FALSE);
          }

       ucase(pat, upat);
       ucase(fname, ufname);
       ret = pmatch(upat, ufname);
       free(upat);
       free(ufname);
       logman("%s:exit[%d]:case insensitive", mname, ret);
       return(ret);
    }

    static int dscan_set_flags(char *flags)
    {
       /* Load scan flags into module globals. Function returns 'TRUE'
          upon success, 'FALSE' otherwise. */

       char mname[] = "dscan_set_flags";
       char uflags[10];
       int len, i;

       dscan_header(mname);

       if (flags == NULL || !strlen(flags))
          {
          logman("%s:exit[0]null or empty[flags]", mname);
          return(FALSE);
          }

       ucase(flags, uflags);
       len = strlen(uflags);
       dscan_for_files = dscan_for_dirs = dscan_descend = FALSE;

    #ifdef OS_UNIX
       dscan_for_symlinks = FALSE;
    #endif

       for(i = 0; i < len; i++)
          {
          switch(uflags[i])
             {
             case 'F':
                dscan_for_files = TRUE;
                break;

             case 'D':
                dscan_for_dirs = TRUE;
                break;

             case 'S':
                dscan_descend = TRUE;
                break;

    #ifdef OS_UNIX
             case 'L':
                dscan_for_symlinks = TRUE;
                break;
    #endif

             default:
                logman("%s:exit[0]:unknown flag[%c]", mname, uflags[i]);
                return(FALSE);
             };
          }

    #ifdef OS_UNIX
       if (!dscan_for_files && !dscan_for_dirs && !dscan_for_symlinks)
          {
          logman("%s:exit[0]one of scan for files[f], scan for "
                 "dirs[d] or scan for symlinks[l] must be given", mname);
    #else
       if (!dscan_for_files && !dscan_for_dirs)
          {
          logman("%s:exit[0]one of scan for files[f] or scan for "
                 "dirs[d] must be given", mname);
    #endif
          return(FALSE);
          }

    #ifdef OS_UNIX
       logman("%s:exit[TRUE]sff=%d,sfd=%d,sfl=%d,sd=%d", mname, dscan_for_files,
              dscan_for_dirs, dscan_for_symlinks, dscan_descend);
    #else
       logman("%s:exit[TRUE]sff=%d,sfd=%d,sd=%d", mname,
                     dscan_for_files, dscan_for_dirs, dscan_descend);
    #endif

       return(TRUE);
    }

    static int dscan_set_current_path(char *p)
    {
       /* Save the current path. Function returns 'TRUE' upon success,
          'FALSE' otherwise. */

       char mname[] = "dscan_set_current_path";

       dscan_header(mname);

       if (p == NULL || !strlen(p))
          {
          logman("%s:exit[0]null or empty[p]", mname);
          return(FALSE);
          }

       if (dscan_current_path != NULL)
          free(dscan_current_path);

       if ((dscan_current_path = (char *)malloc(strlen(p) + 1))
           == (char *)NULL)
          {
          logman("%s:exit[0]alloc fail[dscan_current_path]", mname);
          return(FALSE);
          }

       strcpy(dscan_current_path, p);
       logman("%s:exit[TRUE]:cp=%s", mname, dscan_current_path);
       return(TRUE);
    }

    static void dscan_ll_delete(char *p)
    {
       /* Delete a link list entry by it's path. */

       struct dscan_entry *rov, *prev;
       char mname[] = "dscan_ll_delete";
       int found = FALSE;

       dscan_header(mname);
       logman("%s:ll before", mname);
       dscan_ll_debug();

       if (p == NULL || !strlen(p))
          {
          logman("%s:exit:null or empty[p]", mname);
          return;
          }

       logman("%s:p=%s[%d]", mname, p, strlen(p));
       rov = dscan_list_head;
       prev = NULL;

       while(rov != NULL && !found)
          if (!strcmp(p, rov->dir_path))
             {
             found = TRUE;
             break;
             }
          else
             {
             prev = rov;
             rov = rov->next;
             }

       if (!found)
          {
          logman("%s:exit:entry not found", mname);
          return;
          }

       closedir(rov->dir);

       if (rov->subs != NULL)
          free(rov->subs);

       free(rov->dir_path);

       if (prev != NULL)
          prev->next = rov->next;
       else
          dscan_list_head = rov->next;

       free(rov);
       logman("%s:normal exit:ll after", mname);
       dscan_ll_debug();
    }

    static void dscan_ll_delete_all(void)
    {
       /* Delete all members of the link list and set the
          head of list pointer to 'NULL'. */

       struct dscan_entry *rov, *tmp;
       char mname[] = "dscan_ll_delete_all";

       dscan_header(mname);

       if (dscan_list_head == NULL)
          {
          logman("%s:exit:link list empty", mname);
          return;
          }

       rov = dscan_list_head;

       while(rov != NULL)
          {
          closedir(rov->dir);
          free(rov->dir_path);

          if (rov->subs != NULL)
             free(rov->subs);

          tmp = rov->next;
          free(rov);
          rov = tmp;
          }

       dscan_list_head = NULL;
       logman("%s:normal exit", mname);
    }

    static int dscan_set_mvars(char *path, char *fname)
    {
       /* Allocate space for global path and file name. Function returns
          'TRUE' upon success, 'FALSE' otherwise. */

       char mname[] = "dscan_set_mvars";

       dscan_header(mname);

       if (path == NULL || !strlen(path) || fname == NULL || !strlen(fname))
          {
          logman("%s:exit[0]parm error", mname);
          return(FALSE);
          }

       if (dscan_base_path != NULL)
          {
          logman("%s:exit[0]base path already assigned", mname);
          return(FALSE);
          }

       if ((dscan_base_path = (char *)malloc(strlen(path) + 1)) == (char *)NULL)
          {
          logman("%s:exit[0]alloc fail[dscan_base_path]", mname);
          return(FALSE);
          }

       strcpy(dscan_base_path, path);

       if (dscan_fname != NULL)
          {
          logman("%s:exit[0]target file name already assigned", mname);
          return(FALSE);
          }

       if ((dscan_fname = (char *)malloc(strlen(fname) + 1)) == (char *)NULL)
          {
          free(dscan_base_path);
          dscan_base_path = NULL;
          logman("%s:exit[0]alloc fail[dscan_fname]", mname);
          return(FALSE);
          }

       strcpy(dscan_fname, fname);
       logman("%s:exit[TRUE]", mname);
       return(TRUE);
    }

    static void dscan_delete_mvars(void)
    {
       /* Delete the module global strings. */

       char mname[] = "dscan_delete_mvars";

       dscan_header(mname);

       if (dscan_base_path != NULL)
          {
          free(dscan_base_path);
          dscan_base_path = NULL;
          }

       if (dscan_current_path != NULL)
          {
          free(dscan_current_path);
          dscan_current_path = NULL;
          }

       if (dscan_fname != NULL)
          {
          free(dscan_fname);
          dscan_fname = NULL;
          }

       logman("%s:normal exit", mname);
    }

    static struct dscan_entry *dscan_ll_find(char *p)
    {
       /* Locate a link list entry by the path. Function returns
          a pointer to the appropriate link list entry upon success,
          a NULL pointer otherwise. */

       struct dscan_entry *rov;
       char mname[] = "dscan_ll_find";

       dscan_header(mname);

       if (p == NULL || !strlen(p))
          {
          logman("%s:exit[NULL]:null or empty[p]", mname);
          return(NULL);
          }

       logman("%s:attempting to locate %s[%d]", mname, p, strlen(p));
       rov = dscan_list_head;

       if (rov == NULL)
          {
          logman("%s:exit[NULL]:link list empty", mname);
          return(NULL);
          }

       while(rov != NULL)
          {
          if (!strcmp(rov->dir_path, p))
             {
             logman("%s:exit:found entry", mname);
             return(rov);
             }

          rov = rov->next;
          }

       logman("%s:exit:entry not found", mname);
       return(NULL);
    }

    static struct dscan_entry *dscan_ll_add(char *p)
    {
       /* Add a directory to the link list. Function returns a pointer
          to the new entry upon success, a NULL pointer otherwise. */

       struct dscan_entry *rov, *last;
       char mname[] = "dscan_ll_add";
       int len;

       dscan_header(mname);
       dscan_ll_debug();

       if (p == NULL || !strlen(p))
          {
          logman("%s:exit[NULL]null or empty[p]", mname);
          return(NULL);
          }

       logman("%s:adding %s", mname, p);
       rov = dscan_list_head;
       last = NULL;

       while(rov != NULL)
          {
          if (rov->next == NULL)
             {
             last = rov;
             break;
             }

          rov = rov->next;
          }

       len = sizeof(struct dscan_entry);

       if ((rov = (struct dscan_entry *)malloc(len)) == NULL)
          {
          logman("%s:exit[NULL]alloc error[rov]", mname);
          return(NULL);
          }

       if ((rov->dir_path = (char *)malloc(strlen(p) + 1)) == NULL)
          {
          free(rov);
          logman("%s:exit[NULL]alloc fail[p]", mname);
          return(NULL);
          }

       strcpy(rov->dir_path, p);
       rov->subs = NULL;
       rov->sub_count = rov->sub_index = 0;
       rov->next = NULL;

       if ((rov->dir = opendir(rov->dir_path)) == NULL)
          {
          free(rov->dir_path);
          free(rov);
          logman("%s:unable to access directory %s[%d], dumping",
                        mname, p, strlen(p));
          (void)dscan_chop_path();
          logman("%s:exit[NULL]", mname);
          return(NULL);
          }

       if (last != NULL)
          last->next = rov;

       if (dscan_list_head == NULL)
          dscan_list_head = rov;

       dscan_ll_debug();
       logman("%s:exit[ok]", mname);
       return(rov);
    }

    static void dscan_header(char *mname)
    {
       // Log the entrance into a function.

       logman("%s:enter", mname);
    }

/* [<][>][^][v][top][bottom][index][help] */