• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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