|  | /* basename.c | 
|  | * | 
|  | * $Id: basename.c,v 1.2 2007/03/08 23:15:58 keithmarshall Exp $ | 
|  | * | 
|  | * 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 | 
|  | #define __cdecl | 
|  | #endif | 
|  |  | 
|  | char * __cdecl | 
|  | basename (char *path) | 
|  | { | 
|  | static char *retfail = NULL; | 
|  | size_t len; | 
|  | /* 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; | 
|  | } |