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