## root/clib/armtree.c

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

## DEFINITIONS

This source file includes following definitions.

1. rmtree
2. armtree_delete_dirs
3. armtree_ll_add
4. armtree_ll_delete
5. armtree_ll_debug

    /* Directory tree removal API.
       Rick Smereka, Copyright (C) 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

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

       This API will delete all files and directories from a specific
       directory. BE VERY CAREFULL. You can easly delete the entire
       contents of the disk by starting from the root of the drive.
       This API uses the 'dscan' API to navigate through the tree.

       Two passes take place. The first pass deletes all the files
       and stores all directory names in a link list (new entires
       are added to the top of the list [LIFO]). The second pass
       goes through the link list from the top and deletes all
       the directories.

       Original version for Linux. Jan/2005, Rick Smereka.

       Ported to QNX, 32bit Windows and DOS. Jun/2005, Rick Smereka. */

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

    // global data

    // directory name link list node structure

    struct armtree_entry
       {
       char *dir_path;
       struct armtree_entry *next;
       };

    #define ARMTREE_NULL ((struct armtree_entry *)NULL)

    struct armtree_entry *armtree_head;

    // private functions

    static int armtree_delete_dirs(void);
    static int armtree_ll_add(char *);
    static void armtree_ll_delete(void);
    static void armtree_ll_debug(void);

    int rmtree(char *path)
    {
       /* Delete all files and directores from 'path' down.
          Function returns 'TRUE' upon success, 'FALSE'
          otherwise. */

       char mname[] = "rmtree", fname[256];
       int ret;

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

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

       // scan for everything and include sub-directories

    #ifdef OS_UNIX
       if ((ret = dscan_findfirst(path, "*", "flds", fname)) == FALSE)
    #else
       if ((ret = dscan_findfirst(path, "*", "fds", fname)) == FALSE)
    #endif
          {
          logman("%s:exit[0]:bad rc[FALSE] from dscan_findfirst", mname);
          return(FALSE);
          }

       // loop to delete files and build dir link list

       while(ret)
          {
          switch(ret)
             {
             case DSCAN_TYPE_FILE:
                logman("%s:deleting found file %s", mname, fname);
                unlink(fname);
                break;

             case DSCAN_TYPE_DIR:
                logman("%s:adding found dir %s", mname, fname);

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

                break;

    #ifdef OS_UNIX
             case DSCAN_TYPE_SYMLINK:
                logman("%s:deleting found symlink %s", mname, fname);
                unlink(fname);
                break;
    #endif

             default:
                logman("%s:unknown type:rc=%d", mname, ret);
             };

          ret = dscan_findnext(fname);
          }

       // armtree_ll_debug();

       // delete all directories in the tree

       if (!armtree_delete_dirs())
          {
          logman("%s:exit:deletion of all dirs failed", mname);
          armtree_ll_delete();
          return(FALSE);
          }

       logman("%s:delete of all sub-dirs successful", mname);
       armtree_ll_delete();

       // remove top level directory

       if (rmdir(path))
          {
          logman("%s:exit:deletion of top level dir %s failed", mname, path);
          return(FALSE);
          }

       logman("%s:delete of top level dir successful", mname);
       return(TRUE);
    }

    static int armtree_delete_dirs(void)
    {
       /* Delete all directories in the link list. Function
          returns 'TRUE' upon success, 'FALSE' otherwise. */

       struct armtree_entry *rov = armtree_head;
       char mname[] = "armtree_delete_dirs";

       while(rov != ARMTREE_NULL)
          {
          if (rmdir(rov->dir_path))
             {
             logman("%s:exit[0]:delete of dir %s failed", mname, rov->dir_path);
             return(FALSE);
             }

          rov = rov->next;
          }

       return(TRUE);
    }

    static int armtree_ll_add(char *dirname)
    {
       /* Add a directory to the link list. The new entry will be
          placed at the top of the list. Function returns 'TRUE'
          upon success, 'FALSE' otherwise. */

       struct armtree_entry *rov;
       struct armtree_entry *prev;
       char mname[] = "armtree_ll_add";
       int size, len;

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

       if (armtree_head != ARMTREE_NULL)
          prev = armtree_head;
       else
          prev = ARMTREE_NULL;

       size = sizeof(struct armtree_entry);
       len = strlen(dirname);

       // if the last char of the path is '.', do not add

       if (dirname[len - 1] == '.')
          return(TRUE);

       if ((rov = (struct armtree_entry *)malloc(size)) == ARMTREE_NULL)
          {
          logman("%s:exit[0]:alloc fail[rov]", mname);
          return(FALSE);
          }

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

       strcpy(rov->dir_path, dirname);
       armtree_head = rov;
       rov->next = prev;
       return(TRUE);
    }

    static void armtree_ll_delete(void)
    {
       struct armtree_entry *rov = armtree_head;
       struct armtree_entry *tmp;

       while(rov != ARMTREE_NULL)
          {
          free(rov->dir_path);
          tmp = rov->next;
          free(rov);
          rov = tmp;
          }

       armtree_head = ARMTREE_NULL;
    }

    static void armtree_ll_debug(void)
    {
       struct armtree_entry *rov = armtree_head;

       while(rov != ARMTREE_NULL)
          {
          logman("dir:%s", rov->dir_path);
          rov = rov->next;
          }
    }

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