6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 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 int mkpath( char *buf, const char *fmt, const char *file, const char *modifier )
79 /* A helper function, for constructing package URL strings.
80 * Return value is the length, in bytes, of the constructed string,
81 * which is returned in "buf"; call with "buf = NULL" to determine
82 * the size of buffer required, without storing the string.
84 * Constructed URL is copied from "fmt", with...
86 * %% replaced by a single literal "%" character;
87 * %[/]F replaced by the string passed as "file";
88 * %[/]M replaced by the string passed as "modifier";
89 * %[/]R replaced by the "APPROOT" environment string.
91 * Any other character present in "fmt" is copied literally. In
92 * the case of "%F", "%M" and "%R", inclusion of the optional "/"
93 * flag causes a single "/" character to be inserted before the
94 * substitute string, provided this is not NULL; ("\\" may be
95 * used, but is not recommended, in place of the "/" flag).
102 do { if( (c = *fmt++) == '%' )
104 /* Interpret substitution tags...
107 const char *subst = NULL;
109 /* ...checking for presence of a "/" flag...
111 if( ((flag == '/') || (flag == '\\')) && fmt[1] )
113 * ...and, when found, with a possibly valid format spec,
114 * advance to parse that spec...
119 /* ...establish absence of the flag.
127 * Schedule substitution of text specified as "file".
134 * Schedule substitution of text specified as "modifier",
141 * Schedule substitution from the "APPROOT" environment string,
143 subst = getenv( "APPROOT" );
148 * Interpreting "%%", but may have been "%/%", which is invalid...
153 * It was just "%%", so store a literal "%" character.
161 /* If we get to here, it was the invalid "%/%" form; backtrack,
162 * and fall through to emit literal "%/", then resume parsing,
163 * treating the second "%" as the possible starting character
164 * of a new format specification.
172 /* Store the literal "%" character,
173 * followed by the unrecognised tag character.
183 /* Perform scheduled substitution of "file", "modifier"
184 * or the APPROOT environment string...
194 /* ...counting and copying character by character.
207 * Copy one literal character from "fmt"...
211 * ...storing as necessary...
215 /* ...and counting it anyway.
221 /* Always return the total number of characters which were, or would
222 * have been transferred to "buf".
228 void create_parent_directory_hierarchy( const char *pathname, int mode )
230 /* Recursive helper function to create a directory branch, including
231 * all missing parent directories, (analogous to using "mkdir -p").
233 * FIXME: We allow for either "/" or "\" as the directory separator;
234 * do we also need to accommodate possible use of multibyte-character
235 * encodings? (Hopefully, archives should rely exclusively on the
236 * POSIX Portable Character set, i.e. 7-bit ASCII, so maybe not).
238 char *parse, *parent, *mark = NULL, *stop = NULL;
240 /* We work with a copy of the supplied "pathname", so we can guarantee
241 * we have a modifiable string, and can accept a "const" input string.
243 if( (parse = parent = strdup( pathname )) != NULL )
245 /* Having obtained a valid copy of "pathname", we parse it...
249 /* Set the "stop" mark at the first in the last sequence of
250 * one or more directory separators detected, (if any)...
254 * ...then step over any following characters which are
255 * neither the string terminator, nor further separators.
257 while( *parse && (*parse != '/') && (*parse != '\\') )
260 /* If we haven't yet found the string terminator, then we
261 * must have found a new sequence of one or more separators;
262 * mark it as a new candidate "stop" mark location.
267 /* Now, step over all contiguous separators in the current
268 * sequence, before restarting the outer loop, to parse the
269 * next directory or file name, (if any). Note that we defer
270 * updating the "stop" mark until the start of this new cycle;
271 * this ensures that we correctly ignore any separators which
272 * trail at the end of "pathname", with no following "name"
275 while( (*parse == '/') || (*parse == '\\') )
280 /* We found a valid point, at which to split the current leaf
281 * of "pathname" from its parent branch hierarchy; split it and
282 * recurse through the "mkdir" function, to create the parent
283 * directory hierarchy, as required.
286 mkdir_recursive( parent, mode );
289 /* We are now done with our temporary copy of "pathname"; reclaim
290 * the memory which was allocated to store it.
296 int mkdir_recursive( const char *pathname, int mode )
298 /* Public entry point for the recursive "mkdir" function.
300 * First, we attempt a simple "mkdir"; if this succeeds, the
301 * parent directory branch is already in place, and we have
302 * nothing more to do.
304 if( mkdir( pathname, mode ) == 0 )
313 * This indicates a gap in the parent branch hierarchy;
314 * call the preceding helper, to fill the gap...
316 create_parent_directory_hierarchy( pathname, mode );
318 * ...before making a further attempt to add the leaf.
320 return mkdir( pathname, mode );
324 /* Here, the initial "mkdir" failed because a file
325 * system entity called "pathname" already exists; if
326 * this is already a directory, all is well; (there is
327 * no need to create it again)...
330 if( (stat( pathname, &target ) == 0) && S_ISDIR( target.st_mode ) )
334 /* ...otherwise we simply fall through and fail...
339 int set_output_stream( const char *pathname, int mode )
341 /* Attach the extractor's output data stream to a specified file,
342 * creating the parent directory branch hierarchy as required, and
343 * return a file descriptor on the stream.
347 /* First, simply attempt to create the destination file...
349 if( ((fd = creat( pathname, mode )) < 0) && (errno == ENOENT) )
351 /* ...but, on failure due to a gap in the directory structure
352 * call the preceding helper to create the necessary hierarchy...
354 create_parent_directory_hierarchy( pathname, 0755 );
356 * ...before making a further attempt to create the file.
358 return creat( pathname, mode );
361 /* Here, we will have the invalid file descriptor from the initial
362 * failed attempt to create the file; we return it to indicate the
363 * ultimate failure to create this file.
368 /* $RCSfile$: end of file */