basename() & dirname() <-- a bug?

[email protected] ("Rich Clingman") Tue, 6 Oct 1998 05:03:51 -0700
Newsgroups php.beta
Message-ID <022601bdf121$63325660$9e5c1ed1@pentium>
** This is going to beta list as this seems more appropriate than the
general discussion. Please copy me on any replies.  I dropped my name from
the list a couple days ago and today tried to add it again.  It says I'm
already on the list, but I haven't seen any messages.  Maybe it's just dead
because the beta testing is done....

I was trying to use basename() & dirname() to break up a parse_url()[path]
into the directory & filename.  It seems these functions just break at the
last '/'.

Here's what they return:
PATH       dirname()  basename()
adir/afile  adir       afile <<this one is ok
adir/       adir       adir/
/adir/     (nothing)   adir
afile       afile      afile
/          (nothing)    /

This would be nice (and seems correct to me):
PATH       dirname()  basename()
adir/afile  adir       afile
adir/       adir      (nothing)
/adir/      /adir     (nothing)
afile      (nothing)   afile
/afile       /             afile
/             /          (nothing)

I'm using phpweb distribution...
PHP Version 3.0.5-dev
System: Windows 95/98 4.0
Build Date: Oct 3 1998

The ChangeLog says, "Added basename and dirname functions similar to sh
counterparts."  I don't know if these are the desired results.

If I'm going to gripe...
Below is patched source code for what I think is supposed to happen.  If the
results I'm getting are the desired results, I'd love to still have the
functions below (with any suitable name).  The existing ones don't seem too
useful--which could be a lack of understanding on my part.

Rich Clingman
[email protected]

**** All of the tabs disappeared, so I'm attaching a file
as well...  Sorry.  It's short.

###  patches to basename() & dirname() in functions/string.c
###  Rich Clingman [email protected] 06OCT98
###  manual "patch" notation: < take out    > put in

void php3_basename(INTERNAL_FUNCTION_PARAMETERS)
{
pval *str;
char *ret, *c;
TLS_VARS;

if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &str)) {
  WRONG_PARAM_COUNT;
}
convert_to_string(str);

### As the function is rewritten below,
### we do not modify the string
### (only return a value).
### Can we use 'str' directly?
### If so, dump this line
### and change 'ret' to 'str' in added lines.

ret = estrdup(str->value.str.val);

< c = ret + str->value.str.len -1;
<  while (*c == '/'
<  #ifdef MSVC5
<       || *c == '\\'
<  #endif
<    )
<    c--;
<   *(c + 1) = '\0';
< if ((c = strrchr(ret, '/'))
<#ifdef MSVC5
<  || (c = strrchr(ret, '\\'))
<#endif
<  ) {
<  RETVAL_STRING(c + 1,1);
< } else {
<    RETVAL_STRING(str->value.str.val,1);
< }

> // if no slash, then no dir, only filename
> if ( (c=strrchr(ret, '/'))
>#ifdef MSVC5
>  || (c=strrchr(ret, '\\'))
>#endif
>  )
>  RETVAL_STRING(c+1,1); // backed to slash, move ahead 1 and return
> else
>  RETVAL_STRING(ret,1); // no slash, only filename

efree(ret);
}


###### it appears the only call to the following
###### is from php3_dirname() so I put the patch
###### there & suggest removing this.

<PHPAPI void _php3_dirname(char *str, int len) {
< register char *c;
<
< c = str + len - 1;
< while (*c == '/'
<#ifdef MSVC5
<     || *c == '\\'
<#endif
<  )
<  c--; /* strip trailing slashes */
< *(c + 1) = '\0';
< if ((c = strrchr(str, '/'))
<#ifdef MSVC5
<  || (c = strrchr(str, '\\'))
<#endif
<  )
<  *c='\0';
<}

void php3_dirname(INTERNAL_FUNCTION_PARAMETERS)
{
pval *str;
char *ret;
TLS_VARS;
> register char *c;

if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &str)) {
  WRONG_PARAM_COUNT;
}
convert_to_string(str);
ret = estrdup(str->value.str.val);

< _php3_dirname(ret,str->value.str.len);

> c=ret + len - 1;
> if ( *c=='/' // do we end with a slash?
>#ifdef MSVC5
>  || *c == '\\'
>#endif
>  || (c = strrchr(ret, '/')) // or found a slash?
>#ifdef MSVC5
>  || (c = strrchr(ret, '\\'))
>#endif
>  )
> {
>  if ( c == str ) // if only a slash remains (root dir)
>   c+1='\0'; // leave it
>  else
>   c='\0'; // else zap this slash
> } else {
>  *ret = '\0'; // if no slash, no dir, only filename
> }

RETVAL_STRING(ret,1);
efree(ret);
}
basename.patch (application/octet-stream, 2.3 KB)

###  patches to basename() & dirname() in functions/string.c
###  Rich Clingman [email protected] 06OCT98

###  manual "patch" notation: < take out    > put in


void php3_basename(INTERNAL_FUNCTION_PARAMETERS)
{
	pval *str;
	char *ret, *c;
	TLS_VARS;
	
	if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &str)) {
		WRONG_PARAM_COUNT;
	}
	convert_to_string(str);

### as the function is rewritten below,
### since we do not modify the string, 
### (only return a value),
### can't we use 'str' directly?

	ret = estrdup(str->value.str.val);
	
<	c = ret + str->value.str.len -1;	
< 	while (*c == '/'
<  #ifdef MSVC5
<  		   || *c == '\\'
<  #endif
<  		)
<  		c--;
<  	*(c + 1) = '\0';	
<	if ((c = strrchr(ret, '/'))
<#ifdef MSVC5
<		|| (c = strrchr(ret, '\\'))
<#endif
<		) {
<		RETVAL_STRING(c + 1,1);
<	} else {
<  		RETVAL_STRING(str->value.str.val,1);
<	}
	
>	// if no slash, then no dir, only filename
>	if ( (c=strrchr(ret, '/'))
>#ifdef MSVC5
>		|| (c=strrchr(ret, '\\'))
>#endif
>		)
>		RETVAL_STRING(c+1,1); // backed to slash, move ahead 1 and return
>	else
>		RETVAL_STRING(ret,1); // no slash, only filename

	efree(ret);
}


###### it appears the only call to the following
###### is from php3_dirname() so I put the patch
###### there & suggest removing this.

<PHPAPI void _php3_dirname(char *str, int len) {
<	register char *c;
<
<	c = str + len - 1;
<	while (*c == '/'
<#ifdef MSVC5
<		   || *c == '\\'
<#endif
<		)
<		c--; /* strip trailing slashes */
<	*(c + 1) = '\0';
<	if ((c = strrchr(str, '/'))
<#ifdef MSVC5
<		|| (c = strrchr(str, '\\'))
<#endif
<		)
<		*c='\0';
<}

void php3_dirname(INTERNAL_FUNCTION_PARAMETERS)
{
	pval *str;
	char *ret;
	TLS_VARS;
>	register char *c;
	
	if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &str)) {
		WRONG_PARAM_COUNT;
	}
	convert_to_string(str);
	ret = estrdup(str->value.str.val);

<	_php3_dirname(ret,str->value.str.len);
	
>	c=ret + len - 1;
>	if ( *c=='/'	// do we end with a slash?
>#ifdef MSVC5
>		|| *c == '\\'
>#endif
>		|| (c = strrchr(ret, '/'))	// or found a slash?
>#ifdef MSVC5
>		|| (c = strrchr(ret, '\\'))
>#endif
>		)
>	{
>		if ( c == str )	// if only a slash remains (root dir)
>			c+1='\0'; // leave it
>		else
>			c='\0'; // else zap this slash
>	} else {
>		*ret = '\0';	// if no slash, no dir, only filename
>	}
	
	RETVAL_STRING(ret,1);
	efree(ret);
}