Crossfire Server, Branch 1.12  R12190
path.c
Go to the documentation of this file.
00001 /* $Id: path.c 11578 2009-02-23 22:02:27Z lalo $ */
00002 
00003 /*
00004     CrossFire, A Multiplayer game for X-windows
00005 
00006     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00007     Copyright (C) 1992 Frank Tore Johansen
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program; if not, write to the Free Software
00021     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00022 
00023     The authors can be reached via e-mail at crossfire-devel@real-time.com
00024 */
00025 
00031 #include <assert.h>
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <global.h>
00035 
00036 #include "define.h"
00037 #include "path.h"
00038 
00039 #if 0
00040 
00043 #define DEBUG_PATH
00044 #endif
00045 
00063 char *path_combine(const char *src, const char *dst, char *path, size_t size) {
00064     char *p;
00065 
00066     if (*dst == '/') {
00067         /* absolute destination path => ignore source path */
00068         snprintf(path, size, "%s", dst);
00069     } else {
00070         /* relative destination path => add after last '/' of source */
00071         snprintf(path, size, "%s", src);
00072         p = strrchr(path, '/');
00073         if (p != NULL) {
00074             p++;
00075         } else {
00076             p = path;
00077             if (*src == '/')
00078                 *p++ = '/';
00079         }
00080         snprintf(p, size-(p-path), "%s", dst);
00081     }
00082 
00083 #if defined(DEBUG_PATH)
00084     LOG(llevDebug, "path_combine(%s, %s) = %s\n", src, dst, path);
00085 #endif
00086     return(path);
00087 }
00088 
00097 void path_normalize(char *path) {
00098     char *p; /* points to the beginning of the path not yet processed; this is
00099                 either a path component or a path separator character */
00100     char *q; /* points to the end of the path component p points to */
00101     char *w; /* points to the end of the already normalized path; w <= p is
00102                 maintained */
00103     size_t len; /* length of current component (which p points to) */
00104 
00105 #if defined(DEBUG_PATH)
00106     LOG(llevDebug, "path_normalize: input '%s'\n", path);
00107 #endif
00108 
00109     p = path;
00110     w = p;
00111     while (*p != '\0') {
00112         if (*p == '/') {
00113             if ((w == path && *path == '/') || (w > path && w[-1] != '/'))
00114                 *w++ = '/';
00115             p++;
00116             continue;
00117         }
00118 
00119         q = strchr(p, '/');
00120         if (q == NULL)
00121             q = p+strlen(p);
00122         len = q-p;
00123         assert(len > 0);
00124 
00125 #if defined(DEBUG_PATH)
00126         LOG(llevDebug, "path_normalize: checking '%.*s'\n", (int)len, p);
00127 #endif
00128 
00129         if (len == 1 && *p == '.') {
00130             /* remove current component */
00131         } else if (len == 2 && memcmp(p, "..", 2) == 0) {
00132             if (w == path || (w == path+3 && memcmp(path, "../", 3) == 0)) {
00133                 /* keep ".." at beginning of relative path ("../x" => "../x") */
00134                 memmove(w, p, len);
00135                 w += len;
00136             } else if (w == path+1 && *path == '/') {
00137                 /* remove ".." at beginning of absolute path ("/../x" => "/x") */
00138             } else {
00139                 /* remove both current component ".." and preceding one */
00140                 if (w > path && w[-1] == '/')
00141                     w--;
00142                 while (w > path && w[-1] != '/')
00143                     w--;
00144             }
00145         } else {
00146             /* normal component ==> add it */
00147             memmove(w, p, len);
00148             w += len;
00149         }
00150 
00151         p = q;
00152 
00153 #if defined(DEBUG_PATH)
00154         LOG(llevDebug, "path_normalize: so far '%.*s'\n", (int)(w-path), path);
00155 #endif
00156     }
00157 
00158     /* remove trailing slashes, but keep the one at the start of the path */
00159     while (w > path+1 && w[-1] == '/') {
00160         w--;
00161     }
00162 
00163     *w = '\0';
00164 
00165 #if defined(DEBUG_PATH)
00166     LOG(llevDebug, "path_normalize: result '%s'\n", path);
00167 #endif
00168 }
00169 
00184 char *path_combine_and_normalize(const char *src, const char *dst, char *path, size_t size) {
00185     path_combine(src, dst, path, size);
00186     path_normalize(path);
00187     return(path);
00188 }