6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2011, MinGW Project
10 * Helper functions for constructing path names, creating directory
11 * hierarchies, and preparing to write new files within any specified
12 * file system hierarchy.
15 * This is free software. Permission is granted to copy, modify and
16 * redistribute this software, under the provisions of the GNU General
17 * Public License, Version 3, (or, at your option, any later version),
18 * as published by the Free Software Foundation; see the file COPYING
19 * for licensing details.
21 * Note, in particular, that this software is provided "as is", in the
22 * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
23 * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
24 * PARTICULAR PURPOSE. Under no circumstances will the author, or the
25 * MinGW Project, accept liability for any damages, however caused,
26 * arising from the use of this software.
34 #include <sys/types.h>
42 * MS-Windows nuisances...
43 * mkdir() function doesn't accept a `mode' argument; ignore it.
45 # define mkdir( PATH, MODE ) _mkdir( PATH )
47 * MS-Windows _O_BINARY vs. _O_TEXT discrimination can't be explicitly
48 * resolved in a simple `creat()' call; instead, we will use `_open()',
49 * with the following explicit attribute set...
51 # define _O_NEWFILE _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY
52 # define creat(P,M) _open( P, _O_NEWFILE, _map_posix_mode(M) )
54 /* Furthermore, MS-Windows protection modes are naive, in comparison
55 * to the POSIX modes specified within tar archive headers; firstly,
56 * there is no concept of `execute' permissions; secondly, there is
57 * no concept of a `write-only' file, (thus _S_IREAD permission is
58 * implicitly granted for all files); thirdly, `write' permission is
59 * granted only by a single _S_IWRITE flag, so at best, we must set
60 * this if any of POSIX's S_IWUSR, S_IWGRP or S_IWOTH flags are set
61 * in the tar header; finally, MS-Windows has no counterpart for any
62 * of POSIX's `suid', `sgid' or `sticky' bits.
64 # define _S_IWANY 0222 /* eqv. POSIX's S_IWUSR | S_IWGRP | S_IWOTH */
65 # define _map_posix_mode(M) _S_IREAD | (((M) & _S_IWANY) ? _S_IWRITE : 0)
69 const char *pkgArchivePath()
71 /* Specify where downloaded packages are cached,
72 * within the local file system.
74 return "%R" "var/cache/mingw-get/packages" "%/M/%F";
77 const char *pkgSourceArchivePath()
79 /* Specify where downloaded source packages are cached,
80 * within the local file system.
82 return "%R" "var/cache/mingw-get/source" "%/M/%F";
85 int mkpath( char *buf, const char *fmt, const char *file, const char *modifier )
87 /* A helper function, for constructing package URL strings.
88 * Return value is the length, in bytes, of the constructed string,
89 * which is returned in "buf"; call with "buf = NULL" to determine
90 * the size of buffer required, without storing the string.
92 * Constructed URL is copied from "fmt", with...
94 * %% replaced by a single literal "%" character;
95 * %[/]F replaced by the string passed as "file";
96 * %[/]M replaced by the string passed as "modifier";
97 * %[/]R replaced by the "APPROOT" environment string.
99 * Any other character present in "fmt" is copied literally. In
100 * the case of "%F", "%M" and "%R", inclusion of the optional "/"
101 * flag causes a single "/" character to be inserted before the
102 * substitute string, provided this is not NULL; ("\\" may be
103 * used, but is not recommended, in place of the "/" flag).
110 do { if( (c = *fmt++) == '%' )
112 /* Interpret substitution tags...
115 const char *subst = NULL;
117 /* ...checking for presence of a "/" flag...
119 if( ((flag == '/') || (flag == '\\')) && fmt[1] )
121 * ...and, when found, with a possibly valid format spec,
122 * advance to parse that spec...
127 /* ...establish absence of the flag.
135 * Schedule substitution of text specified as "file".
142 * Schedule substitution of text specified as "modifier",
149 * Schedule substitution from the "APPROOT" environment string,
151 subst = getenv( "APPROOT" );
156 * Interpreting "%%", but may have been "%/%", which is invalid...
161 * It was just "%%", so store a literal "%" character.
169 /* If we get to here, it was the invalid "%/%" form; backtrack,
170 * and fall through to emit literal "%/", then resume parsing,
171 * treating the second "%" as the possible starting character
172 * of a new format specification.
180 /* Store the literal "%" character,
181 * followed by the unrecognised tag character.
191 /* Perform scheduled substitution of "file", "modifier"
192 * or the APPROOT environment string...
202 /* ...counting and copying character by character.
215 * Copy one literal character from "fmt"...
219 * ...storing as necessary...
223 /* ...and counting it anyway.
229 /* Always return the total number of characters which were, or would
230 * have been transferred to "buf".
236 void create_parent_directory_hierarchy( const char *pathname, int mode )
238 /* Recursive helper function to create a directory branch, including
239 * all missing parent directories, (analogous to using "mkdir -p").
241 * FIXME: We allow for either "/" or "\" as the directory separator;
242 * do we also need to accommodate possible use of multibyte-character
243 * encodings? (Hopefully, archives should rely exclusively on the
244 * POSIX Portable Character set, i.e. 7-bit ASCII, so maybe not).
246 char *parse, *parent, *mark = NULL, *stop = NULL;
248 /* We work with a copy of the supplied "pathname", so we can guarantee
249 * we have a modifiable string, and can accept a "const" input string.
251 if( (parse = parent = strdup( pathname )) != NULL )
253 /* Having obtained a valid copy of "pathname", we parse it...
257 /* Set the "stop" mark at the first in the last sequence of
258 * one or more directory separators detected, (if any)...
262 * ...then step over any following characters which are
263 * neither the string terminator, nor further separators.
265 while( *parse && (*parse != '/') && (*parse != '\\') )
268 /* If we haven't yet found the string terminator, then we
269 * must have found a new sequence of one or more separators;
270 * mark it as a new candidate "stop" mark location.
275 /* Now, step over all contiguous separators in the current
276 * sequence, before restarting the outer loop, to parse the
277 * next directory or file name, (if any). Note that we defer
278 * updating the "stop" mark until the start of this new cycle;
279 * this ensures that we correctly ignore any separators which
280 * trail at the end of "pathname", with no following "name"
283 while( (*parse == '/') || (*parse == '\\') )
288 /* We found a valid point, at which to split the current leaf
289 * of "pathname" from its parent branch hierarchy; split it and
290 * recurse through the "mkdir" function, to create the parent
291 * directory hierarchy, as required.
294 mkdir_recursive( parent, mode );
297 /* We are now done with our temporary copy of "pathname"; reclaim
298 * the memory which was allocated to store it.
304 int mkdir_recursive( const char *pathname, int mode )
306 /* Public entry point for the recursive "mkdir" function.
308 * First, we attempt a simple "mkdir"; if this succeeds, the
309 * parent directory branch is already in place, and we have
310 * nothing more to do.
312 if( mkdir( pathname, mode ) == 0 )
321 * This indicates a gap in the parent branch hierarchy;
322 * call the preceding helper, to fill the gap...
324 create_parent_directory_hierarchy( pathname, mode );
326 * ...before making a further attempt to add the leaf.
328 return mkdir( pathname, mode );
332 /* Here, the initial "mkdir" failed because a file
333 * system entity called "pathname" already exists; if
334 * this is already a directory, all is well; (there is
335 * no need to create it again)...
338 if( (stat( pathname, &target ) == 0) && S_ISDIR( target.st_mode ) )
342 /* ...otherwise we simply fall through and fail...
347 int set_output_stream( const char *pathname, int mode )
349 /* Attach the extractor's output data stream to a specified file,
350 * creating the parent directory branch hierarchy as required, and
351 * return a file descriptor on the stream.
355 /* First, simply attempt to create the destination file...
357 if( ((fd = creat( pathname, mode )) < 0) && (errno == ENOENT) )
359 /* ...but, on failure due to a gap in the directory structure
360 * call the preceding helper to create the necessary hierarchy...
362 create_parent_directory_hierarchy( pathname, 0755 );
364 * ...before making a further attempt to create the file.
366 return creat( pathname, mode );
369 /* Here, we will have the invalid file descriptor from the initial
370 * failed attempt to create the file; we return it to indicate the
371 * ultimate failure to create this file.
376 /* $RCSfile$: end of file */