xref: /dflybsd-src/contrib/grep/lib/dirname-lgpl.c (revision 91b9ed38d3db6a8a8ac5b66da1d43e6e331e259a)
195b7b453SJohn Marino /* dirname.c -- return all but the last element in a file name
295b7b453SJohn Marino 
3*09d4459fSDaniel Fojt    Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2020 Free Software
495b7b453SJohn Marino    Foundation, Inc.
595b7b453SJohn Marino 
695b7b453SJohn Marino    This program is free software: you can redistribute it and/or modify
795b7b453SJohn Marino    it under the terms of the GNU General Public License as published by
895b7b453SJohn Marino    the Free Software Foundation; either version 3 of the License, or
995b7b453SJohn Marino    (at your option) any later version.
1095b7b453SJohn Marino 
1195b7b453SJohn Marino    This program is distributed in the hope that it will be useful,
1295b7b453SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
1395b7b453SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1495b7b453SJohn Marino    GNU General Public License for more details.
1595b7b453SJohn Marino 
1695b7b453SJohn Marino    You should have received a copy of the GNU General Public License
17*09d4459fSDaniel Fojt    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
1895b7b453SJohn Marino 
1995b7b453SJohn Marino #include <config.h>
2095b7b453SJohn Marino 
2195b7b453SJohn Marino #include "dirname.h"
2295b7b453SJohn Marino 
2395b7b453SJohn Marino #include <stdlib.h>
2495b7b453SJohn Marino #include <string.h>
2595b7b453SJohn Marino 
2695b7b453SJohn Marino /* Return the length of the prefix of FILE that will be used by
2795b7b453SJohn Marino    dir_name.  If FILE is in the working directory, this returns zero
28cf28ed85SJohn Marino    even though 'dir_name (FILE)' will return ".".  Works properly even
2995b7b453SJohn Marino    if there are trailing slashes (by effectively ignoring them).  */
3095b7b453SJohn Marino 
3195b7b453SJohn Marino size_t
dir_len(char const * file)3295b7b453SJohn Marino dir_len (char const *file)
3395b7b453SJohn Marino {
3495b7b453SJohn Marino   size_t prefix_length = FILE_SYSTEM_PREFIX_LEN (file);
3595b7b453SJohn Marino   size_t length;
3695b7b453SJohn Marino 
3795b7b453SJohn Marino   /* Advance prefix_length beyond important leading slashes.  */
3895b7b453SJohn Marino   prefix_length += (prefix_length != 0
3995b7b453SJohn Marino                     ? (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
4095b7b453SJohn Marino                        && ISSLASH (file[prefix_length]))
4195b7b453SJohn Marino                     : (ISSLASH (file[0])
4295b7b453SJohn Marino                        ? ((DOUBLE_SLASH_IS_DISTINCT_ROOT
4395b7b453SJohn Marino                            && ISSLASH (file[1]) && ! ISSLASH (file[2])
4495b7b453SJohn Marino                            ? 2 : 1))
4595b7b453SJohn Marino                        : 0));
4695b7b453SJohn Marino 
4795b7b453SJohn Marino   /* Strip the basename and any redundant slashes before it.  */
4895b7b453SJohn Marino   for (length = last_component (file) - file;
4995b7b453SJohn Marino        prefix_length < length; length--)
5095b7b453SJohn Marino     if (! ISSLASH (file[length - 1]))
5195b7b453SJohn Marino       break;
5295b7b453SJohn Marino   return length;
5395b7b453SJohn Marino }
5495b7b453SJohn Marino 
5595b7b453SJohn Marino 
56cf28ed85SJohn Marino /* In general, we can't use the builtin 'dirname' function if available,
5795b7b453SJohn Marino    since it has different meanings in different environments.
58cf28ed85SJohn Marino    In some environments the builtin 'dirname' modifies its argument.
5995b7b453SJohn Marino 
6095b7b453SJohn Marino    Return the leading directories part of FILE, allocated with malloc.
6195b7b453SJohn Marino    Works properly even if there are trailing slashes (by effectively
6295b7b453SJohn Marino    ignoring them).  Return NULL on failure.
6395b7b453SJohn Marino 
6495b7b453SJohn Marino    If lstat (FILE) would succeed, then { chdir (dir_name (FILE));
6595b7b453SJohn Marino    lstat (base_name (FILE)); } will access the same file.  Likewise,
6695b7b453SJohn Marino    if the sequence { chdir (dir_name (FILE));
6795b7b453SJohn Marino    rename (base_name (FILE), "foo"); } succeeds, you have renamed FILE
6895b7b453SJohn Marino    to "foo" in the same directory FILE was in.  */
6995b7b453SJohn Marino 
7095b7b453SJohn Marino char *
mdir_name(char const * file)7195b7b453SJohn Marino mdir_name (char const *file)
7295b7b453SJohn Marino {
7395b7b453SJohn Marino   size_t length = dir_len (file);
7495b7b453SJohn Marino   bool append_dot = (length == 0
7595b7b453SJohn Marino                      || (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
7695b7b453SJohn Marino                          && length == FILE_SYSTEM_PREFIX_LEN (file)
7795b7b453SJohn Marino                          && file[2] != '\0' && ! ISSLASH (file[2])));
7895b7b453SJohn Marino   char *dir = malloc (length + append_dot + 1);
7995b7b453SJohn Marino   if (!dir)
8095b7b453SJohn Marino     return NULL;
8195b7b453SJohn Marino   memcpy (dir, file, length);
8295b7b453SJohn Marino   if (append_dot)
8395b7b453SJohn Marino     dir[length++] = '.';
8495b7b453SJohn Marino   dir[length] = '\0';
8595b7b453SJohn Marino   return dir;
8695b7b453SJohn Marino }
87