6 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
7 * Copyright (C) 2009, 2011, 2013, MinGW.org 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.
35 #include <sys/types.h>
43 * MS-Windows nuisances...
44 * mkdir() function doesn't accept a `mode' argument; ignore it.
46 # define mkdir( PATH, MODE ) _mkdir( PATH )
48 * MS-Windows _O_BINARY vs. _O_TEXT discrimination can't be explicitly
49 * resolved in a simple `creat()' call; instead, we will use `_open()',
50 * with one of the following explicit attribute sets...
52 # if IMPLEMENTATION_LEVEL == SETUP_TOOL_COMPONENT
54 * ...allowing overwrite in the setup tool implementation...
56 # define _O_NEWFILE _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY
58 /* ...but not in the standard mingw-get implementation...
60 # define _O_NEWFILE _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY
62 # define creat(P,M) _open( P, _O_NEWFILE, _map_posix_mode(M) )
64 /* Furthermore, MS-Windows protection modes are naive, in comparison
65 * to the POSIX modes specified within tar archive headers; firstly,
66 * there is no concept of `execute' permissions; secondly, there is
67 * no concept of a `write-only' file, (thus _S_IREAD permission is
68 * implicitly granted for all files); thirdly, `write' permission is
69 * granted only by a single _S_IWRITE flag, so at best, we must set
70 * this if any of POSIX's S_IWUSR, S_IWGRP or S_IWOTH flags are set
71 * in the tar header; finally, MS-Windows has no counterpart for any
72 * of POSIX's `suid', `sgid' or `sticky' bits.
74 # define _S_IWANY 0222 /* eqv. POSIX's S_IWUSR | S_IWGRP | S_IWOTH */
75 # define _map_posix_mode(M) _S_IREAD | (((M) & _S_IWANY) ? _S_IWRITE : 0)
79 const char *pkgArchivePath()
81 /* Specify where downloaded packages are cached,
82 * within the local file system.
84 return "%R" "var/cache/mingw-get/packages" "%/M/%F";
87 const char *pkgSourceArchivePath()
89 /* Specify where downloaded source packages are cached,
90 * within the local file system.
92 return "%R" "var/cache/mingw-get/source" "%/M/%F";
95 int mkpath( char *buf, const char *fmt, const char *file, const char *modifier )
97 /* A helper function, for constructing package URL strings.
98 * Return value is the length, in bytes, of the constructed string,
99 * which is returned in "buf"; call with "buf = NULL" to determine
100 * the size of buffer required, without storing the string.
102 * Constructed URL is copied from "fmt", with...
104 * %% replaced by a single literal "%" character;
105 * %[/]F replaced by the string passed as "file";
106 * %[/]M replaced by the string passed as "modifier";
107 * %[/]R replaced by the "APPROOT" environment string.
109 * Any other character present in "fmt" is copied literally. In
110 * the case of "%F", "%M" and "%R", inclusion of the optional "/"
111 * flag causes a single "/" character to be inserted before the
112 * substitute string, provided this is not NULL; ("\\" may be
113 * used, but is not recommended, in place of the "/" flag).
120 do { if( (c = *fmt++) == '%' )
122 /* Interpret substitution tags...
125 const char *subst = NULL;
127 /* ...checking for presence of a "/" flag...
129 if( ((flag == '/') || (flag == '\\')) && fmt[1] )
131 * ...and, when found, with a possibly valid format spec,
132 * advance to parse that spec...
137 /* ...establish absence of the flag.
145 * Schedule substitution of text specified as "file".
152 * Schedule substitution of text specified as "modifier",
159 * Schedule substitution from the "APPROOT" environment string,
161 subst = getenv( "APPROOT" );
166 * Interpreting "%%", but may have been "%/%", which is invalid...
171 * It was just "%%", so store a literal "%" character.
179 /* If we get to here, it was the invalid "%/%" form; backtrack,
180 * and fall through to emit literal "%/", then resume parsing,
181 * treating the second "%" as the possible starting character
182 * of a new format specification.
190 /* Store the literal "%" character,
191 * followed by the unrecognised tag character.
201 /* Perform scheduled substitution of "file", "modifier"
202 * or the APPROOT environment string...
212 /* ...counting and copying character by character.
225 * Copy one literal character from "fmt"...
229 * ...storing as necessary...
233 /* ...and counting it anyway.
239 /* Always return the total number of characters which were, or would
240 * have been transferred to "buf".
246 void create_parent_directory_hierarchy( const char *pathname, int mode )
248 /* Recursive helper function to create a directory branch, including
249 * all missing parent directories, (analogous to using "mkdir -p").
251 * FIXME: We allow for either "/" or "\" as the directory separator;
252 * do we also need to accommodate possible use of multibyte-character
253 * encodings? (Hopefully, archives should rely exclusively on the
254 * POSIX Portable Character set, i.e. 7-bit ASCII, so maybe not).
256 char *parse, *parent, *mark = NULL, *stop = NULL;
258 /* We work with a copy of the supplied "pathname", so we can guarantee
259 * we have a modifiable string, and can accept a "const" input string.
261 if( (parse = parent = strdup( pathname )) != NULL )
263 /* Having obtained a valid copy of "pathname", we parse it...
267 /* Set the "stop" mark at the first in the last sequence of
268 * one or more directory separators detected, (if any)...
272 * ...then step over any following characters which are
273 * neither the string terminator, nor further separators.
275 while( *parse && (*parse != '/') && (*parse != '\\') )
278 /* If we haven't yet found the string terminator, then we
279 * must have found a new sequence of one or more separators;
280 * mark it as a new candidate "stop" mark location.
285 /* Now, step over all contiguous separators in the current
286 * sequence, before restarting the outer loop, to parse the
287 * next directory or file name, (if any). Note that we defer
288 * updating the "stop" mark until the start of this new cycle;
289 * this ensures that we correctly ignore any separators which
290 * trail at the end of "pathname", with no following "name"
293 while( (*parse == '/') || (*parse == '\\') )
298 /* We found a valid point, at which to split the current leaf
299 * of "pathname" from its parent branch hierarchy; split it and
300 * recurse through the "mkdir" function, to create the parent
301 * directory hierarchy, as required.
304 mkdir_recursive( parent, mode );
307 /* We are now done with our temporary copy of "pathname"; reclaim
308 * the memory which was allocated to store it.
314 int mkdir_recursive( const char *pathname, int mode )
316 /* Public entry point for the recursive "mkdir" function.
318 * First, we attempt a simple "mkdir"; if this succeeds, the
319 * parent directory branch is already in place, and we have
320 * nothing more to do.
322 if( mkdir( pathname, mode ) == 0 )
331 * This indicates a gap in the parent branch hierarchy;
332 * call the preceding helper, to fill the gap...
334 create_parent_directory_hierarchy( pathname, mode );
336 * ...before making a further attempt to add the leaf.
338 return mkdir( pathname, mode );
342 /* Here, the initial "mkdir" failed because a file
343 * system entity called "pathname" already exists; if
344 * this is already a directory, all is well; (there is
345 * no need to create it again)...
348 if( (stat( pathname, &target ) == 0) && S_ISDIR( target.st_mode ) )
352 /* ...otherwise we simply fall through and fail...
357 int set_output_stream( const char *pathname, int mode )
359 /* Attach the extractor's output data stream to a specified file,
360 * creating the parent directory branch hierarchy as required, and
361 * return a file descriptor on the stream.
365 /* First, simply attempt to create the destination file...
367 if( ((fd = creat( pathname, mode )) < 0) && (errno == ENOENT) )
369 /* ...but, on failure due to a gap in the directory structure
370 * call the preceding helper to create the necessary hierarchy...
372 create_parent_directory_hierarchy( pathname, 0755 );
374 * ...before making a further attempt to create the file.
376 return creat( pathname, mode );
379 /* Here, we will have the invalid file descriptor from the initial
380 * failed attempt to create the file; we return it to indicate the
381 * ultimate failure to create this file.
386 /* $RCSfile$: end of file */