/*
	WARNING: This file was generated by dkct.
	Changes you make here will be lost if dkct is run again!
	You should modify the original source and run dkct on it.
	Original source: dk3dir.ctr
*/

/*
Copyright (C) 2011-2013, Dirk Krause

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above opyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used
  to endorse or promote products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**	@file dk3dir.c The dk3dir module.
*/


#line 124 "dk3dir.ctr"

#include "dk3all.h"





#line 130 "dk3dir.ctr"



/**	Keywords and texts used in this module.
*/
static dkChar const * const kw[] = {
/*  0 */ dkT("."),
/*  1 */ dkT(".."),
#if DK3_ON_WINDOWS || DK3_HAVE_BACKSLASH
/*  2 */ dkT("\\"),
/*  3 */ dkT("\\*"),
#else
/*  2 */ dkT("/"),
/*  3 */ dkT("/*"),
#endif
/*  4 */ dkT("*"),
NULL
};



/**	Compare two directory entries by name.
	@param	l	Left name.
	@param	r	Right name.
	@param	cr	Comparison criteria (ignored).
	@return	Comparison result.
*/
static
int
dk3dir_compare_by_name(void *l, void *r, int cr)
{
  int back = 0;
  if(l) {
    if(r) {
#if DK3_ON_WINDOWS || DK3_HAVE_FNCASEINS
      back = dk3str_casecmp((dkChar const *)l,(dkChar const *)r);
#else
      back = dk3str_cmp((dkChar const *)l,(dkChar const *)r);
#endif
    } else { back =  1; }
  } else {
    if(r)  { back = -1; }
  }
  return back;
}



/**	Release all directory entries in a storage.
	@param	s	Storage to clean up.
	@param	i	Iterator for \a s.
*/
static
void
dk3dir_release_entries(dk3_sto_t *s, dk3_sto_it_t *i)
{
  dkChar *p = NULL;	/* Current name to release. */
  

#line 188 "dk3dir.ctr"
  if(s) {
    if(i) {
      dk3sto_it_reset(i);
      while((p = (dkChar *)dk3sto_it_next(i)) != NULL) {
        

#line 193 "dk3dir.ctr"
        dk3_delete(p);
      }
      dk3sto_it_close(i);
    }
    dk3sto_close(s);
  } 

#line 199 "dk3dir.ctr"
}



/**	Close directory.
	@param	dp	Directory to close.
*/
void
dk3dir_close(dk3_dir_t *dp)
{
  

#line 210 "dk3dir.ctr"
  if(dp) {
    dk3dir_release_entries(dp->sdi, dp->idi);
    dk3dir_release_entries(dp->sfi, dp->ifi);
    dp->sdi = NULL; dp->sfi = NULL; dp->idi = NULL; dp->ifi = NULL;
    dk3_release(dp->dirname)
    dk3_release(dp->fullname)
    dp->app = NULL;
    dp->ndir = dp->nfi = DK3_MAX_UINT_0;
    dk3_delete(dp);
  } 

#line 220 "dk3dir.ctr"
}



/**	Create new dir structure.
	@param	dn	Directory name.
	@param	app	Application structure for diagnostics, may be NULL.
	@return	Pointer to new structure on success, NULL on error.
*/
static
dk3_dir_t *
dk3dir_new(dkChar const *dn, dk3_app_t *app)
{
  dk3_dir_t *back = NULL;
  int ok = 0;			/* Flag: Success. */
  

#line 236 "dk3dir.ctr"
  back = dk3_new_app(dk3_dir_t,1,app);
  if(back) {
    back->sdi = NULL; back->sfi = NULL; back->idi = NULL; back->ifi = NULL;
    back->dirname = NULL; back->fullname = NULL; back->app = app;
    back->ndir = back->nfi = DK3_MAX_UINT_0;
    back->sdi = dk3sto_open_app(app);
    if(back->sdi) {
      back->idi = dk3sto_it_open(back->sdi);
      if(back->idi) {
        back->sfi = dk3sto_open_app(app);
	if(back->sfi) {
	  back->ifi = dk3sto_it_open(back->sfi);
	  if(back->ifi) {
	    dk3sto_set_comp(back->sdi,dk3dir_compare_by_name,0);
	    dk3sto_set_comp(back->sfi,dk3dir_compare_by_name,0);
	    back->dirname = dk3str_dup(dn);
	    if(back->dirname) {
	      back->fullname = dk3_new_app(dkChar,DK3_MAX_PATH,app);
	      if(back->fullname) {
	        ok = 1;
	      }
	    }
	  }
	}
      }
    }
    if(!ok) {
      dk3dir_close(back); back = NULL;
    }
  } 

#line 266 "dk3dir.ctr"
  return back;
}



dkChar const *
dk3dir_get_directory_name(dk3_dir_t *dir)
{
  dkChar const *back = NULL;
  

#line 276 "dk3dir.ctr"
  if(dir) {
    back = dir->dirname;
  } 

#line 279 "dk3dir.ctr"
  return back;
}



void
dk3dir_reset(dk3_dir_t *d)
{
  

#line 288 "dk3dir.ctr"
  if(d) {
    dk3sto_it_reset(d->idi);
    dk3sto_it_reset(d->ifi);
    d->shortname = NULL;
  } 

#line 293 "dk3dir.ctr"
}



dk3_um_t
dk3dir_get_number_of_directories(dk3_dir_t *d)
{
  dk3_um_t back = DK3_MAX_UINT_0;
  if(d) {
    back = d->ndir;
  } 

#line 304 "dk3dir.ctr"
  return back;
}



dk3_um_t
dk3dir_get_number_of_files(dk3_dir_t *d)
{
  dk3_um_t back = DK3_MAX_UINT_0;
  if(d) {
    back = d->nfi;
  } 

#line 316 "dk3dir.ctr"
  return back;
}



dkChar const *
dk3dir_get_fullname(dk3_dir_t *d)
{
  dkChar const *back = NULL;
  

#line 326 "dk3dir.ctr"
  if(d) {
    back = (dkChar const *)(d->fullname);
  } 

#line 329 "dk3dir.ctr"
  return back;
}



dk3_stat_t *
dk3dir_get_stat(dk3_dir_t *d)
{
  dk3_stat_t *back = NULL;
  

#line 339 "dk3dir.ctr"
  if(d) {
    back = &(d->stb);
  } 

#line 342 "dk3dir.ctr"
  return back;
}



dkChar const *
dk3dir_get_shortname(dk3_dir_t *d)
{
  dkChar const *back = NULL;
  

#line 352 "dk3dir.ctr"
  if(d) {
    back = (dkChar const *)(d->shortname);
  } 

#line 355 "dk3dir.ctr"
  return back;
}



/**	Get next entry.
	@param	d	Directory structure.
	@param	s	Iterator for the storage.
	@param	isdir	Flag: Current entry is a directory.
	@return	1 on success, 0 on error.
*/
static
int
dk3dir_get_next_entry(dk3_dir_t *d, dk3_sto_it_t *s, int isdir)
{
  int		back = 0;
  int		cc = 1;		/* Flag: Can continue. */
  size_t	sz = 0;		/* Directory name length. */
  

#line 374 "dk3dir.ctr"
  do {
    cc = 0;
    d->shortname = (dkChar *)dk3sto_it_next(s);
    if(d->shortname) {		

#line 378 "dk3dir.ctr"
      cc = 1;
      if(d->dirname) {
        if(dk3str_len(d->dirname)) {
          dk3str_cpy(d->fullname, d->dirname);
	  sz = dk3str_len(d->dirname);
	  if(sz > 0) {
	    if((d->dirname)[sz - 1] != DK3_CHAR_SEP) {
	      dk3str_cat(d->fullname, kw[2]);
	    }
	  } else {
	    dk3str_cat(d->fullname, kw[2]);
	  }
	  dk3str_cat(d->fullname, d->shortname);
        } else {
          dk3str_cpy(d->fullname, d->shortname);
        }
      } else {
        dk3str_cpy(d->fullname, d->shortname);
      }
      if(dk3sf_stat_app(&(d->stb), d->fullname, (dk3_app_t *)(d->app))) {
        switch(((d->stb).ft) & (~(DK3_FT_SYMLINK))) {
	  case DK3_FT_DIRECTORY: {
	    if(isdir) {
	      back = 1; cc = 0;
	    }
	  } break;
	  default: {
	    if(!isdir) {
	      back = 1; cc = 0;
	    }
	  } break;
	}
      }
    } 

#line 412 "dk3dir.ctr"
  } while(cc); 

#line 413 "dk3dir.ctr"
  return back;
}



int
dk3dir_get_next_directory(dk3_dir_t *d)
{
  int back = 0;
  

#line 423 "dk3dir.ctr"
  if(d) {
    back = dk3dir_get_next_entry(d, d->idi, 1);
  } 

#line 426 "dk3dir.ctr"
  return back;
}



int
dk3dir_get_next_file(dk3_dir_t *d)
{
  int back = 0;
  

#line 436 "dk3dir.ctr"
  if(d) {
    back = dk3dir_get_next_entry(d, d->ifi, 0);
  } 

#line 439 "dk3dir.ctr"
  return back;
}



/**	Add a found item to a storage.
	@param	s	Storage to add item to.
	@param	n	Item name.
	@param	c	Pointer to variable to increase to.
	@param	ec	Pointer to error code variable.
	@param	app	Application structure for diagnostics.
*/
static
void
dk3dir_add(dk3_sto_t *s, dkChar const *n, dk3_um_t *c, int *ec, dk3_app_t *app)
{
  dkChar	*newname = NULL;	/* New copy of name. */
  dk3_um_t	mu = DK3_MAX_UINT_0;	/* Number of entries. */
  

#line 458 "dk3dir.ctr"
  newname = dk3str_dup_app(n,(((*ec) & 1) ? NULL : app));
  if(newname) {
    if(dk3sto_add(s, newname)) {
      mu = *c;
      mu++;
      *c = mu;
    } else {
      dk3_delete(newname);
      *ec |= 2;		

#line 467 "dk3dir.ctr"
    }
  } else {
    *ec |= 1;		

#line 470 "dk3dir.ctr"
  } 

#line 471 "dk3dir.ctr"
}



#if DK3_ON_WINDOWS
/* +++ Windows +++ */
#if DK3_CHAR_SIZE > 1
/* +++ Windows > 8 bit +++ */


#if VERSION_BEFORE_2012_04_13

dk3_dir_t *
dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
{
  dk3_dir_t		*back = NULL;
  int			ec = 0;			/* Error code. */
  dkChar		buffer[DK3_MAX_PATH];	/* Buffer for entry names. */
  intptr_t		ffres;			/* Result of findfirst(). */
  dkChar		*namecopy = NULL;	/* New copy of name. */
  struct _wfinddata64_t	ffdata;			/* Find data. */
  if(dn) {
    if((dk3str_len(kw[3]) + dk3str_len(dn)) < DK3_SIZEOF(buffer,dkChar)) {
      dk3str_cpy(buffer, dn);
      dk3str_cat(buffer, kw[3]);
      back = dk3dir_new(dn, app);
      if(back) {
        ffres = _wfindfirst64(buffer, &ffdata);
	if(ffres != -1) {
	  do {
	    if(dk3str_cmp(ffdata.name, kw[0])) {
	      if(dk3str_cmp(ffdata.name, kw[1])) {
		if((ffdata.attrib) & _A_SUBDIR) {
		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
		} else {
		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
		}
	      }
	    }
	  } while(_wfindnext64(ffres, &ffdata) == 0);
	  _findclose(ffres);
	} else {
	  ec = 1;
	  if(app) {
	    /* ERROR: Failed to open directory! */
	    dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
	  }
	}
      }
      if(ec) {
        dk3dir_close(back); back = NULL;
      }
    } else {
      if(app) {
        /* ERROR: Directory name too long! */
	dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
      }
    }
  }
  return back;
}


dk3_dir_t *
dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
{
  dk3_dir_t		*back = NULL;
  int			ec = 0;			/* Error code. */
  dkChar		mybuffer[DK3_MAX_PATH];	/* Buffer for entry names. */
  dkChar		myb2[2];		/* Search pattern. */
  dkChar		*ptr = NULL;		/* Last separator. */
  dkChar		*namecopy = NULL;	/* New copy of entry name. */
  intptr_t		ffres;			/* Result from findfirst(). */
  struct _wfinddata64_t	ffdata;			/* Find data. */
  

#line 546 "dk3dir.ctr"
  if(fn) {
    if(dk3str_len(fn) < DK3_MAX_PATH) {
      dk3str_cpy(mybuffer, fn);
      ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP);
      if(ptr) {
	*ptr = DK3_CHAR_0;
	back = dk3dir_new(mybuffer, app);
	*ptr = DK3_CHAR_SEP;
      } else {
        myb2[0] = DK3_CHAR_0;
	back = dk3dir_new(myb2, app);
      }
      if(back) {		

#line 559 "dk3dir.ctr"
        ffres = _wfindfirst64(mybuffer, &ffdata);
	if(ffres != -1) {
	  do {		

#line 562 "dk3dir.ctr"
	    if(dk3str_cmp(ffdata.name, kw[0])) {	

#line 563 "dk3dir.ctr"
	      if(dk3str_cmp(ffdata.name, kw[1])) {	

#line 564 "dk3dir.ctr"
	        if((ffdata.attrib) & _A_SUBDIR) {	

#line 565 "dk3dir.ctr"
		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
		} else {				

#line 567 "dk3dir.ctr"
		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
		}
	      }
	    }
	  } while(_wfindnext64(ffres, &ffdata) == 0);
	  _findclose(ffres);
	} else {
	  ec = 1;
	  if(app) {
	    /* ERROR: Failed to open directory! */
	    dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, mybuffer);
	  }
	}
      }
      if(ec) {
        dk3dir_close(back); back = NULL;
      }
    } else {
      /* ERROR: Pattern too long! */
      dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn);
    }
  } 

#line 589 "dk3dir.ctr"
  return back;
}


#else


static
dk3_dir_t *
dk3dir_my_open_app(dkChar const *dn, int usefne, dk3_app_t *app)
{
  WIN32_FIND_DATAW	 wfd;	/* Details for file. */
  dkChar		 b1[DK3_MAX_PATH];	/* Directory base. */
  dkChar		 b2[DK3_MAX_PATH];	/* Pattern. */
  dkChar		*p1;			/* Separator. */
  dk3_dir_t		*back = NULL;
  HANDLE		 hSearch;		/* File search handle. */
  size_t		 sz;			/* String length. */
  int			 ok;			/* Flag: No error yet. */
  int			 ec;			/* Error code. */
  int			 isDir;			/* Flag: Entry is dir. */
  

#line 611 "dk3dir.ctr"
  ec = 0;
  if(dn) {
    ok = 0;
    if(usefne) {
      if(dk3str_len(dn) < DK3_SIZEOF(b1,dkChar)) {
        ok = 1;				

#line 617 "dk3dir.ctr"
        dk3str_cpy(b1, dn);
	dk3str_cpy(b2, dn);
	p1 = dk3str_rchr(b1, DK3_CHAR_SEP);
	if(p1) {
	  *p1 = dkT('\0');
	} else {
	  b1[0] = dkT('\0');
	}
      } else {				

#line 626 "dk3dir.ctr"
        /* ERROR: Name too long! */
	if(app) {
	  dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, dn);
	}
      }
    } else {
      sz = dk3str_len(dn) + dk3str_len(kw[3]);
      if(sz < DK3_SIZEOF(b1,dkChar)) {
        ok = 1;				

#line 635 "dk3dir.ctr"
	dk3str_cpy(b1, dn);
	dk3str_cpy(b2, dn);
	sz = dk3str_len(b2);
	if(sz > 1) {
	  dk3str_cat(b2, kw[(DK3_CHAR_SEP == b2[sz - 1]) ? 4 : 3]);
	} else {
	  dk3str_cat(b2, kw[3]);
	}
      } else {				

#line 644 "dk3dir.ctr"
      	/* Name too long! */
	if(app) {
	  dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
	}
      }
    }
    if(ok) {	

#line 651 "dk3dir.ctr"
      back = dk3dir_new(b1, app);
      if(back) {				

#line 653 "dk3dir.ctr"
        hSearch = FindFirstFileW(b2, &wfd);
	if(INVALID_HANDLE_VALUE != hSearch) {	

#line 655 "dk3dir.ctr"
	  do {
	    isDir = 0;		

#line 657 "dk3dir.ctr"
	    if(dk3str_cmp(wfd.cFileName, kw[0])) {
	      if(dk3str_cmp(wfd.cFileName, kw[1])) {	

#line 659 "dk3dir.ctr"
	        if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) {
		  isDir = 1;
#if 0
		  if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_REPARSE_POINT) {
		    switch(wfd.dwReserved0) {
		      case IO_REPARSE_TAG_MOUNT_POINT: {
		        isDir = 0;
		      } break;
		    }
		  }
#endif
		}
		if(isDir) {			

#line 672 "dk3dir.ctr"
		  dk3dir_add(back->sdi, wfd.cFileName, &(back->ndir), &ec, app);
		} else {			

#line 674 "dk3dir.ctr"
		  dk3dir_add(back->sfi, wfd.cFileName, &(back->nfi), &ec, app);
		}
	      }
	    }
	  } while(FindNextFileW(hSearch, &wfd));
	  FindClose(hSearch);
	} else {			

#line 681 "dk3dir.ctr"
	  /* ERROR: Failed to search for pattern! */
	  if(app) {
	    if(usefne) {
	      dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, dn);
	    } else {
	      dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
	    }
	  }
	}
	if(ec) {
	  if(app) {
	    dk3app_log_i1(app, DK3_LL_ERROR, 9);
	  }
	  dk3dir_close(back); back = NULL;
	}
      } else {				

#line 697 "dk3dir.ctr"
      }
    }
  } else {				

#line 700 "dk3dir.ctr"
  } 

#line 701 "dk3dir.ctr"
  return back;
}



dk3_dir_t *
dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
{
  dk3_dir_t		*back;
  

#line 711 "dk3dir.ctr"
  back = dk3dir_my_open_app(dn, 0, app);
  

#line 713 "dk3dir.ctr"
  return back;
}


dk3_dir_t *
dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
{
  dk3_dir_t		*back;
  

#line 722 "dk3dir.ctr"
  back = dk3dir_my_open_app(fn, 1, app);
  

#line 724 "dk3dir.ctr"
  return back;
}


#endif


/* --- Windows > 8 bit --- */
#else
/* +++ Windows, 8 bit +++ */



#if VERSION_BEFORE_2012_04_13

dk3_dir_t *
dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
{
  dk3_dir_t		*back = NULL;
  int			ec = 0;			/* Error code. */
  dkChar		buffer[DK3_MAX_PATH];	/* Buffer for entry names. */
  intptr_t		ffres;			/* Result from findfirst(). */
  dkChar		*namecopy = NULL;	/* New copy of entry name. */
  struct __finddata64_t	ffdata;			/* Find data. */
  if(dn) {
    if((dk3str_len(kw[3]) + dk3str_len(dn)) < DK3_SIZEOF(buffer,dkChar)) {
      dk3str_cpy(buffer, dn);
      dk3str_cat(buffer, kw[3]);
      back = dk3dir_new(dn, app);
      if(back) {
        ffres = _findfirst64(buffer, &ffdata);
	if(ffres != -1) {
	  do {
	    if(dk3str_cmp(ffdata.name, kw[0])) {
	      if(dk3str_cmp(ffdata.name, kw[1])) {
		if((ffdata.attrib) & _A_SUBDIR) {
		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
		} else {
		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
		}
	      }
	    }
	  } while(_findnext64(ffres, &ffdata) == 0);
	  _findclose(ffres);
	} else {
	  ec = 1;	/* Findfirst failed */
	  if(app) {
	    /* ERROR: Findfirst failed! */
	    dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
	  }
	}
      }
      if(ec) {
        dk3dir_close(back); back = NULL;
      }
    } else {
      if(app) {
        /* ERROR: Directory name too long! */
	dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
      }
    }
  }
  return back;
}



dk3_dir_t *
dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
{
  dk3_dir_t		*back = NULL;
  int			ec = 0;			/* Error code. */
  dkChar		mybuffer[DK3_MAX_PATH];	/* Buffer for entry name. */
  dkChar		myb2[2];		/* Buffer for pattern. */
  dkChar		*ptr = NULL;		/* Last separator. */
  dkChar		*namecopy = NULL;	/* New copy of entry name. */
  intptr_t		ffres;			/* Result from findfirst(). */
  struct __finddata64_t	ffdata;			/* Find data. */
  

#line 803 "dk3dir.ctr"
  if(fn) {
    if(dk3str_len(fn) < DK3_MAX_PATH) {
      dk3str_cpy(mybuffer, fn);
      ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP);
      if(ptr) {
	*ptr = DK3_CHAR_0;
	back = dk3dir_new(mybuffer, app);
	*ptr = DK3_CHAR_SEP;
      } else {
        myb2[0] = DK3_CHAR_0;
	back = dk3dir_new(myb2, app);
      }
      if(back) {
        ffres = _findfirst64(mybuffer, &ffdata);
	if(ffres != -1) {
	  do {
	    if(dk3str_cmp(ffdata.name, kw[0])) {
	      if(dk3str_cmp(ffdata.name, kw[1])) {
		if((ffdata.attrib) & _A_SUBDIR) {
		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
		} else {
		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
		}
	      }
	    }
	  } while(_findnext64(ffres, &ffdata) == 0);
	  _findclose(ffres);
	} else {
	  ec = 1;
	  if(app) {
	    /* ERROR: Failed to open directory! */
	    dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, mybuffer);
	  }
	}
      }
      if(ec) {
        dk3dir_close(back); back = NULL;
      }
    } else {
      if(app) {
        /* ERROR: Pattern too long! */
	dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn);
      }
    }
  } 

#line 848 "dk3dir.ctr"
  return back;
}


#else



static
dk3_dir_t *
dk3dir_my_open_app(dkChar const *dn, int usefne, dk3_app_t *app)
{
  WIN32_FIND_DATAA	 wfd;	/* Details for file. */
  dkChar		 b1[DK3_MAX_PATH];	/* Directory base. */
  dkChar		 b2[DK3_MAX_PATH];	/* Pattern. */
  dkChar		*p1;			/* Separator. */
  dk3_dir_t		*back = NULL;
  HANDLE		 hSearch;		/* File search handle. */
  size_t		 sz;			/* String length. */
  int			 ok;			/* Flag: No error yet. */
  int			 ec;			/* Error code. */
  int			 isDir;			/* Flag: Entry is dir. */
  

#line 871 "dk3dir.ctr"
  ec = 0;
  if(dn) {
    ok = 0;
    if(usefne) {
      if(dk3str_len(dn) < DK3_SIZEOF(b1,dkChar)) {
        ok = 1;				

#line 877 "dk3dir.ctr"
        dk3str_cpy(b1, dn);
	dk3str_cpy(b2, dn);
	p1 = dk3str_rchr(b1, DK3_CHAR_SEP);
	if(p1) {
	  *p1 = dkT('\0');
	} else {
	  b1[0] = dkT('\0');
	}
      } else {				

#line 886 "dk3dir.ctr"
        /* ERROR: Name too long! */
	if(app) {
	  dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, dn);
	}
      }
    } else {
      sz = dk3str_len(dn) + dk3str_len(kw[3]);
      if(sz < DK3_SIZEOF(b1,dkChar)) {
        ok = 1;				

#line 895 "dk3dir.ctr"
	dk3str_cpy(b1, dn);
	dk3str_cpy(b2, dn);
	sz = dk3str_len(b2);
	if(sz > 1) {
	  dk3str_cat(b2, kw[(DK3_CHAR_SEP == b2[sz - 1]) ? 4 : 3]);
	} else {
	  dk3str_cat(b2, kw[3]);
	}
      } else {				

#line 904 "dk3dir.ctr"
      	/* ERROR: Name too long! */
	if(app) {
	  dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
	}
      }
    }
    if(ok) {	

#line 911 "dk3dir.ctr"
      back = dk3dir_new(b1, app);
      if(back) {				

#line 913 "dk3dir.ctr"
        hSearch = FindFirstFileA(b2, &wfd);
	if(INVALID_HANDLE_VALUE != hSearch) {	

#line 915 "dk3dir.ctr"
	  do {
	    isDir = 0;		

#line 917 "dk3dir.ctr"
	    if(dk3str_cmp(wfd.cFileName, kw[0])) {
	      if(dk3str_cmp(wfd.cFileName, kw[1])) {	

#line 919 "dk3dir.ctr"
	        if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) {
		  isDir = 1;
#if 0
		  if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_REPARSE_POINT) {
		    switch(wfd.dwReserved0) {
		      case IO_REPARSE_TAG_MOUNT_POINT: {
		        isDir = 0;
		      } break;
		    }
		  }
#endif
		}
		if(isDir) {			

#line 932 "dk3dir.ctr"
		  dk3dir_add(back->sdi, wfd.cFileName, &(back->ndir), &ec, app);
		} else {			

#line 934 "dk3dir.ctr"
		  dk3dir_add(back->sfi, wfd.cFileName, &(back->nfi), &ec, app);
		}
	      }
	    }
	  } while(FindNextFileA(hSearch, &wfd));
	  FindClose(hSearch);
	} else {			

#line 941 "dk3dir.ctr"
	  /* ERROR: Failed to search for pattern! */
	  if(app) {
	    if(usefne) {
	      dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, dn);
	    } else {
	      dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
	    }
	  }
	}
	if(ec) {
	  dk3dir_close(back); back = NULL;
	}
      } else {				

#line 954 "dk3dir.ctr"
      }
    }
  } else {				

#line 957 "dk3dir.ctr"
  } 

#line 958 "dk3dir.ctr"
  return back;
}



dk3_dir_t *
dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
{
  dk3_dir_t		*back;
  

#line 968 "dk3dir.ctr"
  back = dk3dir_my_open_app(dn, 0, app);
  

#line 970 "dk3dir.ctr"
  return back;
}


dk3_dir_t *
dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
{
  dk3_dir_t		*back;
  

#line 979 "dk3dir.ctr"
  back = dk3dir_my_open_app(fn, 1, app);
  

#line 981 "dk3dir.ctr"
  return back;
}



#endif

/* --- Windows, 8 bit --- */
#endif
/* --- Windows --- */
#else
/* +++ non-Windows +++ */
#if DK3_CHAR_SIZE > 1
/* +++ non-Windows > 8 bit +++ */
#error	"16/32-bit file names only available on Windows!"
/* --- non-Windows > 8 bit --- */
#else
/* +++ non-Windows, 8 bit +++ */



/**	Function names used in error messages.
*/
static dkChar const * const dk3dir_function_names[] = {
/* 0 */
dkT("opendir: "),

NULL


#line 1011 "dk3dir.ctr"
};



#if DK3_HAVE_OPENDIR && DK3_HAVE_READDIR && DK3_HAVE_CLOSEDIR



dk3_dir_t *
dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
{
  dk3_dir_t	*back = NULL;
  int		ec = 0;		/* Error code. */
  struct dirent	*de = NULL;	/* Directory entry from readdir(). */
  DIR		*d = NULL;	/* Directory structure from opendir(). */
  size_t	sz = 0;		/* Length of entire file name. */
  size_t	dns = 0;	/* Directory name length. */
  size_t	kw2l = 0;	/* Length of separator string. */
  dk3_stat_t	stb;		/* Buffer for stat() result. */
  

#line 1031 "dk3dir.ctr"
  if(dn) {
    back = dk3dir_new(dn, app);
    if(back) {
      d = opendir(dn);
      if(d) {
        dns = dk3str_len(dn); kw2l = dk3str_len(kw[2]);
        while((de = readdir(d)) != NULL) {
	  if(dk3str_cmp(de->d_name, kw[0])) {
	    if(dk3str_cmp(de->d_name, kw[1])) {
	      sz = dns + kw2l + dk3str_len(de->d_name);
	      if(sz < DK3_MAX_PATH) {
	        dk3str_cpy(back->fullname, dn);
		dk3str_cat(back->fullname, kw[2]);
		dk3str_cat(back->fullname, de->d_name);
		if(dk3sf_stat(&stb,back->fullname)) {
		  switch((stb.ft) & (~(DK3_FT_SYMLINK))) {
		    case DK3_FT_DIRECTORY: {
		      dk3dir_add(back->sdi,de->d_name,&(back->ndir),&ec,app);
		    } break;
		    default: {
		      dk3dir_add(back->sfi,de->d_name,&(back->nfi),&ec,app);
		    } break;
		  }
		} else {
		  if(!(ec & 8)) {
		    if(app) {
		      /* Stat failed! */
		      dk3app_log_i3(app,DK3_LL_ERROR,61,62,back->fullname);
		    }
		  } ec |= 8;
		}
	      } else {
	        if(!(ec & 4)) {
		  if(app) {
		    /* Directory name too long! */
		    dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
		  }
		} ec |= 4;
	      }
	    }
	  }
	}
        closedir(d);
      } else {
        ec = 1;
        if(app) {
	  /* Failed to open directory! */
	  dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
	  dk3sf_report_errno(app, errno, dk3dir_function_names[0]);
	}
      }
      if(ec) {
        dk3dir_close(back); back = NULL;
      }
    }
  } 

#line 1087 "dk3dir.ctr"
  return back;
}



dk3_dir_t *
dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
{
  dk3_dir_t *back = NULL;
  int		ec = 0;			/* Error code. */
  dk3_stat_t	stb;			/* Stat buffer. */
  dkChar	mybuffer[DK3_MAX_PATH];	/* Buffer for entire file name. */
  dkChar	myb2[2];		/* Pattern buffer. */
  dkChar	*ptr;			/* Last separator in pattern. */
  

#line 1102 "dk3dir.ctr"
  if(fn) {
    if(dk3str_len(fn) < DK3_MAX_PATH) {
      dk3str_cpy(mybuffer, fn);
      if(dk3sf_stat_app(&stb, fn, app)) {
        ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP);
        if(ptr) {
          *(ptr++) = DK3_CHAR_0;
	  back = dk3dir_new(mybuffer, app);
        } else {
          myb2[0] = DK3_CHAR_0;
	  back = dk3dir_new(myb2, app);
	  ptr = mybuffer;
        }
        if(back) {
	  switch((stb.ft) & (~(DK3_FT_SYMLINK))) {
	    case DK3_FT_DIRECTORY: {
	      dk3dir_add(back->sdi, ptr, &(back->ndir), &ec, app);
	    } break;
	    default: {
	      dk3dir_add(back->sfi, ptr, &(back->nfi), &ec, app);
	    } break;
	  }
	  if(ec) {
	    dk3dir_close(back); back = NULL;
	  }
        }
      }
    } else {
      if(app) {
        /* Name too long! */
	dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn);
      }
    }
  } 

#line 1136 "dk3dir.ctr"
  return back;
}



#else
#error	"No opendir()/readdir()/closedir() function set!"
#endif

/* --- non-Windows, 8 bit --- */
#endif
/* --- non-Windows --- */
#endif


/**	Directory chain cell.
*/
struct _dk3_dir_cell_t_ {
  struct _dk3_dir_cell_t_	*parent;	/**< Parent cell. */
  dk3_dir_t			*dir;		/**< Directory structure. */
};
/**	Directory chain cell.
*/
typedef struct _dk3_dir_cell_t_  DK3_DIR_CELL;



/**	Destroy chain cell, release memory.
	@param	c	Cell to destroy.
*/
static
void
dk3dir_cell_delete(DK3_DIR_CELL *c)
{
  if(c) {
    if(c->dir) {
      dk3dir_close(c->dir);
    }
    c->dir = NULL;
    c->parent = NULL;
    dk3_delete(c);
  }
}



/**	Create directory cell, allocate memory.
	@param	n	Directory name.
	@param	p	Parent cell in chain.
	@param	app	Application structure for diagnostics.
	@return	Pointer to new cell on success, NULL on error.
*/
static
DK3_DIR_CELL *
dk3dir_cell_new(dkChar const *n, DK3_DIR_CELL *p, dk3_app_t *app)
{
  DK3_DIR_CELL *back = NULL;
  back = dk3_new_app(DK3_DIR_CELL,1,app);
  if(back) {
    back->parent = p;
    back->dir = dk3dir_open_app(n, app);
    if(!(back->dir)) {
      dk3dir_cell_delete(back); back = NULL;
    }
  }
  return back;
}



#if DK3_ON_WINDOWS
/**	Remove a directory reparse point.
	@param	dn	Directory to remove.
	@param	app	Application structure for diagnostics, may be NULL.
	@return	1 on success, 0 on error.
*/
static
int
dk3dir_remove_reparse_point(dkChar const *dn, dk3_app_t *app)
{
  int	back = 0;
  if(dk3sf_remove_dir_app(dn, app)) {
    back = 1;
  } else {
    if(dk3sf_remove_file_app(dn, app)) {
      back = 1;
    }
  }
  return back;
}
#endif



int
dk3dir_remove_app(dkChar const *dn, dk3_app_t *app)
{
  int back = 0;
  dk3_stat_t	stb;		/* Stat buffer. */
  dkChar const	*en = NULL;	/* Entry name. */
  dkChar const	*xdn;		/* Current dir to delete. */
  dk3_stat_t	*es = NULL;	/* Entry stat buffer. */
  DK3_DIR_CELL	*ccell = NULL;	/* Current cell. */
  DK3_DIR_CELL	*ncell = NULL;	/* New cell or next cell. */
  int		 ft;		/* File type. */
  if(dn) {
    if(dk3sf_stat_app(&stb, dn, app)) {
      if((stb.ft) & DK3_FT_SYMLINK) {	/* symbolic link */
        /* Removing symbolic link only, not target! */
	back = dk3sf_remove_file_app(dn, app);
      } else {
        if(stb.ft == DK3_FT_DIRECTORY) {

#if DK3_ON_WINDOWS
	  if(0x00 == stb.cReparse) {
#endif
	    ccell = dk3dir_cell_new(dn, NULL, app);
	    if(ccell) {
	      back = 1;
	      while(ccell) {
	        if(dk3dir_get_next_directory(ccell->dir)) {
	          en = dk3dir_get_fullname(ccell->dir);
	          if(en) {
	            es = dk3dir_get_stat(ccell->dir);
		    if(es) {
		      if((es->ft) & DK3_FT_SYMLINK) {
		        if(!dk3sf_remove_file_app(en, app)) {
		          back = 0;
		        }
		      } else {
#if DK3_ON_WINDOWS
			if(0x00 == es->cReparse) {
#endif
		          ncell = dk3dir_cell_new(en, ccell, app);
		          if(ncell) {
		            ccell = ncell;
		          } else {
		            back = 0;
		          }
#if DK3_ON_WINDOWS
			} else {
			  if(!dk3dir_remove_reparse_point(en, app)) {
			    back = 0;
			  }
			}
#endif
		      }
		    } else {
		      back = 0;
		      /* BUG: Internal error. */
		    }
	          } else {
		    back = 0;
		    /* BUG: Internal error. */
		  }
	        } else {
	          while(dk3dir_get_next_file(ccell->dir)) {
		    ft = DK3_FT_REGULAR;
		    en = dk3dir_get_fullname(ccell->dir);
		    es = dk3dir_get_stat(ccell->dir);
		    if(es) { ft = es->ft; }
		    if(en) {
		      if(!dk3sf_remove_file_app(en, app)) {
		        back = 0;
		      }
		    } else {
		      back = 0;
		      /* BUG: Internal error. */
		    }
		  }
		  ncell = ccell->parent;
		  if(ccell->dir) {
		    xdn = dk3dir_get_directory_name(ccell->dir);
		    if(xdn) {
		      if(!dk3sf_remove_dir_app(xdn, app)) {
		        back = 0;
		      }
		    }
		  }
		  dk3dir_cell_delete(ccell);
		  ccell = ncell;
	        }
	      }
	    }
#if DK3_ON_WINDOWS
	  } else {
	    if(!dk3dir_remove_reparse_point(dn, app)) {
	      back = 0;
	    }
	  }
#endif
	} else {
	  /* ERROR: Not a directory! */
	  dk3app_log_i3(app, DK3_LL_ERROR, 202, 203, dn);
	}
      }
    }
  }
  return back;
}



/* vim: set ai sw=2 : */

