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