| /* 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; | |
| } |