189 lines
5.0 KiB
C
189 lines
5.0 KiB
C
/* $Id: path.c 11578 2009-02-23 22:02:27Z lalo $ */
|
|
|
|
/*
|
|
CrossFire, A Multiplayer game for X-windows
|
|
|
|
Copyright (C) 2006 Mark Wedel & Crossfire Development Team
|
|
Copyright (C) 1992 Frank Tore Johansen
|
|
|
|
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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
The authors can be reached via e-mail at crossfire-devel@real-time.com
|
|
*/
|
|
|
|
/**
|
|
* @file path.c
|
|
* Contains file path manipulation functions.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <global.h>
|
|
|
|
#include "define.h"
|
|
#include "path.h"
|
|
|
|
#if 0
|
|
/**
|
|
* Define DEBUG_PATH to enable debug output.
|
|
*/
|
|
#define DEBUG_PATH
|
|
#endif
|
|
|
|
/**
|
|
* Combines 2 paths, which can be relative.
|
|
*
|
|
* @param src
|
|
* path we're starting from.
|
|
* @param dst
|
|
* path we're doing to.
|
|
* @param path
|
|
* buffer containing the combined path.
|
|
* @param size
|
|
* size of path.
|
|
* @return
|
|
* path.
|
|
*
|
|
* @note
|
|
* this doesn't handle the '..', check path_normalize().
|
|
*/
|
|
char *path_combine(const char *src, const char *dst, char *path, size_t size) {
|
|
char *p;
|
|
|
|
if (*dst == '/') {
|
|
/* absolute destination path => ignore source path */
|
|
snprintf(path, size, "%s", dst);
|
|
} else {
|
|
/* relative destination path => add after last '/' of source */
|
|
snprintf(path, size, "%s", src);
|
|
p = strrchr(path, '/');
|
|
if (p != NULL) {
|
|
p++;
|
|
} else {
|
|
p = path;
|
|
if (*src == '/')
|
|
*p++ = '/';
|
|
}
|
|
snprintf(p, size-(p-path), "%s", dst);
|
|
}
|
|
|
|
#if defined(DEBUG_PATH)
|
|
LOG(llevDebug, "path_combine(%s, %s) = %s\n", src, dst, path);
|
|
#endif
|
|
return(path);
|
|
}
|
|
|
|
/**
|
|
* Cleans specified path. Removes .. and things like that.
|
|
*
|
|
* @param path
|
|
* path to clear. It will be modified in place.
|
|
* @note
|
|
* there shouldn't be any buffer overflow, as we just remove stuff.
|
|
*/
|
|
void path_normalize(char *path) {
|
|
char *p; /* points to the beginning of the path not yet processed; this is
|
|
either a path component or a path separator character */
|
|
char *q; /* points to the end of the path component p points to */
|
|
char *w; /* points to the end of the already normalized path; w <= p is
|
|
maintained */
|
|
size_t len; /* length of current component (which p points to) */
|
|
|
|
#if defined(DEBUG_PATH)
|
|
LOG(llevDebug, "path_normalize: input '%s'\n", path);
|
|
#endif
|
|
|
|
p = path;
|
|
w = p;
|
|
while (*p != '\0') {
|
|
if (*p == '/') {
|
|
if ((w == path && *path == '/') || (w > path && w[-1] != '/'))
|
|
*w++ = '/';
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
q = strchr(p, '/');
|
|
if (q == NULL)
|
|
q = p+strlen(p);
|
|
len = q-p;
|
|
assert(len > 0);
|
|
|
|
#if defined(DEBUG_PATH)
|
|
LOG(llevDebug, "path_normalize: checking '%.*s'\n", (int)len, p);
|
|
#endif
|
|
|
|
if (len == 1 && *p == '.') {
|
|
/* remove current component */
|
|
} else if (len == 2 && memcmp(p, "..", 2) == 0) {
|
|
if (w == path || (w == path+3 && memcmp(path, "../", 3) == 0)) {
|
|
/* keep ".." at beginning of relative path ("../x" => "../x") */
|
|
memmove(w, p, len);
|
|
w += len;
|
|
} else if (w == path+1 && *path == '/') {
|
|
/* remove ".." at beginning of absolute path ("/../x" => "/x") */
|
|
} else {
|
|
/* remove both current component ".." and preceding one */
|
|
if (w > path && w[-1] == '/')
|
|
w--;
|
|
while (w > path && w[-1] != '/')
|
|
w--;
|
|
}
|
|
} else {
|
|
/* normal component ==> add it */
|
|
memmove(w, p, len);
|
|
w += len;
|
|
}
|
|
|
|
p = q;
|
|
|
|
#if defined(DEBUG_PATH)
|
|
LOG(llevDebug, "path_normalize: so far '%.*s'\n", (int)(w-path), path);
|
|
#endif
|
|
}
|
|
|
|
/* remove trailing slashes, but keep the one at the start of the path */
|
|
while (w > path+1 && w[-1] == '/') {
|
|
w--;
|
|
}
|
|
|
|
*w = '\0';
|
|
|
|
#if defined(DEBUG_PATH)
|
|
LOG(llevDebug, "path_normalize: result '%s'\n", path);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Combines the 2 paths.
|
|
*
|
|
* @param src
|
|
* path we're starting from.
|
|
* @param dst
|
|
* path we're getting to.
|
|
* @param path
|
|
* buffer that will contain combined paths.
|
|
* @param size
|
|
* length of path.
|
|
* @return
|
|
* path
|
|
*/
|
|
char *path_combine_and_normalize(const char *src, const char *dst, char *path, size_t size) {
|
|
path_combine(src, dst, path, size);
|
|
path_normalize(path);
|
|
return(path);
|
|
}
|