[PATCH] reimplement dirname v2

Tito farmatito at tiscali.it
Wed Sep 7 20:29:25 UTC 2011


Ops...and now with text :-)

Hi,
in my attempt to reinvent the wheel I've come up
with a nice implementation  of dirname for busybox.
The pros are:
1) you can have it the GNU's way and modify the path passed
2) or the *BSD way and work with a malloced copy of path
     that you have to free yourself.
3) it covers all test cases i was able to think of:
 *  path          dirname
 *  "/usr/lib"    "/usr"
 *  "/usr/"       "/" 
 *  "/"           "/"
 *  "usr"         "." 
 *  "."           "."
 *  ".."          "."
 * This examples are from coreutils dirname:
 *  "usr/lib      "usr"
 *  ""            "."
 *  " "           "."
 * "//usr//bin//" "//usr"
 * "////"         "/"
 * "/è/è//"        "/è"
 * This example is added by me:
 * NULL           "."
4) you don't need to declare libgen.h
The contras:
1) it doesn't handle dir separators other than '/' (but 
     seems to me that we don't do that also in other
     functions)
2) it increases binary size a little
scripts/bloat-o-meter busybox_ref  busybox_unstripped
function                                             old     new   delta
bb_dirname                                             -      90     +90
xmalloc_dirname                                        -      10     +10
syslogd_main                                        1862    1867      +5
dirname_main                                          28      26      -2
udhcp_get_option                                     215     211      -4
unzip_create_leading_dirs                             53      46      -7
install_main                                         697     690      -7
unicode_conv_to_printable2                           194     186      -8
sv_main                                             1200    1191      -9
cp_main                                              354     344     -10
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 1/7 up/down: 105/-47)            Total: 58 bytes

I was not able to shrink the code more than that.
Hints, critics, improvements are welcome.
The bb *dirname functions are tested against the 
test cases shown above. The changes to the apps
are trivial but not tested so "CAVEAT" just in case
this will be really checked in, which I don't believe ;-)

Ciao,
Tito

char* FAST_FUNC bb_dirname(char *s) {
        char *p;
        int firstrun = 0;

        if (!s || !*s)
                return xstrdup(".");

 STRIP_TRAILING_SLASHES_BUT_LAST:
        while ((p = last_char_is(s,'/')) != NULL && p != s)
                *p = '\0';

        if (!firstrun) {
                p = strrchr(s, '/');
                if (p) {
                        *++p = '\0';
                        firstrun++;
                        goto STRIP_TRAILING_SLASHES_BUT_LAST;
                } else {
                        s[0] = '.';
                        s[1] = '\0';
                }
        }
        return s;
}

char* FAST_FUNC xmalloc_dirname(const char *path) {
        return bb_dirname(xstrdup(path));
}

On Wednesday 07 September 2011 21:37:02 Tito wrote:
> Alternative dirname implementation for busybox
> 
> Signed-off-by Tito Ragusa <farmatito at tiscali.it>
> 
> --- include/libbb.h.original	2011-08-29 01:52:50.000000000 +0200
> +++ include/libbb.h	2011-09-01 21:21:19.000000000 +0200
> @@ -133,12 +133,9 @@ int vdprintf(int d, const char *format,
>  #endif
>  /* klogctl is in libc's klog.h, but we cheat and not #include that */
>  int klogctl(int type, char *b, int len);
> -/* This is declared here rather than #including <libgen.h> in order to avoid
> - * confusing the two versions of basename.  See the dirname/basename man page
> - * for details. */
> -#if !defined __FreeBSD__
> -char *dirname(char *path);
> -#endif
> +
> +char *bb_dirname(char *path) FAST_FUNC;
> +char *xmalloc_dirname(const char *path) FAST_FUNC;
>  #ifndef PATH_MAX
>  # define PATH_MAX 256
>  #endif
> --- libbb/Kbuild.src.original	2011-08-29 01:51:59.000000000 +0200
> +++ libbb/Kbuild.src	2011-08-29 01:33:13.000000000 +0200
> @@ -29,6 +29,7 @@ lib-y += copyfd.o
>  lib-y += crc32.o
>  lib-y += create_icmp6_socket.o
>  lib-y += create_icmp_socket.o
> +lib-y += dirname.o
>  lib-y += default_error_retval.o
>  lib-y += device_open.o
>  lib-y += dump.o
> --- coreutils/dirname.c.original	2011-08-18 14:07:40.000000000 +0200
> +++ coreutils/dirname.c	2011-09-05 22:34:48.000000000 +0200
> @@ -28,6 +28,6 @@
>  int dirname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
>  int dirname_main(int argc UNUSED_PARAM, char **argv)
>  {
> -	puts(dirname(single_argv(argv)));
> +	puts(bb_dirname(single_argv(argv)));
>  	return fflush_all();
>  }
> --- coreutils/cp.c.original	2011-08-18 14:07:40.000000000 +0200
> +++ coreutils/cp.c	2011-09-01 16:34:42.000000000 +0200
> @@ -178,15 +178,13 @@ int cp_main(int argc, char **argv)
>  	while (1) {
>  #if ENABLE_FEATURE_CP_LONG_OPTIONS
>  		if (flags & OPT_parents) {
> -			char *dest_dup;
>  			char *dest_dir;
>  			dest = concat_path_file(last, *argv);
> -			dest_dup = xstrdup(dest);
> -			dest_dir = dirname(dest_dup);
> +			dest_dir = xmalloc_dirname(dest);
>  			if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) {
>  				return EXIT_FAILURE;
>  			}
> -			free(dest_dup);
> +			free(dest_dir);
>  			goto DO_COPY;
>  		}
>  #endif
> --- coreutils/install.c.original	2011-09-01 16:35:03.000000000 +0200
> +++ coreutils/install.c	2011-09-01 16:35:56.000000000 +0200
> @@ -177,8 +177,8 @@ int install_main(int argc, char **argv)
>  			}
>  		} else {
>  			if (opts & OPT_MKDIR_LEADING) {
> -				char *ddir = xstrdup(dest);
> -				bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR);
> +				char *ddir = xmalloc_dirname(dest);
> +				bb_make_directory(ddir, 0755, FILEUTILS_RECUR);
>  				/* errors are not checked. copy_file
>  				 * will fail if dir is not created. */
>  				free(ddir);
> --- coreutils/rmdir.c.original	2011-09-06 21:55:05.000000000 +0200
> +++ coreutils/rmdir.c	2011-09-01 21:04:22.000000000 +0200
> @@ -69,7 +69,7 @@ int rmdir_main(int argc UNUSED_PARAM, ch
>  				status = EXIT_FAILURE;
>  			} else if (flags & PARENTS) {
>  				/* Note: path was not "" since rmdir succeeded. */
> -				path = dirname(path);
> +				path = bb_dirname(path);
>  				/* Path is now just the parent component.  Dirname
>  				 * returns "." if there are no parents.
>  				 */
> --- archival/unzip.c.original	2011-06-22 14:29:58.000000000 +0200
> +++ archival/unzip.c	2011-09-01 16:37:52.000000000 +0200
> @@ -233,8 +233,8 @@ static void unzip_skip(off_t skip)
>  static void unzip_create_leading_dirs(const char *fn)
>  {
>  	/* Create all leading directories */
> -	char *name = xstrdup(fn);
> -	if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
> +	char *name = xmalloc_dirname(fn);
> +	if (bb_make_directory(name, 0777, FILEUTILS_RECUR)) {
>  		bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */
>  	}
>  	free(name);
> --- /dev/null	2011-09-07 20:21:14.435919408 +0200
> +++ libbb/dirname.c	2011-09-07 20:59:21.000000000 +0200
> @@ -0,0 +1,60 @@
> +/* vi: set sw=4 ts=4: */
> +/*
> + * Alternative dirname implementation for busybox
> + *
> + * Copyright (C) 2011  Tito Ragusa <farmatito at tiscali.it>
> + *
> + * Licensed under GPLv2 or later, see file LICENSE in this source tree.
> + */
> + 
> +#include "libbb.h"
> +
> +/* The following list of examples (taken from SUSv2) shows the strings
> + * returned by dirname() for different paths:
> + *
> + *  path          dirname
> + *  "/usr/lib"    "/usr"
> + *  "/usr/"       "/" 
> + *  "/"           "/"
> + *  "usr"         "." 
> + *  "."           "."
> + *  ".."          "."
> + * This examples are from coreutils dirname:
> + *  "usr/lib      "usr"
> + *  ""            "."
> + *  " "           "."
> + * "//usr//bin//" "//usr"
> + * "////"         "/"
> + * "/è/è//"        "/è"
> + * This example is added by me:
> + * NULL           "."
> + */
> +
> +char* FAST_FUNC bb_dirname(char *s) {
> +	char *p;
> +	int firstrun = 0;
> +
> +	if (!s || !*s)
> +		return xstrdup(".");
> +
> + STRIP_TRAILING_SLASHES_BUT_LAST:
> +	while ((p = last_char_is(s,'/')) != NULL && p != s)
> +		*p = '\0';
> +
> +	if (!firstrun) {
> +		p = strrchr(s, '/');
> +		if (p) {
> +			*++p = '\0';
> +			firstrun++;
> +			goto STRIP_TRAILING_SLASHES_BUT_LAST;
> +		} else {
> +			s[0] = '.';
> +			s[1] = '\0';
> +		}
> +	}
> +	return s;
> +}
> +
> +char* FAST_FUNC xmalloc_dirname(const char *path) {
> +	return bb_dirname(xstrdup(path));
> +}
> 


More information about the busybox mailing list