[cairo] [PATCH 5/5] util: Added a basename library which can be used on Windows builds.

Bill Spitzak spitzak at gmail.com
Thu Jun 24 14:59:11 PDT 2010


Also watch out that the "GNU" version may be wanted, it is much better. 
It returns "" for "/". The Posix version is quite useless especially on 
Windows as the result is "/" leading to filenames with two slashes.

I would certainly stay away from mbtowcs as it can truncate and even 
segfault on bad strings.

Zoxc wrote:
> ---
>  util/basename.c |  165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  util/basename.h |    7 ++
>  2 files changed, 172 insertions(+), 0 deletions(-)
>  create mode 100644 util/basename.c
>  create mode 100644 util/basename.h
> 
> diff --git a/util/basename.c b/util/basename.c
> new file mode 100644
> index 0000000..8d034cf
> --- /dev/null
> +++ b/util/basename.c
> @@ -0,0 +1,165 @@
> +/* 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 at 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 <locale.h>
> +
> +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 */
> diff --git a/util/basename.h b/util/basename.h
> new file mode 100644
> index 0000000..0392a29
> --- /dev/null
> +++ b/util/basename.h
> @@ -0,0 +1,7 @@
> +
> +#ifndef BASENAME_H
> +#define BASENAME_H
> +
> +char *basename (char *path);
> +
> +#endif /* BASENAME_H */


More information about the cairo mailing list