/* 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  /* 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, * refpath;
    len = mbstowcs( NULL, path, 0 );
    refcopy = malloc((1 + len) * sizeof(wchar_t));

    /* create the wide character reference copy of path,
     * and step over the drive designator, if present ...
     */

    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 );
    }

    free(refcopy);
    /* 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: basename.c,v $$Revision: 1.2 $: end of file */
