(root)/
tar-1.35/
gnu/
filenamecat-lgpl.c
       1  /* Concatenate two arbitrary file names.
       2  
       3     Copyright (C) 1996-2007, 2009-2023 Free Software Foundation, Inc.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by Jim Meyering.  */
      19  
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include "filenamecat.h"
      24  
      25  #include <stdlib.h>
      26  #include <string.h>
      27  
      28  #include "basename-lgpl.h"
      29  #include "filename.h"
      30  
      31  #if ! HAVE_MEMPCPY && ! defined mempcpy
      32  # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
      33  #endif
      34  
      35  /* Concatenate two file name components, DIR and BASE, in
      36     newly-allocated storage and return the result.
      37     The resulting file name F is such that the commands "ls F" and "(cd
      38     DIR; ls ./BASE)" refer to the same file.  If necessary, put
      39     a separator between DIR and BASE in the result.  Typically this
      40     separator is "/", but in rare cases it might be ".".
      41     In any case, if BASE_IN_RESULT is non-NULL, set
      42     *BASE_IN_RESULT to point to the copy of BASE at the end of the
      43     returned concatenation.
      44  
      45     If malloc fails, return NULL with errno set.  */
      46  
      47  char *
      48  mfile_name_concat (char const *dir, char const *base, char **base_in_result)
      49  {
      50    char const *dirbase = last_component (dir);
      51    size_t dirbaselen = base_len (dirbase);
      52    size_t dirlen = dirbase - dir + dirbaselen;
      53    size_t baselen = strlen (base);
      54    char sep = '\0';
      55    if (dirbaselen)
      56      {
      57        /* DIR is not a file system root, so separate with / if needed.  */
      58        if (! ISSLASH (dir[dirlen - 1]) && ! ISSLASH (*base))
      59          sep = '/';
      60      }
      61    else if (ISSLASH (*base))
      62      {
      63        /* DIR is a file system root and BASE begins with a slash, so
      64           separate with ".".  For example, if DIR is "/" and BASE is
      65           "/foo" then return "/./foo", as "//foo" would be wrong on
      66           some POSIX systems.  A fancier algorithm could omit "." in
      67           some cases but is not worth the trouble.  */
      68        sep = '.';
      69      }
      70  
      71    char *p_concat = malloc (dirlen + (sep != '\0')  + baselen + 1);
      72    if (p_concat == NULL)
      73      return NULL;
      74  
      75    {
      76      char *p;
      77  
      78      p = mempcpy (p_concat, dir, dirlen);
      79      *p = sep;
      80      p += sep != '\0';
      81  
      82      if (base_in_result)
      83        *base_in_result = p;
      84  
      85      p = mempcpy (p, base, baselen);
      86      *p = '\0';
      87    }
      88  
      89    return p_concat;
      90  }