-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 According to Paul Eggert on 11/21/2005 5:42 PM:
Thanks for the review. Updated patch attached. > >>- /* If the file name ends in slash, use the trailing slash as >>- the basename if no non-slashes have been found. */ >>+ /* If the file name ends in slash, but no non-slashes have >>+ been found, then return the empty string. */ >> if (! *p) >> { >> if (ISSLASH (*base)) >>- base = p - 1; >>+ base = p; >> break; >> } > > > That is confusing. Wouldn't this be a lot simpler, as a replacement > for the entire main loop in last_component? > > for (p = base; +p; p++) > if (ISSLASH (*p)) > base = p + 1; Not quite - base only started as a slash if the name was absolute; thereafter, it was always assigned to a non-slash character. This is important, because trailing slashes must be ignored. Your simplification would return "" for something like "/a/", when it should instead return "a/". But given your complaint, I simplified the loop of last_component (and I agree that it is now a lot easier to understand without nested loops, even though the complexity remains O(n) for either implementation). > > + /* Collapse a sequence of trailing slashes into one. */ > + size_t length = base_len (base); > > This assumes C99 declaration-after-statement. Let's keep the code > portable to C89. Fixed. I'll have to remember to use -Wdeclaration-after-statement in addition to -Wall when smoke-screening my code. >>+ /* On systems with drive letters, `a/b:c' must return `./b:c' rather >>+ than `b:c' to avoid confusion with a drive letter. On systems >>+ with pure POSIX semantics, this is not an issue. */ >>+ if (FILE_SYSTEM_PREFIX_LEN (base)) >>+ { >>+ char *p = xmalloc (length + 3); >>+ p[0] = '.'; >>+ p[1] = DIRECTORY_SEPARATOR; >>+ memcpy (p + 2, base, length); >>+ p[length + 2] = '\0'; >>+ return p; >>+ } > > > This mishandles 'a/b:c/', no? The trailing slash gets dropped. Added that to the testsuite. But the trailing slash was preserved by the earlier line that bumped length if the name had trailing slashes. > > Also, what's the point of DIRECTORY_SEPARATOR? Can't we just use '/'? I kept it with DIRECTORY_SEPARATOR, in case the application sets DIRECTORY_SEPARATOR to '\\' in config.h; after all, systems with drive letters have users that are used to the backslash. But I don't have any problem with hard-coding it to '/' if others agree. Or should I change it to check ISSLASH (base[-1]), in which case use the existing spelling? So for 'a/b:c', use './b:c', for 'a\b:c', use '.\b:c', and for 'a:b:c', use './b:c'? > > >>+ if (prefix_len && ! FILE_SYSTEM_DRIVE_PREFIX_IS_ABSOLUTE > > > This would be easier to read this way: > > if (FILE_SYSTEM_DRIVE_PREFIX_IS_RELATIVE && prefix_len > > so that it's more obvious that the code is optimized away on normal systems. OK. I changed the macro to FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE throughout the code. > >>+ /* Be careful of drive prefixes, where that matters. */ >>+ if (0 < prefix_length) >>+ return (prefix_length >>+ + (! FILE_SYSTEM_DRIVE_PREFIX_IS_ABSOLUTE >>+ && ISSLASH (file[prefix_length]))); >>+ >>+ /* Don't strip the only slash from "/", or from "//" where that matters. >>*/ >>+ return (ISSLASH (file[0]) >>+ + (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (file[0]) >>+ && ISSLASH (file[1]) && ! ISSLASH (file[2]))); >> } > > > Hmm, a bit hard to read, for the same reason as above. Also, it tests > ISSLASH (file[0]) more than once. How about something like this > instead, right after initializing prefix_length? Or perhaps you can > come up with something better. > > prefix_length += > (prefix_length != 0 > ? FILE_SYSTEM_DRIVE_PREFIX_IS_RELATIVE && ISSLASH (file[prefix_length]) > : ! ISSLASH (file[0]) > : 0 > ? (DOUBLE_SLASH_IS_DISTINCT_ROOT > && ISSLASH (file[1]) && ! ISSLASH (file[2])) > ? 2 > : 1); > OK, I simplified dir_len based on your idea, so that ISSLASH is used at most once per character. ChangeLog: 2005-11-21 Eric Blake <[EMAIL PROTECTED]> * modules/dirname (Depends-on): Add xstrndup. * modules/dirname-tests: New module. * tests/test-dirname.c: New file, replacing dirname.c TEST_DIRNAME section. m4/ChangeLog: 2005-11-22 Eric Blake <[EMAIL PROTECTED]> * dirname.m4 (DOUBLE_SLASH_IS_DISTINCT_ROOT): New define. * dos.m4 (FILE_SYSTEM_PREFIX_LEN): Move from here to dirname.h. (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE): New define. lib/ChangeLog: 2005-11-22 Eric Blake <[EMAIL PROTECTED]> * backupfile.c (check_extension, numbered_backup): Adjust to changed semantics in dirname module. * filenamecat.c (file_name_concat): Ditto. * same.c (same_name): Ditto. 2005-11-22 Eric Blake <[EMAIL PROTECTED]>, Paul Eggert <[EMAIL PROTECTED]> * dirname.h (FILE_SYSTEM_PREFIX_LEN): Move here from dos.m4. [FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX]: Don't treat 1: as a drive prefix. (IS_ABSOLUTE_FILE_NAME): Treat all drive letters as absolute on platforms like cygwin with FILE_SYSTEM_DRIVE_PREFIX_IS_ABSOLUTE. (last_component): New method. * dirname.c (dir_len): Determine when drive letters need a subsequent slash. Preserve // when it is special. (dir_name): Don't append dot when drive letter is absolute. [TEST_DIRNAME]: Move into a full-blown gnulib test. * basename.c (base_name): New semantics - malloc the result. Preserve // when it is special. Preserve relative files that look like drive letters. (base_len): Preserve // when it is special. (last_component): New method, similar to old base_name semantics. * stripslash.c (strip_trailing_slashes): Use last_component, not base_name. Strip redundant slashes from ///. - -- Life is short - so eat dessert first! Eric Blake [EMAIL PROTECTED] -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Cygwin) Comment: Public key at home.comcast.net/~ericblake/eblake.gpg Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org iD8DBQFDgyWI84KuGfSFAYARAu+mAJ9VbAllE/JRMkMwJ3VswbPWqceYJwCfZsVT PkEyog6tOzOjB5mSJ+1BdgY= =SP9H -----END PGP SIGNATURE-----
Index: ChangeLog =================================================================== RCS file: /cvsroot/gnulib/gnulib/ChangeLog,v retrieving revision 1.445 diff -u -p -r1.445 ChangeLog Index: lib/ChangeLog =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/ChangeLog,v retrieving revision 1.1051 diff -u -p -r1.1051 ChangeLog Index: lib/backupfile.c =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/backupfile.c,v retrieving revision 1.47 diff -u -p -r1.47 backupfile.c --- lib/backupfile.c 23 Sep 2005 04:15:13 -0000 1.47 +++ lib/backupfile.c 22 Nov 2005 13:51:24 -0000 @@ -115,7 +115,7 @@ char const *simple_backup_suffix = "~"; static void check_extension (char *file, size_t filelen, char e) { - char *base = base_name (file); + char *base = last_component (file); size_t baselen = base_len (base); size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM; @@ -202,7 +202,7 @@ numbered_backup (char **buffer, size_t b struct dirent *dp; char *buf = *buffer; size_t versionlenmax = 1; - char *base = base_name (buf); + char *base = last_component (buf); size_t base_offset = base - buf; size_t baselen = base_len (base); Index: lib/basename.c =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/basename.c,v retrieving revision 1.26 diff -u -p -r1.26 basename.c --- lib/basename.c 19 Sep 2005 17:28:14 -0000 1.26 +++ lib/basename.c 22 Nov 2005 13:51:24 -0000 @@ -22,58 +22,107 @@ #endif #include "dirname.h" + #include <string.h> +#include "xalloc.h" +#include "xstrndup.h" + +/* Return the address of the last file name component of NAME. If + NAME has no relative file name components because it is a file + system root, return the empty string. */ + +char * +last_component (char const *name) +{ + char const *base = name + FILE_SYSTEM_PREFIX_LEN (name); + char const *p; + bool saw_slash = false; + + while (ISSLASH (*base)) + base++; + for (p = base; *p; p++) + if (ISSLASH (*p)) + saw_slash = true; + else if (saw_slash) + { + base = p; + saw_slash = false; + } + + return (char *) base; +} + /* In general, we can't use the builtin `basename' function if available, since it has different meanings in different environments. In some environments the builtin `basename' modifies its argument. - Return the address of the last file name component of NAME. If - NAME has no file name components because it is all slashes, return - NAME if it is empty, the address of its last slash otherwise. */ + Return the last file name component of NAME, allocated with + xmalloc. On systems with drive letters, a leading "./" + distinguishes relative names that would otherwise look like a drive + letter. Unlike POSIX basename(), NAME cannot be NULL, + base_name("") returns "", and the first trailing slash is not + stripped. + + If lstat (NAME) would succeed, then { chdir (dir_name (NAME)); + lstat (base_name (NAME)); } will access the same file. Likewise, + if the sequence { chdir (dir_name (NAME)); + rename (base_name (NAME), "foo"); } succeeds, you have renamed NAME + to "foo" in the same directory NAME was in. */ char * base_name (char const *name) { - char const *base = name + FILE_SYSTEM_PREFIX_LEN (name); - char const *p; + char const *base = last_component (name); + size_t length; - for (p = base; *p; p++) + /* If there is no last component, then name is a file system root or the + empty string. */ + if (! *base) + return xstrndup (name, base_len (name)); + + /* Collapse a sequence of trailing slashes into one. */ + length = base_len (base); + if (ISSLASH (base[length])) + length++; + + /* On systems with drive letters, `a/b:c' must return `./b:c' rather + than `b:c' to avoid confusion with a drive letter. On systems + with pure POSIX semantics, this is not an issue. */ + if (FILE_SYSTEM_PREFIX_LEN (base)) { - if (ISSLASH (*p)) - { - /* Treat multiple adjacent slashes like a single slash. */ - do p++; - while (ISSLASH (*p)); - - /* If the file name ends in slash, use the trailing slash as - the basename if no non-slashes have been found. */ - if (! *p) - { - if (ISSLASH (*base)) - base = p - 1; - break; - } - - /* *P is a non-slash preceded by a slash. */ - base = p; - } + char *p = xmalloc (length + 3); + p[0] = '.'; + p[1] = DIRECTORY_SEPARATOR; + memcpy (p + 2, base, length); + p[length + 2] = '\0'; + return p; } - return (char *) base; + /* Finally, copy the basename. */ + return xstrndup (base, length); } -/* Return the length of of the basename NAME. Typically NAME is the - value returned by base_name. Act like strlen (NAME), except omit - redundant trailing slashes. */ +/* Return the length of the basename NAME. Typically NAME is the + value returned by base_name or last_component. Act like strlen + (NAME), except omit all trailing slashes. */ size_t base_len (char const *name) { size_t len; + size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name); for (len = strlen (name); 1 < len && ISSLASH (name[len - 1]); len--) continue; + + if (DOUBLE_SLASH_IS_DISTINCT_ROOT && len == 1 + && ISSLASH (name[0]) && ISSLASH (name[1]) && ! name[2]) + return 2; + + if (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE && prefix_len + && len == prefix_len && ISSLASH (name[prefix_len])) + return prefix_len + 1; return len; } Index: lib/dirname.c =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/dirname.c,v retrieving revision 1.35 diff -u -p -r1.35 dirname.c --- lib/dirname.c 19 Sep 2005 17:28:14 -0000 1.35 +++ lib/dirname.c 22 Nov 2005 13:51:24 -0000 @@ -26,96 +26,62 @@ #include <string.h> #include "xalloc.h" -/* Return the length of `dirname (FILE)', or zero if FILE is - in the working directory. Works properly even if - there are trailing slashes (by effectively ignoring them). */ +/* Return the length of the prefix of FILE that will be used by + dir_name. If FILE is in the working directory, this returns zero + even though `dir_name (FILE)' will return ".". Works properly even + if there are trailing slashes (by effectively ignoring them). */ + size_t dir_len (char const *file) { size_t prefix_length = FILE_SYSTEM_PREFIX_LEN (file); size_t length; + /* Advance prefix_length beyond important leading slashes. */ + prefix_length += (prefix_length != 0 + ? (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE + && ISSLASH (file[prefix_length])) + : ISSLASH (file[0]) + ? (DOUBLE_SLASH_IS_DISTINCT_ROOT + && ISSLASH (file[1]) && ! ISSLASH (file[2]) + ? 2 : 1) + : 0); + /* Strip the basename and any redundant slashes before it. */ - for (length = base_name (file) - file; prefix_length < length; length--) + for (length = last_component (file) - file; + prefix_length < length; length--) if (! ISSLASH (file[length - 1])) - return length; - - /* But don't strip the only slash from "/". */ - return prefix_length + ISSLASH (file[prefix_length]); + break; + return length; } -/* Return the leading directories part of FILE, - allocated with xmalloc. - Works properly even if there are trailing slashes - (by effectively ignoring them). */ + +/* In general, we can't use the builtin `dirname' function if available, + since it has different meanings in different environments. + In some environments the builtin `dirname' modifies its argument. + + Return the leading directories part of FILE, allocated with xmalloc. + Works properly even if there are trailing slashes (by effectively + ignoring them). Unlike POSIX dirname(), FILE cannot be NULL. + + If lstat (FILE) would succeed, then { chdir (dir_name (FILE)); + lstat (base_name (FILE)); } will access the same file. Likewise, + if the sequence { chdir (dir_name (FILE)); + rename (base_name (FILE), "foo"); } succeeds, you have renamed FILE + to "foo" in the same directory FILE was in. */ char * dir_name (char const *file) { size_t length = dir_len (file); - bool append_dot = (length == FILE_SYSTEM_PREFIX_LEN (file)); + bool append_dot = (length == 0 + || (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE + && length == FILE_SYSTEM_PREFIX_LEN (file) + && file[2] != '\0' && ! ISSLASH (file[2]))); char *dir = xmalloc (length + append_dot + 1); memcpy (dir, file, length); if (append_dot) dir[length++] = '.'; - dir[length] = 0; + dir[length] = '\0'; return dir; } - -#ifdef TEST_DIRNAME -/* - -Run the test like this (expect no output): - gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall \ - basename.c dirname.c xmalloc.c error.c - sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out - -If it's been built on a DOS or Windows platforms, run another test like -this (again, expect no output): - sed -n '/^BEGIN-DOS-DATA$/,/^END-DOS-DATA$/p' dirname.c|grep -v DATA|./a.out - -BEGIN-DATA -foo//// . -bar/foo//// bar -foo/ . -/ / -. . -a . -END-DATA - -BEGIN-DOS-DATA -c:///// c:/ -c:/ c:/ -c:/. c:/ -c:foo c:. -c:foo/bar c:foo -END-DOS-DATA - -*/ - -# define MAX_BUFF_LEN 1024 -# include <stdio.h> - -char *program_name; - -int -main (int argc, char *argv[]) -{ - char buff[MAX_BUFF_LEN + 1]; - - program_name = argv[0]; - - buff[MAX_BUFF_LEN] = 0; - while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) - { - char file[MAX_BUFF_LEN]; - char expected_result[MAX_BUFF_LEN]; - char const *result; - sscanf (buff, "%s %s", file, expected_result); - result = dir_name (file); - if (strcmp (result, expected_result)) - printf ("%s: got %s, expected %s\n", file, result, expected_result); - } - return 0; -} -#endif Index: lib/dirname.h =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/dirname.h,v retrieving revision 1.11 diff -u -p -r1.11 dirname.h --- lib/dirname.h 2 Jun 2005 20:41:05 -0000 1.11 +++ lib/dirname.h 22 Nov 2005 13:51:24 -0000 @@ -31,16 +31,39 @@ # endif # ifndef FILE_SYSTEM_PREFIX_LEN -# define FILE_SYSTEM_PREFIX_LEN(File_name) 0 +# if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX + /* This internal macro assumes ASCII, but all hosts that support drive + letters use ASCII. */ +# define _IS_DRIVE_LETTER(c) (((unsigned int) (c) | ('a' - 'A')) - 'a' \ + <= 'z' - 'a') +# define FILE_SYSTEM_PREFIX_LEN(Filename) \ + (_IS_DRIVE_LETTER ((Filename)[0]) && (Filename)[1] == ':' ? 2 : 0) +# else +# define FILE_SYSTEM_PREFIX_LEN(Filename) 0 +# endif # endif -# define IS_ABSOLUTE_FILE_NAME(F) ISSLASH ((F)[FILE_SYSTEM_PREFIX_LEN (F)]) +# ifndef FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE +# define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 0 +# endif + +# ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT +# define DOUBLE_SLASH_IS_DISTINCT_ROOT 1 +# endif + +# if FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE +# define IS_ABSOLUTE_FILE_NAME(F) ISSLASH ((F)[FILE_SYSTEM_PREFIX_LEN (F)]) +# else +# define IS_ABSOLUTE_FILE_NAME(F) \ + (ISSLASH ((F)[0]) || 0 < FILE_SYSTEM_PREFIX_LEN (F)) +# endif # define IS_RELATIVE_FILE_NAME(F) (! IS_ABSOLUTE_FILE_NAME (F)) char *base_name (char const *file); char *dir_name (char const *file); size_t base_len (char const *file); size_t dir_len (char const *file); +char *last_component (char const *file); bool strip_trailing_slashes (char *file); Index: lib/filenamecat.c =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/filenamecat.c,v retrieving revision 1.2 diff -u -p -r1.2 filenamecat.c --- lib/filenamecat.c 19 Sep 2005 17:28:14 -0000 1.2 +++ lib/filenamecat.c 22 Nov 2005 13:51:24 -0000 @@ -64,7 +64,7 @@ longest_relative_suffix (char const *f) char * file_name_concat (char const *dir, char const *abase, char **base_in_result) { - char const *dirbase = base_name (dir); + char const *dirbase = last_component (dir); size_t dirbaselen = base_len (dirbase); size_t dirlen = dirbase - dir + dirbaselen; size_t needs_separator = (dirbaselen && ! ISSLASH (dirbase[dirbaselen - 1])); Index: lib/same.c =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/same.c,v retrieving revision 1.19 diff -u -p -r1.19 same.c --- lib/same.c 23 Sep 2005 04:15:13 -0000 1.19 +++ lib/same.c 22 Nov 2005 13:51:24 -0000 @@ -59,8 +59,8 @@ bool same_name (const char *source, const char *dest) { /* Compare the basenames. */ - char const *source_basename = base_name (source); - char const *dest_basename = base_name (dest); + char const *source_basename = last_component (source); + char const *dest_basename = last_component (dest); size_t source_baselen = base_len (source_basename); size_t dest_baselen = base_len (dest_basename); bool identical_basenames = Index: lib/stripslash.c =================================================================== RCS file: /cvsroot/gnulib/gnulib/lib/stripslash.c,v retrieving revision 1.15 diff -u -p -r1.15 stripslash.c --- lib/stripslash.c 19 Sep 2005 17:28:15 -0000 1.15 +++ lib/stripslash.c 22 Nov 2005 13:51:24 -0000 @@ -22,19 +22,26 @@ #include "dirname.h" -/* Remove trailing slashes from FILE. - Return true if a trailing slash was removed. - This is useful when using file name completion from a shell that - adds a "/" after directory names (such as tcsh and bash), because - the Unix rename and rmdir system calls return an "Invalid argument" error - when given a file that ends in "/" (except for the root directory). */ +/* Remove trailing slashes from FILE. Return true if a trailing slash + was removed. This is useful when using file name completion from a + shell that adds a "/" after directory names (such as tcsh and + bash), because on symlinks to directories, several system calls + have different semantics according to whether a trailing slash is + present. */ bool strip_trailing_slashes (char *file) { - char *base = base_name (file); - char *base_lim = base + base_len (base); - bool had_slash = (*base_lim != '\0'); + char *base = last_component (file); + char *base_lim; + bool had_slash; + + /* last_component returns "" for file system roots, but we need to turn + `///' into `/'. */ + if (! *base) + base = file; + base_lim = base + base_len (base); + had_slash = (*base_lim != '\0'); *base_lim = '\0'; return had_slash; } Index: m4/ChangeLog =================================================================== RCS file: /cvsroot/gnulib/gnulib/m4/ChangeLog,v retrieving revision 1.758 diff -u -p -r1.758 ChangeLog Index: m4/dirname.m4 =================================================================== RCS file: /cvsroot/gnulib/gnulib/m4/dirname.m4,v retrieving revision 1.8 diff -u -p -r1.8 dirname.m4 --- m4/dirname.m4 21 Mar 2005 22:06:27 -0000 1.8 +++ m4/dirname.m4 22 Nov 2005 13:51:25 -0000 @@ -1,4 +1,4 @@ -# dirname.m4 serial 5 +# dirname.m4 serial 6 dnl Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -11,6 +11,28 @@ AC_DEFUN([gl_DIRNAME], dnl Prerequisites of lib/dirname.h. AC_REQUIRE([gl_AC_DOS]) + AC_CACHE_CHECK([whether // is distinct from /], [ac_cv_double_slash_root], + [ if test x"$cross_compiling" = xyes ; then + # When cross-compiling, there is no way to tell whether // is special + # short of a list of hosts; so always treat it as special. + ac_cv_double_slash_root=unknown + else + set x `ls -di / //` + if test $[2] = $[4]; then + ac_cv_double_slash_root=no + else + ac_cv_double_slash_root=yes + fi + fi]) + if test x"$ac_cv_double_slash_root" = xno; then + ac_double_slash_root=0 + else + ac_double_slash_root=1 + fi + + AC_DEFINE_UNQUOTED([DOUBLE_SLASH_IS_DISTINCT_ROOT], + $ac_double_slash_root, + [Define to 1 if // is a file system root distinct from /.]) dnl No prerequisites of lib/basename.c, lib/dirname.c, lib/stripslash.c. ]) Index: m4/dos.m4 =================================================================== RCS file: /cvsroot/gnulib/gnulib/m4/dos.m4,v retrieving revision 1.12 diff -u -p -r1.12 dos.m4 --- m4/dos.m4 23 Jan 2005 08:06:57 -0000 1.12 +++ m4/dos.m4 22 Nov 2005 13:51:25 -0000 @@ -1,9 +1,9 @@ -#serial 9 +#serial 10 # Define some macros required for proper operation of code in lib/*.c # on MSDOS/Windows systems. -# Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc. +# Copyright (C) 2000, 2001, 2004, 2005 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -14,30 +14,38 @@ AC_DEFUN([gl_AC_DOS], [ AC_CACHE_CHECK([whether system is Windows or MSDOS], [ac_cv_win_or_dos], [ - AC_TRY_COMPILE([], - [#if !defined _WIN32 && !defined __WIN32__ && !defined __MSDOS__ && !defined __CYGWIN__ + AC_TRY_COMPILE([], + [#if !defined _WIN32 && !defined __WIN32__ && !defined __MSDOS__ && !defined __CYGWIN__ neither MSDOS nor Windows #endif], - [ac_cv_win_or_dos=yes], - [ac_cv_win_or_dos=no]) + [ac_cv_win_or_dos=yes], + [ac_cv_win_or_dos=no]) ]) if test x"$ac_cv_win_or_dos" = xyes; then ac_fs_accepts_drive_letter_prefix=1 ac_fs_backslash_is_file_name_separator=1 + AC_CACHE_CHECK([whether drive letter can start relative path], + [ac_cv_drive_letter_can_be_relative], + [ + AC_TRY_COMPILE([], + [#if defined __CYGWIN__ +drive letters are always absolute +#endif], + [ac_cv_drive_letter_can_be_relative=yes], + [ac_cv_drive_letter_can_be_relative=no]) + ]) + if test x"$ac_cv_drive_letter_can_be_relative" = xyes; then + ac_fs_drive_letter_can_be_relative=1 + else + ac_fs_drive_letter_can_be_relative=0 + fi else ac_fs_accepts_drive_letter_prefix=0 ac_fs_backslash_is_file_name_separator=0 + ac_fs_drive_letter_can_be_relative=0 fi - AH_VERBATIM(FILE_SYSTEM_PREFIX_LEN, - [#if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX -# define FILE_SYSTEM_PREFIX_LEN(Filename) \ - ((Filename)[0] && (Filename)[1] == ':' ? 2 : 0) -#else -# define FILE_SYSTEM_PREFIX_LEN(Filename) 0 -#endif]) - AC_DEFINE_UNQUOTED([FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX], $ac_fs_accepts_drive_letter_prefix, [Define on systems for which file names may have a so-called @@ -55,4 +63,9 @@ neither MSDOS nor Windows $ac_fs_backslash_is_file_name_separator, [Define if the backslash character may also serve as a file name component separator.]) + + AC_DEFINE_UNQUOTED([FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE], + $ac_fs_drive_letter_can_be_relative, + [Define if a drive letter prefix denotes a relative path if it is + not followed by a file name component separator.]) ]) Index: modules/dirname =================================================================== RCS file: /cvsroot/gnulib/gnulib/modules/dirname,v retrieving revision 1.6 diff -u -p -r1.6 dirname --- modules/dirname 6 May 2005 17:22:45 -0000 1.6 +++ modules/dirname 22 Nov 2005 13:51:25 -0000 @@ -6,12 +6,13 @@ lib/dirname.h lib/dirname.c lib/basename.c lib/stripslash.c -m4/dos.m4 m4/dirname.m4 +m4/dos.m4 Depends-on: -xalloc stdbool +xalloc +xstrndup configure.ac: gl_DIRNAME Index: modules/dirname-tests =================================================================== RCS file: modules/dirname-tests diff -N modules/dirname-tests --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/dirname-tests 22 Nov 2005 13:51:25 -0000 @@ -0,0 +1,12 @@ +Files: +tests/test-dirname.c + +Depends-on: +strdup + +configure.ac: + +Makefile.am: +TESTS += test-dirname +noinst_PROGRAMS += test-dirname +test_dirname_SOURCES = test-dirname.c Index: tests/test-dirname.c =================================================================== RCS file: tests/test-dirname.c diff -N tests/test-dirname.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tests/test-dirname.c 22 Nov 2005 13:51:25 -0000 @@ -0,0 +1,196 @@ +/* Test the gnulib dirname module. + Copyright (C) 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "dirname.h" + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "strdup.h" + +const char *program_name = "test-dirname"; + +struct test { + const char *name; /* Name under test. */ + const char *dir; /* dir_name (name). */ + const char *last; /* last_component (name). */ + const char *base; /* base_name (name). */ + const char *stripped; /* name after strip_trailing_slashes (name). */ + bool modified; /* result of strip_trailing_slashes (name). */ + bool absolute; /* IS_ABSOLUTE_FILE_NAME (name). */ +}; + +static struct test tests[] = { + {"d/f", "d", "f", "f", "d/f", false, false}, + {"/d/f", "/d", "f", "f", "/d/f", false, true}, + {"d/f/", "d", "f/", "f/", "d/f", true, false}, + {"d/f//", "d", "f//", "f/", "d/f", true, false}, + {"f", ".", "f", "f", "f", false, false}, + {"/", "/", "", "/", "/", false, true}, +#if DOUBLE_SLASH_IS_DISTINCT_ROOT + {"//", "//", "", "//", "//", false, true}, + {"//d", "//", "d", "d", "//d", false, true}, +#else + {"//", "/", "", "/", "/", true, true}, + {"//d", "/", "d", "d", "//d", false, true}, +#endif + {"///", "/", "", "/", "/", true, true}, + {"///a///", "/", "a///", "a/", "///a", true, true}, + /* POSIX requires dirname("") and basename("") to both return ".", + but dir_name and base_name are defined differently. */ + {"", ".", "", "", "", false, false}, + {".", ".", ".", ".", ".", false, false}, + {"..", ".", "..", "..", "..", false, false}, +#if FILE_SYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR + {"a\\", ".", "a\\", "a\\", "a", true, false}, + {"a\\b", "a", "b", "b", "a\\b", false, false}, + {"\\", "\\", "", "\\", "\\", false, true}, + {"\\/\\", "\\", "", "\\", "\\", true, true}, + {"\\\\/", "\\", "", "\\", "\\", true, true}, + {"\\//", "\\", "", "\\", "\\", true, true}, + {"//\\", "/", "", "/", "/", true, true}, +#else + {"a\\", ".", "a\\", "a\\", "a\\", false, false}, + {"a\\b", ".", "a\\b", "a\\b", "a\\b", false, false}, + {"\\", ".", "\\", "\\", "\\", false, false}, + {"\\/\\", "\\", "\\", "\\", "\\/\\",false, false}, + {"\\\\/", ".", "\\\\/","\\\\/","\\\\", true, false}, + {"\\//", ".", "\\//", "\\/", "\\", true, false}, +# if DOUBLE_SLASH_IS_DISTINCT_ROOT + {"//\\", "//", "\\", "\\", "//\\", false, true}, +# else + {"//\\", "/", "\\", "\\", "//\\", false, true}, +# endif +#endif +#if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX +# if FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE + {"c:", "c:", "", "c:", "c:", false, false}, + {"c:/", "c:/", "", "c:/", "c:/", false, true}, + {"c://", "c:/", "", "c:/", "c:/", true, true}, + {"c:/d", "c:/", "d", "d", "c:/d", false, true}, + {"c://d", "c:/", "d", "d", "c://d",false, true}, + {"c:/d/", "c:/", "d/", "d/", "c:/d", true, true}, + {"c:/d/f", "c:/d", "f", "f", "c:/d/f",false, true}, + {"c:d", "c:.", "d", "d", "c:d", false, false}, + {"c:d/", "c:.", "d/", "d/", "c:d", true, false}, + {"c:d/f", "c:d", "f", "f", "c:d/f",false, false}, + {"a:b:c", "a:.", "b:c", "./b:c","a:b:c",false, false}, + {"a/b:c", "a", "b:c", "./b:c","a/b:c",false, false}, + {"a/b:c/", "a", "b:c/", "./b:c/","a/b:c",true, false}, +# else /* ! FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE */ + {"c:", "c:", "", "c:", "c:", false, true}, + {"c:/", "c:", "", "c:", "c:", true, true}, + {"c://", "c:", "", "c:", "c:", true, true}, + {"c:/d", "c:", "d", "d", "c:/d", false, true}, + {"c://d", "c:", "d", "d", "c://d",false, true}, + {"c:/d/", "c:", "d/", "d/", "c:/d", true, true}, + {"c:/d/f", "c:/d", "f", "f", "c:/d/f",false, true}, + {"c:d", "c:", "d", "d", "c:d", false, true}, + {"c:d/", "c:", "d/", "d/", "c:d", true, true}, + {"c:d/f", "c:d", "f", "f", "c:d/f",false, true}, + {"a:b:c", "a:", "b:c", "./b:c","a:b:c",false, true}, + {"a/b:c", "a", "b:c", "./b:c","a/b:c",false, false}, + {"a/b:c/", "a", "b:c/", "./b:c/","a/b:c",true, false}, +# endif +#else /* ! FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX */ + {"c:", ".", "c:", "c:", "c:", false, false}, + {"c:/", ".", "c:/", "c:/", "c:", true, false}, + {"c://", ".", "c://", "c:/", "c:", true, false}, + {"c:/d", "c:", "d", "d", "c:/d", false, false}, + {"c://d", "c:", "d", "d", "c://d",false, false}, + {"c:/d/", "c:", "d/", "d/", "c:/d", true, false}, + {"c:/d/f", "c:/d", "f", "f", "c:/d/f",false, false}, + {"c:d", ".", "c:d", "c:d", "c:d", false, false}, + {"c:d/", ".", "c:d/", "c:d/", "c:d", true, false}, + {"c:d/f", "c:d", "f", "f", "c:d/f",false, false}, + {"a:b:c", ".", "a:b:c","a:b:c","a:b:c",false, false}, + {"a/b:c", "a", "b:c", "b:c", "a/b:c",false, false}, + {"a/b:c/", "a", "b:c/", "b:c/", "a/b:c",true, false}, +#endif + {"1:", ".", "1:", "1:", "1:", false, false}, + {"1:/", ".", "1:/", "1:/", "1:", true, false}, + {"/:", "/", ":", ":", "/:", false, true}, + {"/:/", "/", ":/", ":/", "/:", true, true}, + /* End sentinel. */ + {NULL, NULL, NULL, NULL, NULL, false, false} +}; + +int +main () +{ + struct test *t; + bool ok = true; + + for (t = tests; t->name; t++) + { + char *dir = dir_name (t->name); + int dirlen = dir_len (t->name); + char *last = last_component (t->name); + char *base = base_name (t->name); + int baselen = base_len (base); + char *stripped = strdup (t->name); + bool modified = strip_trailing_slashes (stripped); + bool absolute = IS_ABSOLUTE_FILE_NAME (t->name); + if (! (strcmp (dir, t->dir) == 0 + && (dirlen == strlen (dir) + || (dirlen + 1 == strlen (dir) && dir[dirlen] == '.')))) + { + ok = false; + printf ("dir_name `%s': got `%s' len %d, expected `%s' len %d\n", + t->name, dir, dirlen, t->dir, strlen (t->dir)); + } + if (strcmp (last, t->last)) + { + ok = false; + printf ("last_component `%s': got `%s', expected `%s'\n", + t->name, last, t->last); + } + if (! (strcmp (base, t->base) == 0 + && (baselen == strlen (base) + || (baselen + 1 == strlen (base) + && ISSLASH (base[baselen]))))) + { + ok = false; + printf ("base_name `%s': got `%s' len %d, expected `%s' len %d\n", + t->name, base, baselen, t->base, strlen (t->base)); + } + if (strcmp (stripped, t->stripped) || modified != t->modified) + { + ok = false; + printf ("strip_trailing_slashes `%s': got %s %s, expected %s %s\n", + t->name, stripped, modified ? "changed" : "unchanged", + t->stripped, t->modified ? "changed" : "unchanged"); + } + if (t->absolute != absolute) + { + ok = false; + printf ("`%s': got %s, expected %s\n", t->name, + absolute ? "absolute" : "relative", + t->absolute ? "absolute" : "relative"); + } + free (dir); + free (base); + free (stripped); + } + return ok ? 0 : 1; +}
_______________________________________________ bug-gnulib mailing list bug-gnulib@gnu.org http://lists.gnu.org/mailman/listinfo/bug-gnulib