1 /* basename.c
2 *
3 * $Id: basename.c,v 1.2 2007/03/08 23:15:58 keithmarshall Exp $
4 *
5 * Provides an implementation of the "basename" function, conforming
6 * to SUSv3, with extensions to accommodate Win32 drive designators,
7 * and suitable for use on native Microsoft(R) Win32 platforms.
8 *
9 * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
10 *
11 * This is free software. You may redistribute and/or modify it as you
12 * see fit, without restriction of copyright.
13 *
14 * This software is provided "as is", in the hope that it may be useful,
15 * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of
16 * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no
17 * time will the author accept any form of liability for any damages,
18 * however caused, resulting from the use of this software.
19 *
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <libgen.h>
26 #include <locale.h>
27
28 #ifndef __cdecl
29 #define __cdecl
30 #endif
31
32 char * __cdecl
basename(char * path)33 basename (char *path)
34 {
35 static char *retfail = NULL;
36 size_t len;
37 /* to handle path names for files in multibyte character locales,
38 * we need to set up LC_CTYPE to match the host file system locale
39 */
40 char *locale = setlocale (LC_CTYPE, NULL);
41
42 if (locale != NULL)
43 locale = strdup (locale);
44 setlocale (LC_CTYPE, "");
45
46 if (path && *path)
47 {
48 /* allocate sufficient local storage space,
49 * in which to create a wide character reference copy of path
50 */
51 wchar_t refcopy[1 + (len = mbstowcs (NULL, path, 0))];
52 /* create the wide character reference copy of path,
53 * and step over the drive designator, if present ...
54 */
55 wchar_t *refpath = refcopy;
56
57 if ((len = mbstowcs( refpath, path, len)) > 1 && refpath[1] == L':')
58 {
59 /* FIXME: maybe should confirm *refpath is a valid drive designator */
60 refpath += 2;
61 }
62 /* ensure that our wide character reference path is NUL terminated */
63 refcopy[len] = L'\0';
64 /* check again, just to ensure we still have a non-empty path name ... */
65 if (*refpath)
66 {
67 /* and, when we do, process it in the wide character domain ...
68 * scanning from left to right, to the char after the final dir separator. */
69 wchar_t *refname;
70
71 for (refname = refpath; *refpath; ++refpath)
72 {
73 if (*refpath == L'/' || *refpath == L'\\')
74 {
75 /* we found a dir separator ...
76 * step over it, and any others which immediately follow it. */
77 while (*refpath == L'/' || *refpath == L'\\')
78 ++refpath;
79 /* if we didn't reach the end of the path string ... */
80 if (*refpath)
81 /* then we have a new candidate for the base name. */
82 refname = refpath;
83 /* otherwise ...
84 * strip off any trailing dir separators which we found. */
85 else
86 while (refpath > refname
87 && (*--refpath == L'/' || *refpath == L'\\') )
88 *refpath = L'\0';
89 }
90 }
91 /* in the wide character domain ...
92 * refname now points at the resolved base name ... */
93 if (*refname)
94 {
95 /* if it's not empty,
96 * then we transform the full normalised path back into
97 * the multibyte character domain, and skip over the dirname,
98 * to return the resolved basename. */
99 if ((len = wcstombs( path, refcopy, len)) != (size_t)(-1))
100 path[len] = '\0';
101 *refname = L'\0';
102 if ((len = wcstombs( NULL, refcopy, 0 )) != (size_t)(-1))
103 path += len;
104 }
105 else
106 {
107 /* the basename is empty, so return the default value of "/",
108 * transforming from wide char to multibyte char domain, and
109 * returning it in our own buffer. */
110 retfail = realloc (retfail, len = 1 + wcstombs (NULL, L"/", 0));
111 wcstombs (path = retfail, L"/", len);
112 }
113 /* restore the caller's locale, clean up, and return the result */
114 setlocale (LC_CTYPE, locale);
115 free (locale);
116 return path;
117 }
118 /* or we had an empty residual path name, after the drive designator,
119 * in which case we simply fall through ... */
120 }
121 /* and, if we get to here ...
122 * the path name is either NULL, or it decomposes to an empty string;
123 * in either case, we return the default value of "." in our own buffer,
124 * reloading it with the correct value, transformed from the wide char
125 * to the multibyte char domain, just in case the caller trashed it
126 * after a previous call.
127 */
128 retfail = realloc (retfail, len = 1 + wcstombs( NULL, L".", 0));
129 wcstombs (retfail, L".", len);
130
131 /* restore the caller's locale, clean up, and return the result. */
132 setlocale (LC_CTYPE, locale);
133 free (locale);
134 return retfail;
135 }
136