/* 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 * * 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 #include #include #include #include #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 */