1a7c91847Schristos /* Concatenate two arbitrary file names.
2a7c91847Schristos
3a7c91847Schristos Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free
4a7c91847Schristos Software Foundation, Inc.
5a7c91847Schristos
6a7c91847Schristos This program is free software; you can redistribute it and/or modify
7a7c91847Schristos it under the terms of the GNU General Public License as published by
8a7c91847Schristos the Free Software Foundation; either version 2, or (at your option)
9a7c91847Schristos any later version.
10a7c91847Schristos
11a7c91847Schristos This program is distributed in the hope that it will be useful,
12a7c91847Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
13a7c91847Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14a7c91847Schristos GNU General Public License for more details.
15a7c91847Schristos
16a7c91847Schristos You should have received a copy of the GNU General Public License
17a7c91847Schristos along with this program; if not, write to the Free Software Foundation,
18a7c91847Schristos Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19*5a6c14c8Schristos #include <sys/cdefs.h>
20*5a6c14c8Schristos __RCSID("$NetBSD: filenamecat.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
21*5a6c14c8Schristos
22a7c91847Schristos
23a7c91847Schristos /* Written by Jim Meyering. */
24a7c91847Schristos
25a7c91847Schristos #ifdef HAVE_CONFIG_H
26a7c91847Schristos # include <config.h>
27a7c91847Schristos #endif
28a7c91847Schristos
29a7c91847Schristos /* Specification. */
30a7c91847Schristos #include "filenamecat.h"
31a7c91847Schristos
32a7c91847Schristos #include <string.h>
33a7c91847Schristos
34a7c91847Schristos #include "dirname.h"
35a7c91847Schristos #include "xalloc.h"
36a7c91847Schristos
37a7c91847Schristos #if ! HAVE_MEMPCPY && ! defined mempcpy
38a7c91847Schristos # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
39a7c91847Schristos #endif
40a7c91847Schristos
41a7c91847Schristos /* Return the longest suffix of F that is a relative file name.
42a7c91847Schristos If it has no such suffix, return the empty string. */
43a7c91847Schristos
44a7c91847Schristos static char const *
longest_relative_suffix(char const * f)45a7c91847Schristos longest_relative_suffix (char const *f)
46a7c91847Schristos {
47a7c91847Schristos for (f += FILE_SYSTEM_PREFIX_LEN (f); ISSLASH (*f); f++)
48a7c91847Schristos continue;
49a7c91847Schristos return f;
50a7c91847Schristos }
51a7c91847Schristos
52a7c91847Schristos /* Concatenate two file name components, DIR and ABASE, in
53a7c91847Schristos newly-allocated storage and return the result.
54a7c91847Schristos The resulting file name F is such that the commands "ls F" and "(cd
55a7c91847Schristos DIR; ls BASE)" refer to the same file, where BASE is ABASE with any
56a7c91847Schristos file system prefixes and leading separators removed.
57a7c91847Schristos Arrange for a directory separator if necessary between DIR and BASE
58a7c91847Schristos in the result, removing any redundant separators.
59a7c91847Schristos In any case, if BASE_IN_RESULT is non-NULL, set
60a7c91847Schristos *BASE_IN_RESULT to point to the copy of ABASE in the returned
61a7c91847Schristos concatenation. However, if ABASE begins with more than one slash,
62a7c91847Schristos set *BASE_IN_RESULT to point to the sole corresponding slash that
63a7c91847Schristos is copied into the result buffer.
64a7c91847Schristos
65a7c91847Schristos Report an error if memory is exhausted. */
66a7c91847Schristos
67a7c91847Schristos char *
file_name_concat(char const * dir,char const * abase,char ** base_in_result)68a7c91847Schristos file_name_concat (char const *dir, char const *abase, char **base_in_result)
69a7c91847Schristos {
70a7c91847Schristos char const *dirbase = base_name (dir);
71a7c91847Schristos size_t dirbaselen = base_len (dirbase);
72a7c91847Schristos size_t dirlen = dirbase - dir + dirbaselen;
73a7c91847Schristos size_t needs_separator = (dirbaselen && ! ISSLASH (dirbase[dirbaselen - 1]));
74a7c91847Schristos
75a7c91847Schristos char const *base = longest_relative_suffix (abase);
76a7c91847Schristos size_t baselen = strlen (base);
77a7c91847Schristos
78a7c91847Schristos char *p_concat = xmalloc (dirlen + needs_separator + baselen + 1);
79a7c91847Schristos char *p;
80a7c91847Schristos
81a7c91847Schristos p = mempcpy (p_concat, dir, dirlen);
82a7c91847Schristos *p = DIRECTORY_SEPARATOR;
83a7c91847Schristos p += needs_separator;
84a7c91847Schristos
85a7c91847Schristos if (base_in_result)
86a7c91847Schristos *base_in_result = p - IS_ABSOLUTE_FILE_NAME (abase);
87a7c91847Schristos
88a7c91847Schristos p = mempcpy (p, base, baselen);
89a7c91847Schristos *p = '\0';
90a7c91847Schristos
91a7c91847Schristos return p_concat;
92a7c91847Schristos }
93a7c91847Schristos
94a7c91847Schristos #ifdef TEST_FILE_NAME_CONCAT
95a7c91847Schristos # include <stdlib.h>
96a7c91847Schristos # include <stdio.h>
97a7c91847Schristos int
main()98a7c91847Schristos main ()
99a7c91847Schristos {
100a7c91847Schristos static char const *const tests[][3] =
101a7c91847Schristos {
102a7c91847Schristos {"a", "b", "a/b"},
103a7c91847Schristos {"a/", "b", "a/b"},
104a7c91847Schristos {"a/", "/b", "a/b"},
105a7c91847Schristos {"a", "/b", "a/b"},
106a7c91847Schristos
107a7c91847Schristos {"/", "b", "/b"},
108a7c91847Schristos {"/", "/b", "/b"},
109a7c91847Schristos {"/", "/", "/"},
110a7c91847Schristos {"a", "/", "a/"}, /* this might deserve a diagnostic */
111a7c91847Schristos {"/a", "/", "/a/"}, /* this might deserve a diagnostic */
112a7c91847Schristos {"a", "//b", "a/b"},
113a7c91847Schristos };
114a7c91847Schristos size_t i;
115a7c91847Schristos bool fail = false;
116a7c91847Schristos for (i = 0; i < sizeof tests / sizeof tests[0]; i++)
117a7c91847Schristos {
118a7c91847Schristos char *base_in_result;
119a7c91847Schristos char const *const *t = tests[i];
120a7c91847Schristos char *res = file_name_concat (t[0], t[1], &base_in_result);
121a7c91847Schristos if (strcmp (res, t[2]) != 0)
122a7c91847Schristos {
123a7c91847Schristos printf ("got %s, expected %s\n", res, t[2]);
124a7c91847Schristos fail = true;
125a7c91847Schristos }
126a7c91847Schristos }
127a7c91847Schristos exit (fail ? EXIT_FAILURE : EXIT_SUCCESS);
128a7c91847Schristos }
129a7c91847Schristos #endif
130