1 /* basename.c -- return the last element in a file name
   2 
   3    Copyright (C) 1990, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 Free
   4    Software Foundation, Inc.
   5 
   6    This program is free software; you can redistribute it and/or modify
   7    it under the terms of the GNU General Public License as published by
   8    the Free Software Foundation; either version 2, or (at your option)
   9    any later version.
  10 
  11    This program is distributed in the hope that it will be useful,
  12    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14    GNU General Public License for more details.
  15 
  16    You should have received a copy of the GNU General Public License
  17    along with this program; if not, write to the Free Software Foundation,
  18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
  19 
  20 #include <config.h>
  21 
  22 #include "dirname.h"
  23 
  24 #include <string.h>
  25 #include "xalloc.h"
  26 #include "xstrndup.h"
  27 
  28 /* Return the address of the last file name component of NAME.  If
  29    NAME has no relative file name components because it is a file
  30    system root, return the empty string.  */
  31 
  32 char *
  33 last_component (char const *name)
  34 {
  35   char const *base = name + FILE_SYSTEM_PREFIX_LEN (name);
  36   char const *p;
  37   bool saw_slash = false;
  38 
  39   while (ISSLASH (*base))
  40     base++;
  41 
  42   for (p = base; *p; p++)
  43     {
  44       if (ISSLASH (*p))
  45         saw_slash = true;
  46       else if (saw_slash)
  47         {
  48           base = p;
  49           saw_slash = false;
  50         }
  51     }
  52 
  53   return (char *) base;
  54 }
  55 
  56 
  57 /* In general, we can't use the builtin `basename' function if available,
  58    since it has different meanings in different environments.
  59    In some environments the builtin `basename' modifies its argument.
  60 
  61    Return the last file name component of NAME, allocated with
  62    xmalloc.  On systems with drive letters, a leading "./"
  63    distinguishes relative names that would otherwise look like a drive
  64    letter.  Unlike POSIX basename(), NAME cannot be NULL,
  65    base_name("") returns "", and the first trailing slash is not
  66    stripped.
  67 
  68    If lstat (NAME) would succeed, then { chdir (dir_name (NAME));
  69    lstat (base_name (NAME)); } will access the same file.  Likewise,
  70    if the sequence { chdir (dir_name (NAME));
  71    rename (base_name (NAME), "foo"); } succeeds, you have renamed NAME
  72    to "foo" in the same directory NAME was in.  */
  73 
  74 char *
  75 base_name (char const *name)
  76 {
  77   char const *base = last_component (name);
  78   size_t length;
  79 
  80   /* If there is no last component, then name is a file system root or the
  81      empty string.  */
  82   if (! *base)
  83     return xstrndup (name, base_len (name));
  84 
  85   /* Collapse a sequence of trailing slashes into one.  */
  86   length = base_len (base);
  87   if (ISSLASH (base[length]))
  88     length++;
  89 
  90   /* On systems with drive letters, `a/b:c' must return `./b:c' rather
  91      than `b:c' to avoid confusion with a drive letter.  On systems
  92      with pure POSIX semantics, this is not an issue.  */
  93   if (FILE_SYSTEM_PREFIX_LEN (base))
  94     {
  95       char *p = xmalloc (length + 3);
  96       p[0] = '.';
  97       p[1] = '/';
  98       memcpy (p + 2, base, length);
  99       p[length + 2] = '\0';
 100       return p;
 101     }
 102 
 103   /* Finally, copy the basename.  */
 104   return xstrndup (base, length);
 105 }
 106 
 107 /* Return the length of the basename NAME.  Typically NAME is the
 108    value returned by base_name or last_component.  Act like strlen
 109    (NAME), except omit all trailing slashes.  */
 110 
 111 size_t
 112 base_len (char const *name)
 113 {
 114   size_t len;
 115   size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
 116 
 117   for (len = strlen (name);  1 < len && ISSLASH (name[len - 1]);  len--)
 118     continue;
 119 
 120   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && len == 1
 121       && ISSLASH (name[0]) && ISSLASH (name[1]) && ! name[2])
 122     return 2;
 123 
 124   if (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE && prefix_len
 125       && len == prefix_len && ISSLASH (name[prefix_len]))
 126     return prefix_len + 1;
 127 
 128   return len;
 129 }