newlib-cygwin/winsup/mingw/mingwex/dirname.c

175 lines
4.7 KiB
C
Raw Normal View History

/* dirname.c
*
* $Id$
*
* Provides an implementation of the "dirname" 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 <string.h>
#include <libgen.h>
#ifndef __cdecl /* If compiling on any non-Win32 platform ... */
#define __cdecl /* this may not be defined. */
#endif
__cdecl char *dirname( char *path )
{
static char retfail[] = "?:.";
char *retname, *basename, *copyptr = retfail;
if( path && *path )
{
retname = path;
/* SUSv3 identifies a special case, where path is exactly equal to "//";
* (we will also accept "\\" in the Win32 context, but not "/\" or "\/",
* and neither will we consider paths with an initial drive designator).
* For this special case, SUSv3 allows the implementation to choose to
* return "/" or "//", (or "\" or "\\", since this is Win32); we will
* simply return the path unchanged, (i.e. "//" or "\\").
*/
if( (*path == '/') || (*path == '\\') )
{
if( (path[1] == *retname) && (path[2] == '\0') )
return retname;
}
/* For all other cases ...
* step over the drive designator, if present, copying it to retfail ...
* (FIXME: maybe should confirm *path is a valid drive designator).
*/
else if( *path && (path[1] == ':') )
{
*copyptr++ = *path++;
*copyptr++ = *path++;
}
if( *path )
{
/* reproduce the scanning logic of the "basename" function
* to locate the basename component of the current path string,
* (but also remember where the dirname component starts).
*/
for( retname = basename = path ; *path ; ++path )
{
if( (*path == '/') || (*path == '\\') )
{
/* we found a dir separator ...
* step over it, and any others which immediately follow it
*/
while( (*path == '/') || (*path == '\\') )
++path;
/* if we didn't reach the end of the path string ... */
if( *path )
/* then we have a new candidate for the base name */
basename = path;
else
/* we struck an early termination of the path string,
* with trailing dir separators following the base name,
* so break out of the for loop, to avoid overrun.
*/
break;
}
}
/* now check,
* to confirm that we have distinct dirname and basename components
*/
if( basename > retname )
{
/* and, when we do ...
* backtrack over all trailing separators on the dirname component,
* (but preserve exactly two initial dirname separators, if identical),
* and add a NULL terminator in their place.
*/
--basename;
while( (basename > retname) && ((*basename == '/') || (*basename == '\\')) )
--basename;
if( (basename == retname) && ((*retname == '/') || (*retname == '\\'))
&& (retname[1] == *retname) && (retname[2] != '/') && (retname[2] != '\\') )
++basename;
*++basename = '\0';
/* adjust the start point of the dirname,
* to accommodate the Win32 drive designator, if it was present.
*/
if( copyptr > retfail )
retname -= 2;
/* if the resultant dirname begins with EXACTLY two dir separators,
* AND both are identical, then we preserve them.
*/
path = copyptr = retname;
while( ((*path == '/') || (*path == '\\')) )
++path;
if( ((path - retname) == 2) && (*++copyptr == *retname) )
++copyptr;
/* and finally ...
* we remove any residual, redundantly duplicated separators from the dirname,
* reterminate, and return it.
*/
path = copyptr;
while( *path )
{
if( ((*copyptr++ = *path) == '/') || (*path++ == '\\') )
{
while( (*path == '/') || (*path == '\\') )
++path;
}
}
*copyptr = '\0';
return retname;
}
else if( (*retname == '/') || (*retname == '\\') )
{
*copyptr++ = *retname;
*copyptr = '\0';
return retfail;
}
}
}
/* path is NULL, or an empty string; default return value is "." ...
* return this in our own static buffer, but strcpy it, just in case
* the caller trashed it after a previous call.
*/
strcpy( copyptr, "." );
return retfail;
}
/* $RCSfile$: end of file */