af8e63023a
containing multibyte character strings.
168 lines
4.6 KiB
C
Executable File
168 lines
4.6 KiB
C
Executable File
/* basename.c
|
|
*
|
|
* $Id$
|
|
*
|
|
* Provides an implementation of the "basename" function, conforming
|
|
* to SUSv3, with extensions to accommodate Win32 drive designators,
|
|
* and suitable for use on native Microsoft(R) Win32 platforms.
|
|
*
|
|
* Written by Keith Marshall <keithmarshall@users.sourceforge.net>
|
|
*
|
|
* This is free software. You may redistribute and/or modify it as you
|
|
* see fit, without restriction of copyright.
|
|
*
|
|
* This software is provided "as is", in the hope that it may be useful,
|
|
* but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of
|
|
* MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no
|
|
* time will the author accept any form of liability for any damages,
|
|
* however caused, resulting from the use of this software.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <libgen.h>
|
|
#include <locale.h>
|
|
|
|
#ifndef __cdecl /* If compiling on any non-Win32 platform ... */
|
|
#define __cdecl /* this may not be defined. */
|
|
#endif
|
|
|
|
__cdecl char *basename( char *path )
|
|
{
|
|
size_t len;
|
|
static char *retfail = NULL;
|
|
|
|
/* to handle path names for files in multibyte character locales,
|
|
* we need to set up LC_CTYPE to match the host file system locale
|
|
*/
|
|
|
|
char *locale = setlocale( LC_CTYPE, NULL );
|
|
if( locale != NULL ) locale = strdup( locale );
|
|
setlocale( LC_CTYPE, "" );
|
|
|
|
if( path && *path )
|
|
{
|
|
/* allocate sufficient local storage space,
|
|
* in which to create a wide character reference copy of path
|
|
*/
|
|
|
|
wchar_t refcopy[1 + (len = mbstowcs( NULL, path, 0 ))];
|
|
|
|
/* create the wide character reference copy of path,
|
|
* and step over the drive designator, if present ...
|
|
*/
|
|
|
|
wchar_t *refpath = refcopy;
|
|
if( ((len = mbstowcs( refpath, path, len )) > 1) && (refpath[1] == L':') )
|
|
{
|
|
/* FIXME: maybe should confirm *refpath is a valid drive designator */
|
|
|
|
refpath += 2;
|
|
}
|
|
|
|
/* ensure that our wide character reference path is NUL terminated */
|
|
|
|
refcopy[ len ] = L'\0';
|
|
|
|
/* check again, just to ensure we still have a non-empty path name ... */
|
|
|
|
if( *refpath )
|
|
{
|
|
/* and, when we do, process it in the wide character domain ...
|
|
* scanning from left to right, to the char after the final dir separator
|
|
*/
|
|
|
|
wchar_t *refname;
|
|
for( refname = refpath ; *refpath ; ++refpath )
|
|
{
|
|
if( (*refpath == L'/') || (*refpath == L'\\') )
|
|
{
|
|
/* we found a dir separator ...
|
|
* step over it, and any others which immediately follow it
|
|
*/
|
|
|
|
while( (*refpath == L'/') || (*refpath == L'\\') )
|
|
++refpath;
|
|
|
|
/* if we didn't reach the end of the path string ... */
|
|
|
|
if( *refpath )
|
|
|
|
/* then we have a new candidate for the base name */
|
|
|
|
refname = refpath;
|
|
|
|
/* otherwise ...
|
|
* strip off any trailing dir separators which we found
|
|
*/
|
|
|
|
else while( (refpath > refname)
|
|
&& ((*--refpath == L'/') || (*refpath == L'\\')) )
|
|
*refpath = L'\0';
|
|
}
|
|
}
|
|
|
|
/* in the wide character domain ...
|
|
* refname now points at the resolved base name ...
|
|
*/
|
|
|
|
if( *refname )
|
|
{
|
|
/* if it's not empty,
|
|
* then we transform the full normalised path back into
|
|
* the multibyte character domain, and skip over the dirname,
|
|
* to return the resolved basename.
|
|
*/
|
|
|
|
if( (len = wcstombs( path, refcopy, len )) != (size_t)(-1) )
|
|
path[ len ] = '\0';
|
|
*refname = L'\0';
|
|
if( (len = wcstombs( NULL, refcopy, 0 )) != (size_t)(-1) )
|
|
path += len;
|
|
}
|
|
|
|
else
|
|
{
|
|
/* the basename is empty, so return the default value of "/",
|
|
* transforming from wide char to multibyte char domain, and
|
|
* returning it in our own buffer.
|
|
*/
|
|
|
|
retfail = realloc( retfail, len = 1 + wcstombs( NULL, L"/", 0 ));
|
|
wcstombs( path = retfail, L"/", len );
|
|
}
|
|
|
|
/* restore the caller's locale, clean up, and return the result */
|
|
|
|
setlocale( LC_CTYPE, locale );
|
|
free( locale );
|
|
return( path );
|
|
}
|
|
|
|
/* or we had an empty residual path name, after the drive designator,
|
|
* in which case we simply fall through ...
|
|
*/
|
|
}
|
|
|
|
/* and, if we get to here ...
|
|
* the path name is either NULL, or it decomposes to an empty string;
|
|
* in either case, we return the default value of "." in our own buffer,
|
|
* reloading it with the correct value, transformed from the wide char
|
|
* to the multibyte char domain, just in case the caller trashed it
|
|
* after a previous call.
|
|
*/
|
|
|
|
retfail = realloc( retfail, len = 1 + wcstombs( NULL, L".", 0 ));
|
|
wcstombs( retfail, L".", len );
|
|
|
|
/* restore the caller's locale, clean up, and return the result */
|
|
|
|
setlocale( LC_CTYPE, locale );
|
|
free( locale );
|
|
return( retfail );
|
|
}
|
|
|
|
/* $RCSfile$$Revision$: end of file */
|