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