1 /*
2 Copyright (C) 2011-2021, Dirk Krause
3 SPDX-License-Identifier: BSD-3-Clause
4 */
5 
6 /*
7 	WARNING: This file was generated by the dkct program (see
8 	http://dktools.sourceforge.net/ for details).
9 	Changes you make here will be lost if dkct is run again!
10 	You should modify the original source and run dkct on it.
11 	Original source: dk3dir.ctr
12 */
13 
14 /**	@file dk3dir.c The dk3dir module.
15 */
16 
17 
18 #include <libdk3c/dk3all.h>
19 #include <libdk3c/dk3unused.h>
20 
21 
22 
23 
24 
25 
26 
27 /*	Error flags used in ec:
28 	1	Failed to allocate memory for name to save.
29 	2	Failed to save name to storage.
30 	4	Directory name too long.
31 	8	stat() failed for full name.
32 	16	Failed to list directory contents.
33 */
34 
35 /**	Keywords and texts used in this module.
36 */
37 static dkChar const * const kw[] = {
38 /*  0 */ dkT("."),
39 /*  1 */ dkT(".."),
40 #if DK3_ON_WINDOWS || DK3_HAVE_BACKSLASH
41 /*  2 */ dkT("\\"),
42 /*  3 */ dkT("\\*"),
43 #else
44 /*  2 */ dkT("/"),
45 /*  3 */ dkT("/*"),
46 #endif
47 /*  4 */ dkT("*"),
48 NULL
49 };
50 
51 
52 
53 /**	Compare two directory entries by name.
54 	@param	l	Left name.
55 	@param	r	Right name.
56 	@param	cr	Comparison criteria (ignored).
57 	@return	Comparison result.
58 */
59 static
60 int
dk3dir_compare_by_name(void const * l,void const * r,int DK3_ARG_UNUSED (cr))61 dk3dir_compare_by_name(void const *l, void const *r, int DK3_ARG_UNUSED(cr) )
62 {
63   int back = 0;
64 
65   DK3_UNUSED_ARG(cr)
66   if(l) {
67     if(r) {
68 #if DK3_ON_WINDOWS || DK3_HAVE_FNCASEINS
69       back = dk3str_casecmp((dkChar const *)l,(dkChar const *)r);
70 #else
71       back = dk3str_cmp((dkChar const *)l,(dkChar const *)r);
72 #endif
73     } else { back =  1; }
74   } else {
75     if(r)  { back = -1; }
76   }
77   return back;
78 }
79 
80 
81 
82 /**	Release all directory entries in a storage.
83 	@param	s	Storage to clean up.
84 	@param	i	Iterator for \a s.
85 */
86 static
87 void
dk3dir_release_entries(dk3_sto_t * s,dk3_sto_it_t * i)88 dk3dir_release_entries(dk3_sto_t *s, dk3_sto_it_t *i)
89 {
90   dkChar *p = NULL;	/* Current name to release. */
91 
92   if(s) {
93     if(i) {
94       dk3sto_it_reset(i);
95       while((p = (dkChar *)dk3sto_it_next(i)) != NULL) {
96 
97         dk3_delete(p);
98       }
99       dk3sto_it_close(i);
100     }
101     dk3sto_close(s);
102   }
103 }
104 
105 
106 
107 /**	Close directory.
108 	@param	dp	Directory to close.
109 */
110 void
dk3dir_close(dk3_dir_t * dp)111 dk3dir_close(dk3_dir_t *dp)
112 {
113 
114   if(dp) {
115     dk3dir_release_entries(dp->sdi, dp->idi);
116     dk3dir_release_entries(dp->sfi, dp->ifi);
117     dp->sdi = NULL; dp->sfi = NULL; dp->idi = NULL; dp->ifi = NULL;
118     dk3_release(dp->dirname);
119     dk3_release(dp->fullname);
120     dp->app = NULL;
121     dp->ndir = dp->nfi = DK3_UM_0;
122     dk3_delete(dp);
123   }
124 }
125 
126 
127 
128 /**	Create new dir structure.
129 	@param	dn	Directory name.
130 	@param	app	Application structure for diagnostics, may be NULL.
131 	@return	Pointer to new structure on success, NULL on error.
132 */
133 static
134 dk3_dir_t *
dk3dir_new(dkChar const * dn,dk3_app_t * app)135 dk3dir_new(dkChar const *dn, dk3_app_t *app)
136 {
137   dk3_dir_t *back = NULL;
138   int ok = 0;			/* Flag: Success. */
139 
140   back = dk3_new_app(dk3_dir_t,1,app);
141   if(back) {
142     back->sdi = NULL; back->sfi = NULL; back->idi = NULL; back->ifi = NULL;
143     back->dirname = NULL; back->fullname = NULL; back->app = app;
144     back->ndir = back->nfi = DK3_UM_0;
145     back->sdi = dk3sto_open_app(app);
146     if(back->sdi) {
147       back->idi = dk3sto_it_open(back->sdi);
148       if(back->idi) {
149         back->sfi = dk3sto_open_app(app);
150 	if(back->sfi) {
151 	  back->ifi = dk3sto_it_open(back->sfi);
152 	  if(back->ifi) {
153 	    dk3sto_set_comp(back->sdi,dk3dir_compare_by_name,0);
154 	    dk3sto_set_comp(back->sfi,dk3dir_compare_by_name,0);
155 	    back->dirname = dk3str_dup_app(dn, NULL);
156 	    if(back->dirname) {
157 	      back->fullname = dk3_new_app(dkChar,DK3_MAX_PATH,app);
158 	      if(back->fullname) {
159 	        ok = 1;
160 	      }
161 	    }
162 	  }
163 	}
164       }
165     }
166     if(!ok) {
167       dk3dir_close(back); back = NULL;
168     }
169   }
170   return back;
171 }
172 
173 
174 
175 dkChar const *
dk3dir_get_directory_name(dk3_dir_t const * dir)176 dk3dir_get_directory_name(dk3_dir_t const *dir)
177 {
178   dkChar const *back = NULL;
179 
180   if(dir) {
181     back = dir->dirname;
182   }
183   return back;
184 }
185 
186 
187 
188 void
dk3dir_reset(dk3_dir_t * d)189 dk3dir_reset(dk3_dir_t *d)
190 {
191 
192   if(d) {
193     dk3sto_it_reset(d->idi);
194     dk3sto_it_reset(d->ifi);
195     d->shortname = NULL;
196   }
197 }
198 
199 
200 
201 dk3_um_t
dk3dir_get_number_of_directories(dk3_dir_t const * d)202 dk3dir_get_number_of_directories(dk3_dir_t const *d)
203 {
204   dk3_um_t back = DK3_UM_0;
205   if(d) {
206     back = d->ndir;
207   }
208   return back;
209 }
210 
211 
212 
213 dk3_um_t
dk3dir_get_number_of_files(dk3_dir_t const * d)214 dk3dir_get_number_of_files(dk3_dir_t const *d)
215 {
216   dk3_um_t back = DK3_UM_0;
217   if(d) {
218     back = d->nfi;
219   }
220   return back;
221 }
222 
223 
224 
225 dkChar const *
dk3dir_get_fullname(dk3_dir_t const * d)226 dk3dir_get_fullname(dk3_dir_t const *d)
227 {
228   dkChar const *back = NULL;
229 
230   if(d) {
231     back = (dkChar const *)(d->fullname);
232   }
233   return back;
234 }
235 
236 
237 
238 dk3_stat_t const *
dk3dir_get_stat(dk3_dir_t * d)239 dk3dir_get_stat(dk3_dir_t *d)
240 {
241   dk3_stat_t *back = NULL;
242 
243   if(d) {
244     back = &(d->stb);
245   }
246   return back;
247 }
248 
249 
250 
251 dkChar const *
dk3dir_get_shortname(dk3_dir_t const * d)252 dk3dir_get_shortname(dk3_dir_t const *d)
253 {
254   dkChar const *back = NULL;
255 
256   if(d) {
257     back = (dkChar const *)(d->shortname);
258   }
259   return back;
260 }
261 
262 
263 
264 /**	Get next entry.
265 	@param	d	Directory structure.
266 	@param	s	Iterator for the storage.
267 	@param	isdir	Flag: Current entry is a directory.
268 	@return	1 on success, 0 on error.
269 */
270 static
271 int
dk3dir_get_next_entry(dk3_dir_t * d,dk3_sto_it_t * s,int isdir)272 dk3dir_get_next_entry(dk3_dir_t *d, dk3_sto_it_t *s, int isdir)
273 {
274   int		back = 0;
275   int		cc = 1;		/* Flag: Can continue. */
276   size_t	sz = 0;		/* Directory name length. */
277 
278   do {
279     cc = 0;
280     d->shortname = (dkChar *)dk3sto_it_next(s);
281     if(d->shortname) {
282       cc = 1;
283       if(d->dirname) {
284         if(dk3str_len(d->dirname)) {
285           dk3str_cpy_not_overlapped(d->fullname, d->dirname);
286 	  sz = dk3str_len(d->dirname);
287 	  if(sz > 0) {
288 	    if((d->dirname)[sz - 1] != DK3_CHAR_SEP) {
289 	      dk3str_cat(d->fullname, kw[2]);
290 	    }
291 	  } else {
292 	    dk3str_cat(d->fullname, kw[2]);
293 	  }
294 	  dk3str_cat(d->fullname, d->shortname);
295         } else {
296           dk3str_cpy_not_overlapped(d->fullname, d->shortname);
297         }
298       } else {
299         dk3str_cpy_not_overlapped(d->fullname, d->shortname);
300       }
301       if(dk3sf_stat_app(&(d->stb), d->fullname, (dk3_app_t *)(d->app))) {
302         switch(((d->stb).ft) & (~(DK3_FT_SYMLINK))) {
303 	  case DK3_FT_DIRECTORY: {
304 	    if(isdir) {
305 	      back = 1; cc = 0;
306 	    }
307 	  } break;
308 	  default: {
309 	    if(!isdir) {
310 	      back = 1; cc = 0;
311 	    }
312 	  } break;
313 	}
314       }
315     }
316   } while(cc);
317   return back;
318 }
319 
320 
321 
322 int
dk3dir_get_next_directory(dk3_dir_t * d)323 dk3dir_get_next_directory(dk3_dir_t *d)
324 {
325   int back = 0;
326 
327   if(d) {
328     back = dk3dir_get_next_entry(d, d->idi, 1);
329   }
330   return back;
331 }
332 
333 
334 
335 int
dk3dir_get_next_file(dk3_dir_t * d)336 dk3dir_get_next_file(dk3_dir_t *d)
337 {
338   int back = 0;
339 
340   if(d) {
341     back = dk3dir_get_next_entry(d, d->ifi, 0);
342   }
343   return back;
344 }
345 
346 
347 
348 /**	Add a found item to a storage.
349 	@param	s	Storage to add item to.
350 	@param	n	Item name.
351 	@param	c	Pointer to variable to increase to.
352 	@param	ec	Pointer to error code variable.
353 	@param	app	Application structure for diagnostics.
354 */
355 static
356 void
dk3dir_add(dk3_sto_t * s,dkChar const * n,dk3_um_t * c,int * ec,dk3_app_t * app)357 dk3dir_add(dk3_sto_t *s, dkChar const *n, dk3_um_t *c, int *ec, dk3_app_t *app)
358 {
359   dkChar	*newname = NULL;	/* New copy of name. */
360   dk3_um_t	mu = DK3_UM_0;	/* Number of entries. */
361 
362   newname = dk3str_dup_app(n,(((*ec) & 1) ? NULL : app));
363   if(newname) {
364     if(dk3sto_add(s, newname)) {
365       mu = *c;
366       mu++;
367       *c = mu;
368     } else {
369       dk3_delete(newname);
370       *ec |= 2;
371     }
372   } else {
373     *ec |= 1;
374   }
375 }
376 
377 
378 
379 #if DK3_ON_WINDOWS
380 /* +++ Windows +++ */
381 #if DK3_CHAR_SIZE > 1
382 /* +++ Windows > 8 bit +++ */
383 
384 
385 #if VERSION_BEFORE_2012_04_13
386 
387 dk3_dir_t *
dk3dir_open_app(dkChar const * dn,dk3_app_t * app)388 dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
389 {
390   dk3_dir_t		*back = NULL;
391   int			ec = 0;			/* Error code. */
392   dkChar		buffer[DK3_MAX_PATH];	/* Buffer for entry names. */
393   intptr_t		ffres;			/* Result of findfirst(). */
394   dkChar		*namecopy = NULL;	/* New copy of name. */
395   struct _wfinddata64_t	ffdata;			/* Find data. */
396   if(dn) {
397     if((dk3str_len(kw[3]) + dk3str_len(dn)) < DK3_SIZEOF(buffer,dkChar)) {
398       dk3str_cpy_not_overlapped(buffer, dn);
399       dk3str_cat(buffer, kw[3]);
400       back = dk3dir_new(dn, app);
401       if(back) {
402         ffres = _wfindfirst64(buffer, &ffdata);
403 	if(ffres != -1) {
404 	  do {
405 	    if(dk3str_cmp(ffdata.name, kw[0])) {
406 	      if(dk3str_cmp(ffdata.name, kw[1])) {
407 		if((ffdata.attrib) & _A_SUBDIR) {
408 		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
409 		} else {
410 		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
411 		}
412 	      }
413 	    }
414 	  } while(_wfindnext64(ffres, &ffdata) == 0);
415 	  _findclose(ffres);
416 	} else {
417 	  ec |= 16;
418 	  if(app) {
419 	    /* ERROR: Failed to open directory! */
420 	    dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
421 	  }
422 	}
423       }
424       if(ec) {
425 	if(3 & ec) {
426 	  if(app) {
427 	    dk3app_log_i1(app, DK3_LL_ERROR, 9);
428 	  }
429 	}
430         dk3dir_close(back); back = NULL;
431       }
432     } else {
433       if(app) {
434         /* ERROR: Directory name too long! */
435 	dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
436       }
437     }
438   }
439   return back;
440 }
441 
442 
443 dk3_dir_t *
dk3dir_fne_open_app(dkChar const * fn,dk3_app_t * app)444 dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
445 {
446   dk3_dir_t		*back = NULL;
447   int			ec = 0;			/* Error code. */
448   dkChar		mybuffer[DK3_MAX_PATH];	/* Buffer for entry names. */
449   dkChar		myb2[2];		/* Search pattern. */
450   dkChar		*ptr = NULL;		/* Last separator. */
451   dkChar		*namecopy = NULL;	/* New copy of entry name. */
452   intptr_t		ffres;			/* Result from findfirst(). */
453   struct _wfinddata64_t	ffdata;			/* Find data. */
454 
455   if(fn) {
456     if(dk3str_len(fn) < DK3_MAX_PATH) {
457       dk3str_cpy_not_overlapped(mybuffer, fn);
458       ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP);
459       if(ptr) {
460 	*ptr = DK3_CHAR_0;
461 	back = dk3dir_new(mybuffer, app);
462 	*ptr = DK3_CHAR_SEP;
463       } else {
464         myb2[0] = DK3_CHAR_0;
465 	back = dk3dir_new(myb2, app);
466       }
467       if(back) {
468         ffres = _wfindfirst64(mybuffer, &ffdata);
469 	if(ffres != -1) {
470 	  do {
471 	    if(dk3str_cmp(ffdata.name, kw[0])) {
472 	      if(dk3str_cmp(ffdata.name, kw[1])) {
473 	        if((ffdata.attrib) & _A_SUBDIR) {
474 		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
475 		} else {
476 		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
477 		}
478 	      }
479 	    }
480 	  } while(_wfindnext64(ffres, &ffdata) == 0);
481 	  _findclose(ffres);
482 	} else {
483 	  ec |= 16;
484 	  if(app) {
485 	    /* ERROR: Failed to open directory! */
486 	    dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, mybuffer);
487 	  }
488 	}
489       }
490       if(ec) {
491 	if(3 & ec) {
492 	  if(app) {
493 	    dk3app_log_i1(app, DK3_LL_ERROR, 9);
494 	  }
495 	}
496         dk3dir_close(back); back = NULL;
497       }
498     } else {
499       /* ERROR: Pattern too long! */
500       dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn);
501     }
502   }
503   return back;
504 }
505 
506 
507 #else
508 
509 
510 static
511 dk3_dir_t *
dk3dir_my_open_app(dkChar const * dn,int usefne,dk3_app_t * app)512 dk3dir_my_open_app(dkChar const *dn, int usefne, dk3_app_t *app)
513 {
514   WIN32_FIND_DATAW	 wfd;	/* Details for file. */
515   dkChar		 b1[DK3_MAX_PATH];	/* Directory base. */
516   dkChar		 b2[DK3_MAX_PATH];	/* Pattern. */
517   dkChar		*p1;			/* Separator. */
518   dk3_dir_t		*back = NULL;
519   HANDLE		 hSearch;		/* File search handle. */
520   size_t		 sz;			/* String length. */
521   int			 ok;			/* Flag: No error yet. */
522   int			 ec;			/* Error code. */
523   int			 isDir;			/* Flag: Entry is dir. */
524 
525   ec = 0;
526   if(dn) {
527     ok = 0;
528     if(usefne) {
529       if(dk3str_len(dn) < DK3_SIZEOF(b1,dkChar)) {
530         ok = 1;
531         dk3str_cpy_not_overlapped(b1, dn);
532 	dk3str_cpy_not_overlapped(b2, dn);
533 	p1 = dk3str_rchr(b1, DK3_CHAR_SEP);
534 	if(p1) {
535 	  *p1 = dkT('\0');
536 	} else {
537 	  b1[0] = dkT('\0');
538 	}
539       } else {
540         /* ERROR: Name too long! */
541 	if(app) {
542 	  dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, dn);
543 	}
544       }
545     } else {
546       sz = dk3str_len(dn) + dk3str_len(kw[3]);
547       if(sz < DK3_SIZEOF(b1,dkChar)) {
548         ok = 1;
549 	dk3str_cpy_not_overlapped(b1, dn);
550 	dk3str_cpy_not_overlapped(b2, dn);
551 	sz = dk3str_len(b2);
552 	if(sz > 1) {
553 	  dk3str_cat(b2, kw[(DK3_CHAR_SEP == b2[sz - 1]) ? 4 : 3]);
554 	} else {
555 	  dk3str_cat(b2, kw[3]);
556 	}
557       } else {
558       	/* Name too long! */
559 	if(app) {
560 	  dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
561 	}
562       }
563     }
564     if(ok) {
565       back = dk3dir_new(b1, app);
566       if(back) {
567         hSearch = FindFirstFileW(b2, &wfd);
568 	if(INVALID_HANDLE_VALUE != hSearch) {
569 	  do {
570 	    isDir = 0;
571 	    if(dk3str_cmp(wfd.cFileName, kw[0])) {
572 	      if(dk3str_cmp(wfd.cFileName, kw[1])) {
573 	        if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) {
574 		  isDir = 1;
575 #if 0
576 		  if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_REPARSE_POINT) {
577 		    switch(wfd.dwReserved0) {
578 		      case IO_REPARSE_TAG_MOUNT_POINT: {
579 		        isDir = 0;
580 		      } break;
581 		    }
582 		  }
583 #endif
584 		}
585 		if(isDir) {
586 		  dk3dir_add(back->sdi, wfd.cFileName, &(back->ndir), &ec, app);
587 		} else {
588 		  dk3dir_add(back->sfi, wfd.cFileName, &(back->nfi), &ec, app);
589 		}
590 	      }
591 	    }
592 	  } while(FindNextFileW(hSearch, &wfd));
593 	  FindClose(hSearch);
594 	} else {
595 	  /* ERROR: Failed to search for pattern! */
596 	  ec |= 16;
597 	  if(app) {
598 	    if(usefne) {
599 	      dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, dn);
600 	    } else {
601 	      dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
602 	    }
603 	  }
604 	}
605 	if(ec) {
606 	  if(3 & ec) {
607 	    if(app) {
608 	      dk3app_log_i1(app, DK3_LL_ERROR, 9);
609 	    }
610 	  }
611 	  dk3dir_close(back); back = NULL;
612 	}
613       } else {
614       }
615     }
616   } else {
617   }
618   return back;
619 }
620 
621 
622 
623 dk3_dir_t *
dk3dir_open_app(dkChar const * dn,dk3_app_t * app)624 dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
625 {
626   dk3_dir_t		*back;
627 
628   back = dk3dir_my_open_app(dn, 0, app);
629 
630   return back;
631 }
632 
633 
634 dk3_dir_t *
dk3dir_fne_open_app(dkChar const * fn,dk3_app_t * app)635 dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
636 {
637   dk3_dir_t		*back;
638 
639   back = dk3dir_my_open_app(fn, 1, app);
640 
641   return back;
642 }
643 
644 
645 #endif
646 
647 
648 /* --- Windows > 8 bit --- */
649 #else
650 /* +++ Windows, 8 bit +++ */
651 
652 
653 
654 #if VERSION_BEFORE_2012_04_13
655 
656 dk3_dir_t *
dk3dir_open_app(dkChar const * dn,dk3_app_t * app)657 dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
658 {
659   dk3_dir_t		*back = NULL;
660   int			ec = 0;			/* Error code. */
661   dkChar		buffer[DK3_MAX_PATH];	/* Buffer for entry names. */
662   intptr_t		ffres;			/* Result from findfirst(). */
663   dkChar		*namecopy = NULL;	/* New copy of entry name. */
664   struct __finddata64_t	ffdata;			/* Find data. */
665   if(dn) {
666     if((dk3str_len(kw[3]) + dk3str_len(dn)) < DK3_SIZEOF(buffer,dkChar)) {
667       dk3str_cpy_not_overlapped(buffer, dn);
668       dk3str_cat(buffer, kw[3]);
669       back = dk3dir_new(dn, app);
670       if(back) {
671         ffres = _findfirst64(buffer, &ffdata);
672 	if(ffres != -1) {
673 	  do {
674 	    if(dk3str_cmp(ffdata.name, kw[0])) {
675 	      if(dk3str_cmp(ffdata.name, kw[1])) {
676 		if((ffdata.attrib) & _A_SUBDIR) {
677 		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
678 		} else {
679 		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
680 		}
681 	      }
682 	    }
683 	  } while(_findnext64(ffres, &ffdata) == 0);
684 	  _findclose(ffres);
685 	} else {
686 	  ec |= 16;	/* Findfirst failed */
687 	  if(app) {
688 	    /* ERROR: Findfirst failed! */
689 	    dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
690 	  }
691 	}
692       }
693       if(ec) {
694 	if(3 & ec) {
695 	  if(app) {
696 	    dk3app_log_i1(app, DK3_LL_ERROR, 9);
697 	  }
698 	}
699         dk3dir_close(back); back = NULL;
700       }
701     } else {
702       if(app) {
703         /* ERROR: Directory name too long! */
704 	dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
705       }
706     }
707   }
708   return back;
709 }
710 
711 
712 
713 dk3_dir_t *
dk3dir_fne_open_app(dkChar const * fn,dk3_app_t * app)714 dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
715 {
716   dk3_dir_t		*back = NULL;
717   int			ec = 0;			/* Error code. */
718   dkChar		mybuffer[DK3_MAX_PATH];	/* Buffer for entry name. */
719   dkChar		myb2[2];		/* Buffer for pattern. */
720   dkChar		*ptr = NULL;		/* Last separator. */
721   dkChar		*namecopy = NULL;	/* New copy of entry name. */
722   intptr_t		ffres;			/* Result from findfirst(). */
723   struct __finddata64_t	ffdata;			/* Find data. */
724 
725   if(fn) {
726     if(dk3str_len(fn) < DK3_MAX_PATH) {
727       dk3str_cpy_not_overlapped(mybuffer, fn);
728       ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP);
729       if(ptr) {
730 	*ptr = DK3_CHAR_0;
731 	back = dk3dir_new(mybuffer, app);
732 	*ptr = DK3_CHAR_SEP;
733       } else {
734         myb2[0] = DK3_CHAR_0;
735 	back = dk3dir_new(myb2, app);
736       }
737       if(back) {
738         ffres = _findfirst64(mybuffer, &ffdata);
739 	if(ffres != -1) {
740 	  do {
741 	    if(dk3str_cmp(ffdata.name, kw[0])) {
742 	      if(dk3str_cmp(ffdata.name, kw[1])) {
743 		if((ffdata.attrib) & _A_SUBDIR) {
744 		  dk3dir_add(back->sdi, ffdata.name, &(back->ndir), &ec, app);
745 		} else {
746 		  dk3dir_add(back->sfi, ffdata.name, &(back->nfi), &ec, app);
747 		}
748 	      }
749 	    }
750 	  } while(_findnext64(ffres, &ffdata) == 0);
751 	  _findclose(ffres);
752 	} else {
753 	  ec |= 16;
754 	  if(app) {
755 	    /* ERROR: Failed to open directory! */
756 	    dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, mybuffer);
757 	  }
758 	}
759       }
760       if(ec) {
761 	if(3 & ec) {
762 	  if(app) {
763 	    dk3app_log_i1(app, DK3_LL_ERROR, 9);
764 	  }
765 	}
766         dk3dir_close(back); back = NULL;
767       }
768     } else {
769       if(app) {
770         /* ERROR: Pattern too long! */
771 	dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn);
772       }
773     }
774   }
775   return back;
776 }
777 
778 
779 #else
780 
781 
782 
783 static
784 dk3_dir_t *
dk3dir_my_open_app(dkChar const * dn,int usefne,dk3_app_t * app)785 dk3dir_my_open_app(dkChar const *dn, int usefne, dk3_app_t *app)
786 {
787   WIN32_FIND_DATAA	 wfd;	/* Details for file. */
788   dkChar		 b1[DK3_MAX_PATH];	/* Directory base. */
789   dkChar		 b2[DK3_MAX_PATH];	/* Pattern. */
790   dkChar		*p1;			/* Separator. */
791   dk3_dir_t		*back = NULL;
792   HANDLE		 hSearch;		/* File search handle. */
793   size_t		 sz;			/* String length. */
794   int			 ok;			/* Flag: No error yet. */
795   int			 ec;			/* Error code. */
796   int			 isDir;			/* Flag: Entry is dir. */
797 
798   ec = 0;
799   if(dn) {
800     ok = 0;
801     if(usefne) {
802       if(dk3str_len(dn) < DK3_SIZEOF(b1,dkChar)) {
803         ok = 1;
804         dk3str_cpy_not_overlapped(b1, dn);
805 	dk3str_cpy_not_overlapped(b2, dn);
806 	p1 = dk3str_rchr(b1, DK3_CHAR_SEP);
807 	if(p1) {
808 	  *p1 = dkT('\0');
809 	} else {
810 	  b1[0] = dkT('\0');
811 	}
812       } else {
813         /* ERROR: Name too long! */
814 	if(app) {
815 	  dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, dn);
816 	}
817       }
818     } else {
819       sz = dk3str_len(dn) + dk3str_len(kw[3]);
820       if(sz < DK3_SIZEOF(b1,dkChar)) {
821         ok = 1;
822 	dk3str_cpy_not_overlapped(b1, dn);
823 	dk3str_cpy_not_overlapped(b2, dn);
824 	sz = dk3str_len(b2);
825 	if(sz > 1) {
826 	  dk3str_cat(b2, kw[(DK3_CHAR_SEP == b2[sz - 1]) ? 4 : 3]);
827 	} else {
828 	  dk3str_cat(b2, kw[3]);
829 	}
830       } else {
831       	/* ERROR: Name too long! */
832 	if(app) {
833 	  dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
834 	}
835       }
836     }
837     if(ok) {
838       back = dk3dir_new(b1, app);
839       if(back) {
840         hSearch = FindFirstFileA(b2, &wfd);
841 	if(INVALID_HANDLE_VALUE != hSearch) {
842 	  do {
843 	    isDir = 0;
844 	    if(dk3str_cmp(wfd.cFileName, kw[0])) {
845 	      if(dk3str_cmp(wfd.cFileName, kw[1])) {
846 	        if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) {
847 		  isDir = 1;
848 #if 0
849 		  if((wfd.dwFileAttributes) & FILE_ATTRIBUTE_REPARSE_POINT) {
850 		    switch(wfd.dwReserved0) {
851 		      case IO_REPARSE_TAG_MOUNT_POINT: {
852 		        isDir = 0;
853 		      } break;
854 		    }
855 		  }
856 #endif
857 		}
858 		if(isDir) {
859 		  dk3dir_add(back->sdi, wfd.cFileName, &(back->ndir), &ec, app);
860 		} else {
861 		  dk3dir_add(back->sfi, wfd.cFileName, &(back->nfi), &ec, app);
862 		}
863 	      }
864 	    }
865 	  } while(FindNextFileA(hSearch, &wfd));
866 	  FindClose(hSearch);
867 	} else {
868 	  /* ERROR: Failed to search for pattern! */
869 	  ec |= 16;
870 	  if(app) {
871 	    if(usefne) {
872 	      dk3app_log_i3(app, DK3_LL_ERROR, 215, 216, dn);
873 	    } else {
874 	      dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
875 	    }
876 	  }
877 	}
878 	if(ec) {
879 	  if(3 & ec) {
880 	    if(app) {
881 	      dk3app_log_i1(app, DK3_LL_ERROR, 9);
882 	    }
883 	  }
884 	  dk3dir_close(back); back = NULL;
885 	}
886       } else {
887       }
888     }
889   } else {
890   }
891   return back;
892 }
893 
894 
895 
896 dk3_dir_t *
dk3dir_open_app(dkChar const * dn,dk3_app_t * app)897 dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
898 {
899   dk3_dir_t		*back;
900 
901   back = dk3dir_my_open_app(dn, 0, app);
902 
903   return back;
904 }
905 
906 
907 dk3_dir_t *
dk3dir_fne_open_app(dkChar const * fn,dk3_app_t * app)908 dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
909 {
910   dk3_dir_t		*back;
911 
912   back = dk3dir_my_open_app(fn, 1, app);
913 
914   return back;
915 }
916 
917 
918 
919 #endif
920 
921 /* --- Windows, 8 bit --- */
922 #endif
923 /* --- Windows --- */
924 #else
925 /* +++ non-Windows +++ */
926 #if DK3_CHAR_SIZE > 1
927 /* +++ non-Windows > 8 bit +++ */
928 #error	"16/32-bit file names only available on Windows!"
929 /* --- non-Windows > 8 bit --- */
930 #else
931 /* +++ non-Windows, 8 bit +++ */
932 
933 
934 
935 /**	Function names used in error messages.
936 */
937 static dkChar const * const dk3dir_function_names[] = {
938 /* 0 */
939 dkT("opendir: "),
940 
941 NULL
942 
943 };
944 
945 
946 
947 #if DK3_HAVE_OPENDIR && DK3_HAVE_READDIR && DK3_HAVE_CLOSEDIR
948 
949 
950 
951 dk3_dir_t *
dk3dir_open_app(dkChar const * dn,dk3_app_t * app)952 dk3dir_open_app(dkChar const *dn, dk3_app_t *app)
953 {
954   dk3_dir_t	*back = NULL;
955   int		ec = 0;		/* Error code. */
956   struct dirent	*de = NULL;	/* Directory entry from readdir(). */
957   DIR		*d = NULL;	/* Directory structure from opendir(). */
958   size_t	sz = 0;		/* Length of entire file name. */
959   size_t	dns = 0;	/* Directory name length. */
960   size_t	kw2l = 0;	/* Length of separator string. */
961   dk3_stat_t	stb;		/* Buffer for stat() result. */
962 
963   if(dn) {
964     back = dk3dir_new(dn, app);
965     if(back) {
966       d = opendir(dn);
967       if(d) {
968         dns = dk3str_len(dn); kw2l = dk3str_len(kw[2]);
969         while((de = readdir(d)) != NULL) {
970 	  if(dk3str_cmp(de->d_name, kw[0])) {
971 	    if(dk3str_cmp(de->d_name, kw[1])) {
972 	      sz = dns + kw2l + dk3str_len(de->d_name);
973 	      if(sz < DK3_MAX_PATH) {
974 	        dk3str_cpy_not_overlapped(back->fullname, dn);
975 		dk3str_cat(back->fullname, kw[2]);
976 		dk3str_cat(back->fullname, de->d_name);
977 		if(dk3sf_stat_app(&stb, back->fullname, NULL)) {
978 		  switch((stb.ft) & (~(DK3_FT_SYMLINK))) {
979 		    case DK3_FT_DIRECTORY: {
980 		      dk3dir_add(back->sdi,de->d_name,&(back->ndir),&ec,app);
981 		    } break;
982 		    default: {
983 		      dk3dir_add(back->sfi,de->d_name,&(back->nfi),&ec,app);
984 		    } break;
985 		  }
986 		} else {
987 		  if(!(ec & 8)) {
988 		    if(app) {
989 		      /* Stat failed! */
990 		      dk3app_log_i3(app,DK3_LL_ERROR,61,62,back->fullname);
991 		    }
992 		  } ec |= 8;
993 		}
994 	      } else {
995 	        if(!(ec & 4)) {
996 		  if(app) {
997 		    /* Directory name too long! */
998 		    dk3app_log_i3(app, DK3_LL_ERROR, 59, 60, dn);
999 		  }
1000 		} ec |= 4;
1001 	      }
1002 	    }
1003 	  }
1004 	}
1005         closedir(d);
1006       } else {
1007         ec |= 16;
1008         if(app) {
1009 	  /* Failed to open directory! */
1010 	  dk3app_log_i3(app, DK3_LL_ERROR, 63, 64, dn);
1011 	  dk3sf_report_errno(app, errno, dk3dir_function_names[0]);
1012 	}
1013       }
1014       if(ec) {
1015 	if(3 & ec) {
1016 	  if(app) {
1017 	    dk3app_log_i1(app, DK3_LL_ERROR, 9);
1018 	  }
1019 	}
1020         dk3dir_close(back); back = NULL;
1021       }
1022     }
1023   }
1024   return back;
1025 }
1026 
1027 
1028 
1029 dk3_dir_t *
dk3dir_fne_open_app(dkChar const * fn,dk3_app_t * app)1030 dk3dir_fne_open_app(dkChar const *fn, dk3_app_t *app)
1031 {
1032   dk3_dir_t *back = NULL;
1033   int		ec = 0;			/* Error code. */
1034   dk3_stat_t	stb;			/* Stat buffer. */
1035   dkChar	mybuffer[DK3_MAX_PATH];	/* Buffer for entire file name. */
1036   dkChar	myb2[2];		/* Pattern buffer. */
1037   dkChar	*ptr;			/* Last separator in pattern. */
1038 
1039   if(fn) {
1040     if(dk3str_len(fn) < DK3_MAX_PATH) {
1041       dk3str_cpy_not_overlapped(mybuffer, fn);
1042       if(dk3sf_stat_app(&stb, fn, app)) {
1043         ptr = dk3str_rchr(mybuffer, DK3_CHAR_SEP);
1044         if(ptr) {
1045           *(ptr++) = DK3_CHAR_0;
1046 	  back = dk3dir_new(mybuffer, app);
1047         } else {
1048           myb2[0] = DK3_CHAR_0;
1049 	  back = dk3dir_new(myb2, app);
1050 	  ptr = mybuffer;
1051         }
1052         if(back) {
1053 	  switch((stb.ft) & (~(DK3_FT_SYMLINK))) {
1054 	    case DK3_FT_DIRECTORY: {
1055 	      dk3dir_add(back->sdi, ptr, &(back->ndir), &ec, app);
1056 	    } break;
1057 	    default: {
1058 	      dk3dir_add(back->sfi, ptr, &(back->nfi), &ec, app);
1059 	    } break;
1060 	  }
1061 	  if(ec) {
1062 	    if(3 & ec) {
1063 	      if(app) {
1064 	        dk3app_log_i1(app, DK3_LL_ERROR, 9);
1065 	      }
1066 	    }
1067 	    dk3dir_close(back); back = NULL;
1068 	  }
1069         }
1070       }
1071     } else {
1072       if(app) {
1073         /* Name too long! */
1074 	dk3app_log_i3(app, DK3_LL_ERROR, 65, 66, fn);
1075       }
1076     }
1077   }
1078   return back;
1079 }
1080 
1081 
1082 
1083 #else
1084 #error	"No opendir()/readdir()/closedir() function set!"
1085 #endif
1086 
1087 /* --- non-Windows, 8 bit --- */
1088 #endif
1089 /* --- non-Windows --- */
1090 #endif
1091 
1092 
1093 /**	Directory chain cell.
1094 */
1095 struct _dk3_dir_cell_t_ {
1096   struct _dk3_dir_cell_t_	*parent;	/**< Parent cell. */
1097   dk3_dir_t			*dir;		/**< Directory structure. */
1098 };
1099 /**	Directory chain cell.
1100 */
1101 typedef struct _dk3_dir_cell_t_  DK3_DIR_CELL;
1102 
1103 
1104 
1105 /**	Destroy chain cell, release memory.
1106 	@param	c	Cell to destroy.
1107 */
1108 static
1109 void
dk3dir_cell_delete(DK3_DIR_CELL * c)1110 dk3dir_cell_delete(DK3_DIR_CELL *c)
1111 {
1112   if(c) {
1113     if(c->dir) {
1114       dk3dir_close(c->dir);
1115     }
1116     c->dir = NULL;
1117     c->parent = NULL;
1118     dk3_delete(c);
1119   }
1120 }
1121 
1122 
1123 
1124 /**	Create directory cell, allocate memory.
1125 	@param	n	Directory name.
1126 	@param	p	Parent cell in chain.
1127 	@param	app	Application structure for diagnostics.
1128 	@return	Pointer to new cell on success, NULL on error.
1129 */
1130 static
1131 DK3_DIR_CELL *
dk3dir_cell_new(dkChar const * n,DK3_DIR_CELL * p,dk3_app_t * app)1132 dk3dir_cell_new(dkChar const *n, DK3_DIR_CELL *p, dk3_app_t *app)
1133 {
1134   DK3_DIR_CELL *back = NULL;
1135   back = dk3_new_app(DK3_DIR_CELL,1,app);
1136   if(back) {
1137     back->parent = p;
1138     back->dir = dk3dir_open_app(n, app);
1139     if(!(back->dir)) {
1140       dk3dir_cell_delete(back); back = NULL;
1141     }
1142   }
1143   return back;
1144 }
1145 
1146 
1147 
1148 #if DK3_ON_WINDOWS
1149 /**	Remove a directory reparse point.
1150 	@param	dn	Directory to remove.
1151 	@param	app	Application structure for diagnostics, may be NULL.
1152 	@return	1 on success, 0 on error.
1153 */
1154 static
1155 int
dk3dir_remove_reparse_point(dkChar const * dn,dk3_app_t * app)1156 dk3dir_remove_reparse_point(dkChar const *dn, dk3_app_t *app)
1157 {
1158   int	back = 0;
1159   if(dk3sf_remove_dir_app(dn, app)) {
1160     back = 1;
1161   } else {
1162     if(dk3sf_remove_file_app(dn, app)) {
1163       back = 1;
1164     }
1165   }
1166   return back;
1167 }
1168 #endif
1169 
1170 
1171 
1172 int
dk3dir_remove_app(dkChar const * dn,dk3_app_t * app)1173 dk3dir_remove_app(dkChar const *dn, dk3_app_t *app)
1174 {
1175   int			 back = 0;
1176   dk3_stat_t		 stb;		/* Stat buffer. */
1177   dkChar const		*en = NULL;	/* Entry name. */
1178   dkChar const		*xdn;		/* Current dir to delete. */
1179   dk3_stat_t const	*es = NULL;	/* Entry stat buffer. */
1180   DK3_DIR_CELL		*ccell = NULL;	/* Current cell. */
1181   DK3_DIR_CELL		*ncell = NULL;	/* New cell or next cell. */
1182 #if VERSION_BEFORE_20150821
1183   int			 ft;		/* File type. */
1184 #endif
1185 
1186   if(dn) {
1187     if(dk3sf_stat_app(&stb, dn, app)) {
1188       if((stb.ft) & DK3_FT_SYMLINK) {	/* symbolic link */
1189         /* Removing symbolic link only, not target! */
1190 	back = dk3sf_remove_file_app(dn, app);
1191       } else {
1192         if(stb.ft == DK3_FT_DIRECTORY) {
1193 
1194 #if DK3_ON_WINDOWS
1195 	  if(0x00 == stb.cReparse) {
1196 #endif
1197 	    ccell = dk3dir_cell_new(dn, NULL, app);
1198 	    if(ccell) {
1199 	      back = 1;
1200 	      while(ccell) {
1201 	        if(dk3dir_get_next_directory(ccell->dir)) {
1202 	          en = dk3dir_get_fullname(ccell->dir);
1203 	          if(en) {
1204 	            es = dk3dir_get_stat(ccell->dir);
1205 		    if(es) {
1206 		      if((es->ft) & DK3_FT_SYMLINK) {
1207 		        if(!dk3sf_remove_file_app(en, app)) {
1208 		          back = 0;
1209 		        }
1210 		      } else {
1211 #if DK3_ON_WINDOWS
1212 			if(0x00 == es->cReparse) {
1213 #endif
1214 		          ncell = dk3dir_cell_new(en, ccell, app);
1215 		          if(ncell) {
1216 		            ccell = ncell;
1217 		          } else {
1218 		            back = 0;
1219 		          }
1220 #if DK3_ON_WINDOWS
1221 			} else {
1222 			  if(!dk3dir_remove_reparse_point(en, app)) {
1223 			    back = 0;
1224 			  }
1225 			}
1226 #endif
1227 		      }
1228 		    } else {
1229 		      back = 0;
1230 		      /* BUG: Internal error. */
1231 		    }
1232 	          } else {
1233 		    back = 0;
1234 		    /* BUG: Internal error. */
1235 		  }
1236 	        } else {
1237 	          while(dk3dir_get_next_file(ccell->dir)) {
1238 #if VERSION_BEFORE_20150821
1239 		    ft = DK3_FT_REGULAR;
1240 #endif
1241 		    en = dk3dir_get_fullname(ccell->dir);
1242 #if VERSION_BEFORE_20150821
1243 		    es = dk3dir_get_stat(ccell->dir);
1244 		    if(es) { ft = es->ft; }
1245 #endif
1246 		    if(en) {
1247 		      if(!dk3sf_remove_file_app(en, app)) {
1248 		        back = 0;
1249 		      }
1250 		    } else {
1251 		      back = 0;
1252 		      /* BUG: Internal error. */
1253 		    }
1254 		  }
1255 		  ncell = ccell->parent;
1256 		  if(ccell->dir) {
1257 		    xdn = dk3dir_get_directory_name(ccell->dir);
1258 		    if(xdn) {
1259 		      if(!dk3sf_remove_dir_app(xdn, app)) {
1260 		        back = 0;
1261 		      }
1262 		    }
1263 		  }
1264 		  dk3dir_cell_delete(ccell);
1265 		  ccell = ncell;
1266 	        }
1267 	      }
1268 	    }
1269 #if DK3_ON_WINDOWS
1270 	  } else {
1271 	    if(!dk3dir_remove_reparse_point(dn, app)) {
1272 	      back = 0;
1273 	    }
1274 	  }
1275 #endif
1276 	} else {
1277 	  /* ERROR: Not a directory! */
1278 	  dk3app_log_i3(app, DK3_LL_ERROR, 202, 203, dn);
1279 	}
1280       }
1281     }
1282   }
1283   return back;
1284 }
1285 
1286 
1287 
1288 /* vim: set ai sw=2 : */
1289 
1290