Blame gnulib/lib/dirname-lgpl.c

Packit 06dd63
/* dirname.c -- return all but the last element in a file name
Packit 06dd63
Packit 06dd63
   Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2019 Free Software
Packit 06dd63
   Foundation, Inc.
Packit 06dd63
Packit 06dd63
   This program is free software: you can redistribute it and/or modify
Packit 06dd63
   it under the terms of the GNU Lesser General Public License as published by
Packit 06dd63
   the Free Software Foundation; either version 2.1 of the License, or
Packit 06dd63
   (at your option) any later version.
Packit 06dd63
Packit 06dd63
   This program is distributed in the hope that it will be useful,
Packit 06dd63
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 06dd63
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 06dd63
   GNU Lesser General Public License for more details.
Packit 06dd63
Packit 06dd63
   You should have received a copy of the GNU Lesser General Public License
Packit 06dd63
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 06dd63
Packit 06dd63
#include <config.h>
Packit 06dd63
Packit 06dd63
#include "dirname.h"
Packit 06dd63
Packit 06dd63
#include <stdlib.h>
Packit 06dd63
#include <string.h>
Packit 06dd63
Packit 06dd63
/* Return the length of the prefix of FILE that will be used by
Packit 06dd63
   dir_name.  If FILE is in the working directory, this returns zero
Packit 06dd63
   even though 'dir_name (FILE)' will return ".".  Works properly even
Packit 06dd63
   if there are trailing slashes (by effectively ignoring them).  */
Packit 06dd63
Packit 06dd63
size_t
Packit 06dd63
dir_len (char const *file)
Packit 06dd63
{
Packit 06dd63
  size_t prefix_length = FILE_SYSTEM_PREFIX_LEN (file);
Packit 06dd63
  size_t length;
Packit 06dd63
Packit 06dd63
  /* Advance prefix_length beyond important leading slashes.  */
Packit 06dd63
  prefix_length += (prefix_length != 0
Packit 06dd63
                    ? (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
Packit 06dd63
                       && ISSLASH (file[prefix_length]))
Packit 06dd63
                    : (ISSLASH (file[0])
Packit 06dd63
                       ? ((DOUBLE_SLASH_IS_DISTINCT_ROOT
Packit 06dd63
                           && ISSLASH (file[1]) && ! ISSLASH (file[2])
Packit 06dd63
                           ? 2 : 1))
Packit 06dd63
                       : 0));
Packit 06dd63
Packit 06dd63
  /* Strip the basename and any redundant slashes before it.  */
Packit 06dd63
  for (length = last_component (file) - file;
Packit 06dd63
       prefix_length < length; length--)
Packit 06dd63
    if (! ISSLASH (file[length - 1]))
Packit 06dd63
      break;
Packit 06dd63
  return length;
Packit 06dd63
}
Packit 06dd63
Packit 06dd63
Packit 06dd63
/* In general, we can't use the builtin 'dirname' function if available,
Packit 06dd63
   since it has different meanings in different environments.
Packit 06dd63
   In some environments the builtin 'dirname' modifies its argument.
Packit 06dd63
Packit 06dd63
   Return the leading directories part of FILE, allocated with malloc.
Packit 06dd63
   Works properly even if there are trailing slashes (by effectively
Packit 06dd63
   ignoring them).  Return NULL on failure.
Packit 06dd63
Packit 06dd63
   If lstat (FILE) would succeed, then { chdir (dir_name (FILE));
Packit 06dd63
   lstat (base_name (FILE)); } will access the same file.  Likewise,
Packit 06dd63
   if the sequence { chdir (dir_name (FILE));
Packit 06dd63
   rename (base_name (FILE), "foo"); } succeeds, you have renamed FILE
Packit 06dd63
   to "foo" in the same directory FILE was in.  */
Packit 06dd63
Packit 06dd63
char *
Packit 06dd63
mdir_name (char const *file)
Packit 06dd63
{
Packit 06dd63
  size_t length = dir_len (file);
Packit 06dd63
  bool append_dot = (length == 0
Packit 06dd63
                     || (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
Packit 06dd63
                         && length == FILE_SYSTEM_PREFIX_LEN (file)
Packit 06dd63
                         && file[2] != '\0' && ! ISSLASH (file[2])));
Packit 06dd63
  char *dir = malloc (length + append_dot + 1);
Packit 06dd63
  if (!dir)
Packit 06dd63
    return NULL;
Packit 06dd63
  memcpy (dir, file, length);
Packit 06dd63
  if (append_dot)
Packit 06dd63
    dir[length++] = '.';
Packit 06dd63
  dir[length] = '\0';
Packit 06dd63
  return dir;
Packit 06dd63
}