windows-nt/Source/XPSP1/NT/base/crts/crtw32/misc/splitpat.c
2020-09-26 16:20:57 +08:00

209 lines
7 KiB
C

/***
*splitpath.c - break down path name into components
*
* Copyright (c) 1987-2001, Microsoft Corporation. All rights reserved.
*
*Purpose:
* To provide support for accessing the individual components of an
* arbitrary path name
*
*Revision History:
* 06-14-87 DFW initial implementation
* 09-23-87 JCR Removed 'const' from declarations (fixed cl warnings)
* 12-11-87 JCR Added "_LOAD_DS" to declaration
* 11-20-89 GJF Fixed indents, copyright. Added const attribute to
* type of path.
* 03-15-90 GJF Replaced _LOAD_DS with _CALLTYPE1 and added #include
* <cruntime.h>.
* 07-25-90 SBM Removed redundant include (stdio.h), replaced local
* MIN macro with standard min macro
* 10-04-90 GJF New-style function declarator.
* 01-22-91 GJF ANSI naming.
* 11-20-92 KRS Port _MBCS support from 16-bit tree.
* 05-12-93 KRS Add fix for MBCS max path handling.
* 12-07-93 CFW Wide char enable.
* 10-15-95 BWT _NTSUBSET_ doesn't do MBCS here.
* 09-09-96 JWM Test length of input string before accessing (Orion 7985).
* 04-28-98 GJF No more _ISLEADBYTE macro.
*
*******************************************************************************/
#ifdef _NTSUBSET_
#undef _MBCS
#endif
#include <cruntime.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MBCS
#include <mbstring.h>
#include <mbctype.h>
#include <mbdata.h>
#endif
#include <tchar.h>
/***
*_splitpath() - split a path name into its individual components
*
*Purpose:
* to split a path name into its individual components
*
*Entry:
* path - pointer to path name to be parsed
* drive - pointer to buffer for drive component, if any
* dir - pointer to buffer for subdirectory component, if any
* fname - pointer to buffer for file base name component, if any
* ext - pointer to buffer for file name extension component, if any
*
*Exit:
* drive - pointer to drive string. Includes ':' if a drive was given.
* dir - pointer to subdirectory string. Includes leading and trailing
* '/' or '\', if any.
* fname - pointer to file base name
* ext - pointer to file extension, if any. Includes leading '.'.
*
*Exceptions:
*
*******************************************************************************/
void __cdecl _tsplitpath (
register const _TSCHAR *path,
_TSCHAR *drive,
_TSCHAR *dir,
_TSCHAR *fname,
_TSCHAR *ext
)
{
register _TSCHAR *p;
_TSCHAR *last_slash = NULL, *dot = NULL;
unsigned len;
/* we assume that the path argument has the following form, where any
* or all of the components may be missing.
*
* <drive><dir><fname><ext>
*
* and each of the components has the following expected form(s)
*
* drive:
* 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
* ':'
* dir:
* 0 to _MAX_DIR-1 characters in the form of an absolute path
* (leading '/' or '\') or relative path, the last of which, if
* any, must be a '/' or '\'. E.g -
* absolute path:
* \top\next\last\ ; or
* /top/next/last/
* relative path:
* top\next\last\ ; or
* top/next/last/
* Mixed use of '/' and '\' within a path is also tolerated
* fname:
* 0 to _MAX_FNAME-1 characters not including the '.' character
* ext:
* 0 to _MAX_EXT-1 characters where, if any, the first must be a
* '.'
*
*/
/* extract drive letter and :, if any */
if ((_tcslen(path) >= (_MAX_DRIVE - 2)) && (*(path + _MAX_DRIVE - 2) == _T(':'))) {
if (drive) {
_tcsncpy(drive, path, _MAX_DRIVE - 1);
*(drive + _MAX_DRIVE-1) = _T('\0');
}
path += _MAX_DRIVE - 1;
}
else if (drive) {
*drive = _T('\0');
}
/* extract path string, if any. Path now points to the first character
* of the path, if any, or the filename or extension, if no path was
* specified. Scan ahead for the last occurence, if any, of a '/' or
* '\' path separator character. If none is found, there is no path.
* We will also note the last '.' character found, if any, to aid in
* handling the extension.
*/
for (last_slash = NULL, p = (_TSCHAR *)path; *p; p++) {
#ifdef _MBCS
if (_ismbblead(*p))
p++;
else {
#endif
if (*p == _T('/') || *p == _T('\\'))
/* point to one beyond for later copy */
last_slash = p + 1;
else if (*p == _T('.'))
dot = p;
#ifdef _MBCS
}
#endif
}
if (last_slash) {
/* found a path - copy up through last_slash or max. characters
* allowed, whichever is smaller
*/
if (dir) {
len = __min((unsigned)(((char *)last_slash - (char *)path) / sizeof(_TSCHAR)),
(_MAX_DIR - 1));
_tcsncpy(dir, path, len);
*(dir + len) = _T('\0');
}
path = last_slash;
}
else if (dir) {
/* no path found */
*dir = _T('\0');
}
/* extract file name and extension, if any. Path now points to the
* first character of the file name, if any, or the extension if no
* file name was given. Dot points to the '.' beginning the extension,
* if any.
*/
if (dot && (dot >= path)) {
/* found the marker for an extension - copy the file name up to
* the '.'.
*/
if (fname) {
len = __min((unsigned)(((char *)dot - (char *)path) / sizeof(_TSCHAR)),
(_MAX_FNAME - 1));
_tcsncpy(fname, path, len);
*(fname + len) = _T('\0');
}
/* now we can get the extension - remember that p still points
* to the terminating nul character of path.
*/
if (ext) {
len = __min((unsigned)(((char *)p - (char *)dot) / sizeof(_TSCHAR)),
(_MAX_EXT - 1));
_tcsncpy(ext, dot, len);
*(ext + len) = _T('\0');
}
}
else {
/* found no extension, give empty extension and copy rest of
* string into fname.
*/
if (fname) {
len = __min((unsigned)(((char *)p - (char *)path) / sizeof(_TSCHAR)),
(_MAX_FNAME - 1));
_tcsncpy(fname, path, len);
*(fname + len) = _T('\0');
}
if (ext) {
*ext = _T('\0');
}
}
}