1 /* Concatenate two arbitrary file names.
3 Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free
4 Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
20 /* Written by Jim Meyering. */
27 #include "filenamecat.h"
34 #if ! HAVE_MEMPCPY && ! defined mempcpy
35 # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
38 /* Return the longest suffix of F that is a relative file name.
39 If it has no such suffix, return the empty string. */
42 longest_relative_suffix (char const *f
)
44 for (f
+= FILE_SYSTEM_PREFIX_LEN (f
); ISSLASH (*f
); f
++)
49 /* Concatenate two file name components, DIR and ABASE, in
50 newly-allocated storage and return the result.
51 The resulting file name F is such that the commands "ls F" and "(cd
52 DIR; ls BASE)" refer to the same file, where BASE is ABASE with any
53 file system prefixes and leading separators removed.
54 Arrange for a directory separator if necessary between DIR and BASE
55 in the result, removing any redundant separators.
56 In any case, if BASE_IN_RESULT is non-NULL, set
57 *BASE_IN_RESULT to point to the copy of ABASE in the returned
58 concatenation. However, if ABASE begins with more than one slash,
59 set *BASE_IN_RESULT to point to the sole corresponding slash that
60 is copied into the result buffer.
62 Report an error if memory is exhausted. */
65 file_name_concat (char const *dir
, char const *abase
, char **base_in_result
)
67 char const *dirbase
= base_name (dir
);
68 size_t dirbaselen
= base_len (dirbase
);
69 size_t dirlen
= dirbase
- dir
+ dirbaselen
;
70 size_t needs_separator
= (dirbaselen
&& ! ISSLASH (dirbase
[dirbaselen
- 1]));
72 char const *base
= longest_relative_suffix (abase
);
73 size_t baselen
= strlen (base
);
75 char *p_concat
= xmalloc (dirlen
+ needs_separator
+ baselen
+ 1);
78 p
= mempcpy (p_concat
, dir
, dirlen
);
79 *p
= DIRECTORY_SEPARATOR
;
83 *base_in_result
= p
- IS_ABSOLUTE_FILE_NAME (abase
);
85 p
= mempcpy (p
, base
, baselen
);
91 #ifdef TEST_FILE_NAME_CONCAT
97 static char const *const tests
[][3] =
107 {"a", "/", "a/"}, /* this might deserve a diagnostic */
108 {"/a", "/", "/a/"}, /* this might deserve a diagnostic */
113 for (i
= 0; i
< sizeof tests
/ sizeof tests
[0]; i
++)
115 char *base_in_result
;
116 char const *const *t
= tests
[i
];
117 char *res
= file_name_concat (t
[0], t
[1], &base_in_result
);
118 if (strcmp (res
, t
[2]) != 0)
120 printf ("got %s, expected %s\n", res
, t
[2]);
124 exit (fail
? EXIT_FAILURE
: EXIT_SUCCESS
);