18dffb485Schristos /* dirname.c -- return all but the last element in a file name
28dffb485Schristos
3*4b169a6bSchristos Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2022 Free Software
48dffb485Schristos Foundation, Inc.
58dffb485Schristos
6*4b169a6bSchristos This file is free software: you can redistribute it and/or modify
7*4b169a6bSchristos it under the terms of the GNU Lesser General Public License as
8*4b169a6bSchristos published by the Free Software Foundation; either version 2.1 of the
9*4b169a6bSchristos License, or (at your option) any later version.
108dffb485Schristos
11*4b169a6bSchristos This file is distributed in the hope that it will be useful,
128dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
138dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*4b169a6bSchristos GNU Lesser General Public License for more details.
158dffb485Schristos
16*4b169a6bSchristos You should have received a copy of the GNU Lesser General Public License
178dffb485Schristos along with this program. If not, see <https://www.gnu.org/licenses/>. */
188dffb485Schristos
198dffb485Schristos #include <config.h>
208dffb485Schristos
218dffb485Schristos #include "dirname.h"
228dffb485Schristos
238dffb485Schristos #include <stdlib.h>
248dffb485Schristos #include <string.h>
258dffb485Schristos
268dffb485Schristos /* Return the length of the prefix of FILE that will be used by
278dffb485Schristos dir_name. If FILE is in the working directory, this returns zero
288dffb485Schristos even though 'dir_name (FILE)' will return ".". Works properly even
298dffb485Schristos if there are trailing slashes (by effectively ignoring them). */
308dffb485Schristos
318dffb485Schristos size_t
dir_len(char const * file)328dffb485Schristos dir_len (char const *file)
338dffb485Schristos {
348dffb485Schristos size_t prefix_length = FILE_SYSTEM_PREFIX_LEN (file);
358dffb485Schristos size_t length;
368dffb485Schristos
378dffb485Schristos /* Advance prefix_length beyond important leading slashes. */
388dffb485Schristos prefix_length += (prefix_length != 0
398dffb485Schristos ? (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
408dffb485Schristos && ISSLASH (file[prefix_length]))
418dffb485Schristos : (ISSLASH (file[0])
428dffb485Schristos ? ((DOUBLE_SLASH_IS_DISTINCT_ROOT
438dffb485Schristos && ISSLASH (file[1]) && ! ISSLASH (file[2])
448dffb485Schristos ? 2 : 1))
458dffb485Schristos : 0));
468dffb485Schristos
478dffb485Schristos /* Strip the basename and any redundant slashes before it. */
488dffb485Schristos for (length = last_component (file) - file;
498dffb485Schristos prefix_length < length; length--)
508dffb485Schristos if (! ISSLASH (file[length - 1]))
518dffb485Schristos break;
528dffb485Schristos return length;
538dffb485Schristos }
548dffb485Schristos
558dffb485Schristos
568dffb485Schristos /* In general, we can't use the builtin 'dirname' function if available,
578dffb485Schristos since it has different meanings in different environments.
588dffb485Schristos In some environments the builtin 'dirname' modifies its argument.
598dffb485Schristos
608dffb485Schristos Return the leading directories part of FILE, allocated with malloc.
618dffb485Schristos Works properly even if there are trailing slashes (by effectively
628dffb485Schristos ignoring them). Return NULL on failure.
638dffb485Schristos
648dffb485Schristos If lstat (FILE) would succeed, then { chdir (dir_name (FILE));
658dffb485Schristos lstat (base_name (FILE)); } will access the same file. Likewise,
668dffb485Schristos if the sequence { chdir (dir_name (FILE));
678dffb485Schristos rename (base_name (FILE), "foo"); } succeeds, you have renamed FILE
688dffb485Schristos to "foo" in the same directory FILE was in. */
698dffb485Schristos
708dffb485Schristos char *
mdir_name(char const * file)718dffb485Schristos mdir_name (char const *file)
728dffb485Schristos {
738dffb485Schristos size_t length = dir_len (file);
748dffb485Schristos bool append_dot = (length == 0
758dffb485Schristos || (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
768dffb485Schristos && length == FILE_SYSTEM_PREFIX_LEN (file)
778dffb485Schristos && file[2] != '\0' && ! ISSLASH (file[2])));
788dffb485Schristos char *dir = malloc (length + append_dot + 1);
798dffb485Schristos if (!dir)
808dffb485Schristos return NULL;
818dffb485Schristos memcpy (dir, file, length);
828dffb485Schristos if (append_dot)
838dffb485Schristos dir[length++] = '.';
848dffb485Schristos dir[length] = '\0';
858dffb485Schristos return dir;
868dffb485Schristos }
87