1%%	options
2
3copyright owner	=	Dirk Krause
4copyright year	=	2015-xxxx
5SPDX-License-Identifier:	BSD-3-Clause
6
7
8
9%%	header
10
11/**	@file
12	Make directory hierarchy (create all parent directories for a
13	file or directory and the directory itself if required),
14	the file or directory name is specified as char string.
15*/
16
17#ifndef DK4CONF_H_INCLUDED
18#if DK4_BUILDING_DKTOOLS4
19#include "dk4conf.h"
20#else
21#include <dktools-4/dk4conf.h>
22#endif
23#endif
24
25#ifndef DK4TYPES_H_INCLUDED
26#if DK4_BUILDING_DKTOOLS4
27#include <libdk4base/dk4types.h>
28#else
29#include <dktools-4/dk4types.h>
30#endif
31#endif
32
33#ifndef DK4ERROR_H_INCLUDED
34#if DK4_BUILDING_DKTOOLS4
35#include <libdk4base/dk4error.h>
36#else
37#include <dktools-4/dk4error.h>
38#endif
39#endif
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44
45/**	Find position of first separator for real directory.
46
47	This function is not intended for use in application programs,
48	it is exported only for use from the dk4appmkdh module.
49
50	The function either returns a value or sets the back variable
51	to 1 or reports an error.
52	@param	fn	File name.
53	@param	bptr	Pointer to the back variable.
54	@param	lc	Flag: Last component in fn is directory too.
55	@param	erp	Error report, may be NULL.
56	@return	First separator after a real directory on success if
57	found, NULL otherwise.
58*/
59char *
60dk4mkdir_hierarchy_c8_first_sep(char *fn, int *bptr, int lc, dk4_er_t *erp);
61
62/**	Check whether directory exists or attempt to create directory.
63	@param	fn	File name.
64	@param	ro	Flag: Read only (test directory presence),
65			do not attempt to create the directory.
66	@param	erp	Error report, may be NULL.
67	@return	1 if directory is available now, 0 otherwise.
68
69	Error codes:
70	- DK4_E_INVALID_ARGUMENTS<br>
71	  if fn is NULL,
72	- DK4_E_NOT_FOUND<br>
73	  if a server/share combination is not present on Windows,
74	- DK4_E_NON_DIR<br>
75	  if any component in the path is not a directory,
76	- DK4_E_MKDIR_FAILED<br>
77	  with errno value in iDetails1 if the mkdir() function failed on a
78	  non-Windows system.
79	- DK4_E_CREATE_DIR_FAILED<br>
80	  with GetLastError() result in lDetails1 if the CreateDirectory()
81	  function failed on a Windows system.
82*/
83int
84dk4mkdir_hierarchy_c8_one(const char *fn, int ro, dk4_er_t *erp);
85
86/**	Create directory structure.
87	@param	fn	File name.
88	@param	lc	Flag: Last component in fn is directory too.
89	@param	erp	Error report, may be NULL.
90	@return	1 on success, 0 on error.
91
92	Error codes:
93	- DK4_E_INVALID_ARGUMENTS<br>
94	  if fn is NULL,
95	- DK4_E_BUFFER_TOO_SMALL<br>
96	  if fn is too long,
97	- DK4_E_MATH_OVERFLOW<br>
98	  if a mathematical overflow occured in size allocation,
99	- DK4_E_NOT_FOUND<br>
100	  if a server/share combination is not present on Windows,
101	- DK4_E_NON_DIR<br>
102	  if one of the components in fn is not a directory,
103	- DK4_E_MKDIR_FAILED<br>
104	  with errno in idetails if the function fails to create the directory
105	  or a parent directory,
106	- DK4_E_CREATE_DIR_FAILED<br>
107	  with GetLastError() result in lDetails1 if the CreateDirectory()
108	  function failed on a Windows system.
109*/
110int
111dk4mkdir_hierarchy_c8(const char *fn, int lc, dk4_er_t *erp);
112
113
114#if DK4_HAVE_UID_T && DK4_HAVE_GID_T && DK4_HAVE_MODE_T && (!DK4_ON_WINDOWS)
115
116/**	Create directory structure for a given POSIX user and group.
117	@param	fn	File name.
118	@param	lc	Flag: Last component in fn is directory too.
119	@param	uid	User ID for new directories.
120	@param	gid	Group ID for new directories.
121	@param	mode	Permissions to new directories.
122	@param	erp	Error report, may be NULL.
123	@return	1 on success, 0 on error.
124
125	Error codes:
126	- DK4_E_INVALID_ARGUMENTS<br>
127	  if fn is NULL,
128	- DK4_E_BUFFER_TOO_SMALL<br>
129	  if fn is too long,
130	- DK4_E_MATH_OVERFLOW<br>
131	  if a mathematical overflow occured in size allocation,
132	- DK4_E_NOT_FOUND<br>
133	  if a server/share combination is not present on Windows,
134	- DK4_E_NON_DIR<br>
135	  if one of the components in fn is not a directory,
136	- DK4_E_MKDIR_FAILED<br>
137	  with errno in idetails if the function fails to create the directory
138	  or a parent directory,
139	- DK4_E_CHOWN_FAILED<br>
140	  with errno value in iDetails1 if the chown() function failed on a
141	  non-Windows system,
142	- DK4_E_CHMOD_FAILED<br>
143	  with errno value in iDetails1 if the chmod() function failed on a
144	  non-Windows system,
145	- DK4_E_CREATE_DIR_FAILED<br>
146	  with GetLastError() result in lDetails1 if the CreateDirectory()
147	  function failed on a Windows system.
148*/
149int
150dk4mkdir_hierarchy_ugm_c8(
151  const char	*fn,
152  int		 lc,
153  uid_t		 uid,
154  gid_t		 gid,
155  mode_t	 mode,
156  dk4_er_t	*erp
157);
158
159#endif
160
161#ifdef __cplusplus
162}
163#endif
164
165
166
167%%	module
168
169#include "dk4conf.h"
170#include <libdk4base/dk4types.h>
171
172#if DK4_ON_WINDOWS
173#ifndef WINDOWS_H_INCLUDED
174#include <windows.h>
175#define	WINDOWS_H_INCLUDED 1
176#endif
177#endif
178
179#include <libdk4c/dk4mkdh8.h>
180
181#if DK4_HAVE_SYS_STAT_H
182#ifndef SYS_STAT_H_INCLUDED
183#include <sys/stat.h>
184#define	SYS_STAT_H_INCLUDED 1
185#endif
186#endif
187
188#if DK4_HAVE_SYS_TYPES_H
189#ifndef SYS_TYPES_H_INCLUDED
190#include <sys/types.h>
191#define	SYS_TYPES_H_INCLUDED 1
192#endif
193#endif
194
195#if DK4_HAVE_DIRECT_H
196#ifndef DIRECT_H_INCLUDED
197#include <direct.h>
198#define	DIRECT_H_INCLUDED 1
199#endif
200#endif
201
202#if DK4_HAVE_STDLIB_H
203#ifndef STDLIB_H_INCLUDED
204#include <stdlib.h>
205#define	STDLIB_H_INCLUDED 1
206#endif
207#endif
208
209#if DK4_HAVE_UNISTD_H
210#ifndef UNISTD_H_INCLUDED
211#include <unistd.h>
212#define	UNISTD_H_INCLUDED 1
213#endif
214#endif
215
216#if DK4_HAVE_ERRNO_H
217#ifndef ERRNO_H_INCLUDED
218#include <errno.h>
219#define	ERRNO_H_INCLUDED 1
220#endif
221#endif
222
223#ifndef	DK4MEM_H_INCLUDED
224#include <libdk4base/dk4mem.h>
225#endif
226
227#ifndef DK4MPL_H_INCLUDED
228#include <libdk4base/dk4mpl.h>
229#endif
230
231#ifndef DK4STR8_H_INCLUDED
232#include <libdk4base/dk4str8.h>
233#endif
234
235#ifndef DK4PATH8_H_INCLUDED
236#include <libdk4c/dk4path8.h>
237#endif
238
239#ifndef DK4STAT8_H_INCLUDED
240#include <libdk4c/dk4stat8.h>
241#endif
242
243#ifndef DK4STAT_H_INCLUDED
244#include <libdk4c/dk4stat.h>
245#endif
246
247#if DK4_HAVE_ASSERT_H
248#ifndef	ASSERT_H_INCLUDED
249#include <assert.h>
250#define	ASSERT_H_INCLUDED 1
251#endif
252#endif
253
254
255$!trace-include
256
257
258
259#if DK4_ON_WINDOWS
260/* +++ Windows */
261
262
263
264/**	Check whether c is a character.
265	@param	c	Byte to check.
266	@return	1 for characters, 0 for other bytes.
267*/
268static
269int
270dk4mkdir_hierarchy_c8_is_char(char c)
271{
272  int	 back = 0;
273  if (('a' <= c) && ('z' >= c)) {
274    back = 1;
275  } else {
276    if (('A' <= c) && ('Z' >= c)) {
277      back = 1;
278    }
279  }
280  return back;
281}
282
283
284
285int
286dk4mkdir_hierarchy_c8_one(const char *fn, int ro, dk4_er_t *erp)
287{
288  BY_HANDLE_FILE_INFORMATION	bhfi;
289  WIN32_FIND_DATAA		ffdata;
290  HANDLE			ha;
291  DWORD				dwattr;
292  int				found;
293  int				i;
294  int		 		back = 0;
295  $? "+ dk4mkdir_hierarchy_c8_one \"%!8s\"", fn
296#if	DK4_USE_ASSERT
297  assert(NULL != fn);
298#endif
299  if (NULL == fn) {
300    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
301    goto finished;
302  }
303#if 1
304  dwattr = GetFileAttributesA(fn);
305  if (INVALID_FILE_ATTRIBUTES == dwattr) {
306    ha = FindFirstFileA(fn, &ffdata);
307    if (INVALID_HANDLE_VALUE != ha) {
308      dwattr = ffdata.dwFileAttributes;
309      FindClose(ha);
310    }
311  }
312  if (INVALID_FILE_ATTRIBUTES != dwattr) {
313    if (0 != (FILE_ATTRIBUTE_DIRECTORY & dwattr)) {
314      back = 1;
315    } else {
316      if (0 != (FILE_ATTRIBUTE_REPARSE_POINT & dwattr)) {
317        found = 0;
318	for (i = 0; ((3 > i) && (0 == back) && (0 == found)); i++) {
319	  ha = CreateFileA(
320	    fn,
321	    (
322	      (0 == i)
323	      ? (0)
324	      : ((1 == i) ? (FILE_READ_ATTRIBUTES) : (GENERIC_READ))
325	    ),
326	    (FILE_SHARE_READ | FILE_SHARE_WRITE),
327	    NULL,
328	    OPEN_EXISTING,
329	    (dwattr | FILE_FLAG_BACKUP_SEMANTICS),
330	    NULL
331	  );
332	  if (INVALID_HANDLE_VALUE != ha) {
333	    if (GetFileInformationByHandle(ha, &bhfi)) {
334	      found = 1;
335	      if (0 != (FILE_ATTRIBUTE_DIRECTORY & (bhfi.dwFileAttributes))) {
336	        back = 1;
337	      }
338	    }
339	    CloseHandle(ha);
340	  }
341	}
342      }
343      if (0 == back) {
344        dk4error_set_simple_error_code(erp, DK4_E_NON_DIR);
345      }
346    }
347  } else {
348    if (0 != ro) {
349      dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND);
350    } else {
351#if DK4_WIN_AVOID_CRT || DK4_WIN_DENY_CRT
352      if (CreateDirectoryA(fn, NULL)) {
353        back = 1;
354      } else {
355        dk4error_set_ldetails(
356          erp, DK4_E_CREATE_DIR_FAILED ,
357	  (long)((unsigned long)GetLastError())
358        );
359      }
360#else
361      if (CreateDirectoryA(fn, NULL)) {
362        back = 1;
363      } else {
364        errno = 0;
365        if (0 == _mkdir(fn)) {
366	  back = 1;
367	} else {
368	  dk4error_set_idetails(erp, DK4_E_MKDIR_FAILED, errno);
369	}
370      }
371#endif
372    }
373  }
374#else
375  back = 1;
376#endif
377  $? "- dk4mkdir_hierarchy_c8_one %d", back
378  finished:
379  return back;
380}
381
382
383
384char *
385dk4mkdir_hierarchy_c8_first_sep(char *fn, int *bptr, int lc, dk4_er_t *erp)
386{
387  char		*pc	= NULL;
388  int		 back	= 0;
389  char		 sc;
390
391#if	DK4_USE_ASSERT
392  assert(NULL != fn);
393#endif
394  if (NULL == fn) {
395    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
396    goto finished;
397  }
398
399  if ('\\' == *fn) {					/* leading bs */
400    if ('\\' == fn[1]) {				/* server and share */
401      pc = dk4str8_chr(&(fn[2]), '\\');
402      if (NULL != pc) {		/* Share name */
403        pc = dk4str8_chr(&(pc[1]), '\\');
404	if (NULL != pc) {	/* First dir */
405	  *pc = '\0';
406	  back = dk4mkdir_hierarchy_c8_one(fn, 1, erp);
407	  *pc = '\\';
408	  if (0 != back) {
409	    back = 0;
410	    pc = dk4str8_chr(&(pc[1]), '\\');
411	    if (NULL == pc) {
412	      if (0 != lc) {
413	        back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
414	      } else {
415	        back = 1;
416	      }
417	    }
418	  } else {
419	    pc = dk4str8_chr(&(pc[1]), '\\');
420	    if (NULL == pc) {
421	      if (0 != lc) {
422	        back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
423	      }
424	    }
425	  }
426	} else {		/* Share name but no dir */
427	  if (0 != lc) {
428	    back = dk4mkdir_hierarchy_c8_one(fn, 1, erp);
429	  } else {
430	    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
431	  }
432	}
433      } else {
434        dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
435      }
436    } else {						/* abs path, no drive */
437      pc = dk4str8_chr(&(fn[1]), '\\');
438      if (NULL == pc) {
439        if (0 != lc) {
440	  back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
441	} else {
442          back = 1;
443	}
444      }
445    }
446  } else {						/* no leading bs */
447    if (0 != dk4mkdir_hierarchy_c8_is_char(*fn)) {	/* leading char */
448      if (':' == fn[1]) {				/* drive */
449        if ('\\' == fn[2]) {				/* drive, abs path */
450	  sc = fn[3];
451	  fn[3] = '\0';
452	  back = dk4mkdir_hierarchy_c8_one(fn, 1, erp);
453	  fn[3] = sc;
454	  if (0 != back) {
455	    back = 0;
456	    pc = dk4str8_chr(&(fn[3]), '\\');
457	    if (NULL == pc) {
458	      if (0 != lc) {
459	        back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
460	      } else {
461	        back = 1;
462	      }
463	    }
464	  }
465	} else {					/* drive, rel path */
466	  sc = fn[2];
467	  fn[2] = '\0';
468	  back = dk4mkdir_hierarchy_c8_one(fn, 1, erp);
469	  fn[2] = sc;
470	  if (0 != back) {
471	    back = 0;
472	    pc = dk4str8_chr(&(fn[3]), '\\');
473	    if (NULL == pc) {
474	      if (0 != lc) {
475	        back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
476	      } else {
477	        back = 1;
478	      }
479	    }
480	  }
481	}
482      } else {						/* no drive */
483        pc = dk4str8_chr(fn, '\\');
484	if (NULL == pc) {
485	  if (0 != lc) {
486	    back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
487	  } else {
488	    back = 1;
489	  }
490	}
491      }
492    } else {						/* no leading char */
493      pc = dk4str8_chr(fn, '\\');
494      if (NULL == pc) {
495        if (0 != lc) {
496	  back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
497	} else {
498          back = 1;
499	}
500      }
501    }
502  }
503
504  finished:
505  if (NULL != bptr) { *bptr = back; }
506  return pc;
507}
508
509
510
511/* --- Windows */
512#else
513/* +++ non-Windows */
514
515
516
517int
518dk4mkdir_hierarchy_c8_one(const char *fn, int ro, dk4_er_t *erp)
519{
520  dk4_stat_t	stb;
521  int		back	= 0;
522  $? "+ dk4mkdir_hierarchy_c8_one \"%!8s\"", fn
523#if	DK4_USE_ASSERT
524  assert(NULL != fn);
525#endif
526  if (NULL == fn) {
527    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
528    goto finished;
529  }
530#if 1
531  if (0 != dk4stat_c8(&stb, fn, NULL)) {
532    back = dk4stat_is_directory(&stb, erp);
533    if (0 == back) {
534      dk4error_set_simple_error_code(erp, DK4_E_NON_DIR);
535    }
536  } else {
537    if (0 != ro) {
538      dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND);
539    } else {
540      errno = 0;
541      if (0 == mkdir(fn, 0700)) {
542        back = 1;
543      } else {
544        dk4error_set_idetails(erp, DK4_E_MKDIR_FAILED, errno);
545      }
546    }
547  }
548#else
549  back = 1;
550#endif
551  $? "- dk4mkdir_hierarchy_c8_one %d", back
552  finished:
553  return back;
554}
555
556
557
558char *
559dk4mkdir_hierarchy_c8_first_sep(char *fn, int *bptr, int lc, dk4_er_t *erp)
560{
561  char		*pc	= NULL;
562  int		 back	= 0;
563
564#if	DK4_USE_ASSERT
565  assert(NULL != fn);
566#endif
567  if (NULL == fn) {
568    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
569    goto finished;
570  }
571
572  if ('/' == *fn) {
573    pc = dk4str8_chr(&(fn[1]), '/');
574  } else {
575    pc = dk4str8_chr(fn, '/');
576  }
577  if (NULL == pc) {
578    if (0 != lc) {
579      back = dk4mkdir_hierarchy_c8_one(fn, 0, erp);
580    } else {
581      back = 1;
582    }
583  }
584
585  finished:
586  if (NULL != bptr) { *bptr = back; }
587  return pc;
588}
589
590
591
592/* --- non-Windows */
593#endif
594
595
596
597/**	Create directory structure using an editable copy of the path.
598	@param	fn	File name.
599	@param	lc	Flag: Last component in fn is directory too.
600	@param	erp	Error report, may be NULL.
601	@return	1 on success, 0 on error.
602
603	Error codes:
604	- DK4_E_INVALID_ARGUMENTS<br>
605	  if fn is NULL,
606	- DK4_E_BUFFER_TOO_SMALL<br>
607	  if fn is too long,
608	- DK4_E_MATH_OVERFLOW<br>
609	  if a mathematical overflow occured in size allocation,
610	- DK4_E_NOT_FOUND<br>
611	  if a server/share combination is not present on Windows,
612	- DK4_E_NON_DIR<br>
613	  if one of the components in fn is not a directory,
614	- DK4_E_MKDIR_FAILED<br>
615	  with errno in idetails if the function fails to create the directory
616	  or a parent directory,
617	- DK4_E_CREATE_DIR_FAILED<br>
618	  with GetLastError() result in lDetails1 if the CreateDirectory()
619	  function failed on a Windows system.
620*/
621static
622int
623dk4mkdir_hierarchy_c8_on_copy(char *fn, int lc, dk4_er_t *erp)
624{
625  char			*pc;
626  int			 back 	= 0;
627
628  $? "+ dk4mkdir_hierarchy_c8_on_copy \"%!8s\"", fn
629#if	DK4_USE_ASSERT
630  assert(NULL != fn);
631#endif
632
633  /* Correct file name */
634  dk4path8_correct_sep(fn);
635
636  /* Find first real separator */
637  pc = dk4mkdir_hierarchy_c8_first_sep(fn, &back, lc, erp);
638
639  /* Process directories */
640  if (NULL != pc) {
641    back = 1;
642    while (NULL != pc) {
643      *pc = '\0';
644      if (0 < dk4str8_len(fn)) {
645        if (0 == dk4mkdir_hierarchy_c8_one(fn, 0, erp)) {
646          back = 0;
647        }
648      }
649#if DK4_HAVE_BACKSLASH_AS_SEP
650      *pc = '\\';
651      pc = dk4str8_chr(&(pc[1]), '\\');
652#else
653      *pc = '/';
654      pc = dk4str8_chr(&(pc[1]), '/');
655#endif
656    }
657    if (0 != lc) {
658      if (0 == dk4mkdir_hierarchy_c8_one(fn, 0, erp)) {
659        back = 0;
660      }
661    }
662  }
663  $? "- dk4mkdir_hierarchy_c8_on_copy %d", back
664  return back;
665}
666
667
668
669/**	Create directory structure using a local copy of the path.
670	@param	fn	File name.
671	@param	lc	Flag: Last component in fn is directory too.
672	@param	erp	Error report, may be NULL.
673	@return	1 on success, 0 on error.
674
675	Error codes:
676	- DK4_E_INVALID_ARGUMENTS<br>
677	  if fn is NULL,
678	- DK4_E_BUFFER_TOO_SMALL<br>
679	  if fn is too long,
680	- DK4_E_MATH_OVERFLOW<br>
681	  if a mathematical overflow occured in size allocation,
682	- DK4_E_NOT_FOUND<br>
683	  if a server/share combination is not present on Windows,
684	- DK4_E_NON_DIR<br>
685	  if one of the components in fn is not a directory,
686	- DK4_E_MKDIR_FAILED<br>
687	  with errno in idetails if the function fails to create the directory
688	  or a parent directory,
689	- DK4_E_CREATE_DIR_FAILED<br>
690	  with GetLastError() result in lDetails1 if the CreateDirectory()
691	  function failed on a Windows system.
692*/
693static
694int
695dk4mkdir_hierarchy_c8_local(const char *fn, int lc, dk4_er_t *erp)
696{
697  char		 mycp[DK4_MAX_PATH];
698  int		 back			= 0;
699  $? "+ dk4mkdir_hierarchy_c8_local \"%!8s\"", fn
700#if	DK4_USE_ASSERT
701  assert(NULL != fn);
702#endif
703  if (0 != dk4str8_cpy_s(mycp, sizeof(mycp), fn, erp)) {
704    back = dk4mkdir_hierarchy_c8_on_copy(mycp, lc, erp);
705  } $? "- dk4mkdir_hierarchy_c8_local %d", back
706  return back;
707}
708
709
710
711int
712dk4mkdir_hierarchy_c8(const char *fn, int lc, dk4_er_t *erp)
713{
714  char		*mycp;
715  size_t	 sz;
716  int		 back = 0;
717  $? "+ dk4mkdir_hierarchy_c8 \"%!8s\"", TR_8STR(fn)
718#if	DK4_USE_ASSERT
719  assert(NULL != fn);
720#endif
721  if (NULL != fn) {
722    sz = dk4str8_len(fn);
723    if (DK4_MAX_PATH > sz) {
724      back = dk4mkdir_hierarchy_c8_local(fn, lc, erp);
725    } else {
726      mycp = dk4str8_dup(fn, erp);
727      if (NULL != mycp) {
728        back = dk4mkdir_hierarchy_c8_on_copy(mycp, lc, erp);
729	dk4mem_free(mycp);
730      }
731    }
732  } else {
733    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
734  } $? "- dk4mkdir_hierarchy_c8 %d", back
735  return back;
736}
737
738
739
740#if DK4_HAVE_UID_T && DK4_HAVE_GID_T && DK4_HAVE_MODE_T && (!DK4_ON_WINDOWS)
741
742
743
744/**	Check whether directory exists or attempt to create directory.
745	@param	fn	File name.
746	@param	ro	Flag: Read only (test directory presence),
747			do not attempt to create the directory.
748	@param	uid	User ID for new directories.
749	@param	gid	Group ID for new directories.
750	@param	mode	Mode for new directories.
751	@param	erp	Error report, may be NULL.
752	@return	1 if directory is available now, 0 otherwise.
753
754	Error codes:
755	- DK4_E_INVALID_ARGUMENTS<br>
756	  if fn is NULL,
757	- DK4_E_NOT_FOUND<br>
758	  if a server/share combination is not present on Windows,
759	- DK4_E_NON_DIR<br>
760	  if any component in the path is not a directory,
761	- DK4_E_MKDIR_FAILED<br>
762	  with errno value in iDetails1 if the mkdir() function failed on a
763	  non-Windows system,
764	- DK4_E_CHOWN_FAILED<br>
765	  with errno value in iDetails1 if the chown() function failed on a
766	  non-Windows system,
767	- DK4_E_CHMOD_FAILED<br>
768	  with errno value in iDetails1 if the chmod() function failed on a
769	  non-Windows system,
770	- DK4_E_CREATE_DIR_FAILED<br>
771	  with GetLastError() result in lDetails1 if the CreateDirectory()
772	  function failed on a Windows system.
773*/
774static
775int
776dk4mkdir_hierarchy_ugm_c8_one(
777  const char	*fn,
778  int		 ro,
779  uid_t		 uid,
780  gid_t		 gid,
781  mode_t	 mode,
782  dk4_er_t	*erp
783)
784{
785  dk4_stat_t	stb;
786  int		back	= 0;
787  $? "+ dk4mkdir_hierarchy_c8_one \"%!8s\"", fn
788#if	DK4_USE_ASSERT
789  assert(NULL != fn);
790#endif
791  if (NULL == fn) {
792    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
793    goto finished;
794  }
795#if 1
796  if (0 != dk4stat_c8(&stb, fn, NULL)) {
797    back = dk4stat_is_directory(&stb, erp);
798    if (0 == back) {
799      dk4error_set_simple_error_code(erp, DK4_E_NON_DIR);
800    }
801  } else {
802    if (0 != ro) {
803      dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND);
804    } else {
805      errno = 0;
806      if (0 == mkdir(fn, mode)) {
807        errno = 0;
808	if (0 == chown(fn, uid, gid)) {
809	  errno = 0;
810	  if (0 == chmod(fn, mode)) {
811            back = 1;
812	  } else {
813	    dk4error_set_idetails(erp, DK4_E_CHMOD_FAILED, errno);
814	  }
815	} else {
816	  dk4error_set_idetails(erp, DK4_E_CHOWN_FAILED, errno);
817	}
818      } else {
819        dk4error_set_idetails(erp, DK4_E_MKDIR_FAILED, errno);
820      }
821    }
822  }
823#else
824  back = 1;
825#endif
826  $? "- dk4mkdir_hierarchy_c8_one %d", back
827  finished:
828  return back;
829}
830
831
832
833/**	Create directory structure using an editable copy of the path.
834	@param	fn	File name.
835	@param	lc	Flag: Last component in fn is directory too.
836	@param	uid	User ID for new directories.
837	@param	gid	Group ID for new directories.
838	@param	mode	Mode for new directories.
839	@param	erp	Error report, may be NULL.
840	@return	1 on success, 0 on error.
841
842	Error codes:
843	- DK4_E_INVALID_ARGUMENTS<br>
844	  if fn is NULL,
845	- DK4_E_BUFFER_TOO_SMALL<br>
846	  if fn is too long,
847	- DK4_E_MATH_OVERFLOW<br>
848	  if a mathematical overflow occured in size allocation,
849	- DK4_E_NOT_FOUND<br>
850	  if a server/share combination is not present on Windows,
851	- DK4_E_NON_DIR<br>
852	  if one of the components in fn is not a directory,
853	- DK4_E_MKDIR_FAILED<br>
854	  with errno in idetails if the function fails to create the directory
855	  or a parent directory,
856	- DK4_E_CHOWN_FAILED<br>
857	  with errno value in iDetails1 if the chown() function failed on a
858	  non-Windows system,
859	- DK4_E_CHMOD_FAILED<br>
860	  with errno value in iDetails1 if the chmod() function failed on a
861	  non-Windows system,
862	- DK4_E_CREATE_DIR_FAILED<br>
863	  with GetLastError() result in lDetails1 if the CreateDirectory()
864	  function failed on a Windows system.
865*/
866static
867int
868dk4mkdir_hierarchy_ugm_c8_on_copy(
869  char		*fn,
870  int		 lc,
871  uid_t		 uid,
872  gid_t		 gid,
873  mode_t	 mode,
874  dk4_er_t	*erp
875)
876{
877  char			*pc;
878  int			 back 	= 0;
879
880  $? "+ dk4mkdir_hierarchy_c8_on_copy \"%!8s\"", fn
881
882#if	DK4_USE_ASSERT
883  assert(NULL != fn);
884#endif
885
886  /* Correct file name */
887  dk4path8_correct_sep(fn);
888
889  /* Find first real separator */
890  pc = dk4mkdir_hierarchy_c8_first_sep(fn, &back, lc, erp);
891
892  /* Process directories */
893  if (NULL != pc) {
894    back = 1;
895    while (NULL != pc) {
896      *pc = '\0';
897      if (0 < dk4str8_len(fn)) {
898        if (0 == dk4mkdir_hierarchy_ugm_c8_one(fn, 0, uid, gid, mode, erp)) {
899          back = 0;
900        }
901      }
902#if DK4_HAVE_BACKSLASH_AS_SEP
903      *pc = '\\';
904      pc = dk4str8_chr(&(pc[1]), '\\');
905#else
906      *pc = '/';
907      pc = dk4str8_chr(&(pc[1]), '/');
908#endif
909    }
910    if (0 != lc) {
911      if (0 == dk4mkdir_hierarchy_ugm_c8_one(fn, 0, uid, gid, mode, erp)) {
912        back = 0;
913      }
914    }
915  }
916  $? "- dk4mkdir_hierarchy_c8_on_copy %d", back
917  return back;
918}
919
920
921
922/**	Create directory structure using a local copy of the path.
923	@param	fn	File name.
924	@param	lc	Flag: Last component in fn is directory too.
925	@param	uid	User ID for new directories.
926	@param	gid	Group ID for new directories.
927	@param	mode	Mode for new directories.
928	@param	erp	Error report, may be NULL.
929	@return	1 on success, 0 on error.
930
931	Error codes:
932	- DK4_E_INVALID_ARGUMENTS<br>
933	  if fn is NULL,
934	- DK4_E_BUFFER_TOO_SMALL<br>
935	  if fn is too long,
936	- DK4_E_MATH_OVERFLOW<br>
937	  if a mathematical overflow occured in size allocation,
938	- DK4_E_NOT_FOUND<br>
939	  if a server/share combination is not present on Windows,
940	- DK4_E_NON_DIR<br>
941	  if one of the components in fn is not a directory,
942	- DK4_E_MKDIR_FAILED<br>
943	  with errno in idetails if the function fails to create the directory
944	  or a parent directory,
945	- DK4_E_CHOWN_FAILED<br>
946	  with errno value in iDetails1 if the chown() function failed on a
947	  non-Windows system,
948	- DK4_E_CHMOD_FAILED<br>
949	  with errno value in iDetails1 if the chmod() function failed on a
950	  non-Windows system,
951	- DK4_E_CREATE_DIR_FAILED<br>
952	  with GetLastError() result in lDetails1 if the CreateDirectory()
953	  function failed on a Windows system.
954*/
955static
956int
957dk4mkdir_hierarchy_ugm_c8_local(
958  const char	*fn,
959  int		 lc,
960  uid_t		 uid,
961  gid_t		 gid,
962  mode_t	 mode,
963  dk4_er_t	*erp
964)
965{
966  char		 mycp[DK4_MAX_PATH];
967  int		 back			= 0;
968  $? "+ dk4mkdir_hierarchy_c8_local \"%!8s\"", fn
969#if	DK4_USE_ASSERT
970  assert(NULL != fn);
971#endif
972  if (0 != dk4str8_cpy_s(mycp, sizeof(mycp), fn, erp)) {
973    back = dk4mkdir_hierarchy_ugm_c8_on_copy(mycp, lc, uid, gid, mode, erp);
974  } $? "- dk4mkdir_hierarchy_c8_local %d", back
975  return back;
976}
977
978
979
980int
981dk4mkdir_hierarchy_ugm_c8(
982  const char	*fn,
983  int		 lc,
984  uid_t		 uid,
985  gid_t		 gid,
986  mode_t	 mode,
987  dk4_er_t	*erp
988)
989{
990  char		*mycp;
991  size_t	 sz;
992  int		 back = 0;
993  $? "+ dk4mkdir_hierarchy_c8 \"%!8s\"", TR_8STR(fn)
994#if	DK4_USE_ASSERT
995  assert(NULL != fn);
996#endif
997  if (NULL != fn) {
998    sz = dk4str8_len(fn);
999    if (DK4_MAX_PATH > sz) {
1000      back = dk4mkdir_hierarchy_ugm_c8_local(fn, lc, uid, gid, mode, erp);
1001    } else {
1002      mycp = dk4str8_dup(fn, erp);
1003      if (NULL != mycp) {
1004        back = dk4mkdir_hierarchy_ugm_c8_on_copy(mycp, lc, uid, gid, mode, erp);
1005	dk4mem_free(mycp);
1006      }
1007    }
1008  } else {
1009    dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
1010  } $? "- dk4mkdir_hierarchy_c8 %d", back
1011  return back;
1012}
1013
1014
1015
1016#endif
1017