src/compat/compat-dirname.c
ec302f70
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
  *  Copyright (C) 2011 - David Sommerseth <davids@redhat.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  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.
  *
caa54ac3
  *  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.
ec302f70
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
dc81e743
 
 #ifndef HAVE_DIRNAME
 
ec302f70
 #include "compat.h"
 #include <string.h>
 
 /* Unoptimised version of glibc memrchr().
  * This is considered fast enough, as only this compat
  * version of dirname() depends on it.
  */
 static const char *
 __memrchr(const char *str, int c, size_t n)
 {
81d882d5
     const char *end = str;
 
     end += n - 1; /* Go to the end of the string */
4cd4899e
     while (end >= str)
     {
81d882d5
         if (c == *end)
         {
             return end;
         }
         else
         {
             end--;
         }
     }
     return NULL;
ec302f70
 }
 
 /* Modified version based on glibc-2.14.1 by Ulrich Drepper <drepper@akkadia.org>
  * This version is extended to handle both / and \ in path names.
  */
 char *
81d882d5
 dirname(char *path)
ec302f70
 {
81d882d5
     static const char dot[] = ".";
     char *last_slash;
     char separator = '/';
 
     /* Find last '/'.  */
     last_slash = path != NULL ? strrchr(path, '/') : NULL;
     /* If NULL, check for \ instead ... might be Windows a path */
     if (!last_slash)
     {
         last_slash = path != NULL ? strrchr(path, '\\') : NULL;
         separator = last_slash ? '\\' : '/'; /* Change the separator if \ was found */
     }
 
     if (last_slash != NULL && last_slash != path && last_slash[1] == '\0')
     {
         /* Determine whether all remaining characters are slashes.  */
         char *runp;
 
         for (runp = last_slash; runp != path; --runp)
4cd4899e
         {
81d882d5
             if (runp[-1] != separator)
             {
                 break;
             }
4cd4899e
         }
81d882d5
 
         /* The '/' is the last character, we have to look further.  */
         if (runp != path)
         {
             last_slash = (char *) __memrchr(path, separator, runp - path);
         }
     }
 
     if (last_slash != NULL)
     {
         /* Determine whether all remaining characters are slashes.  */
         char *runp;
 
         for (runp = last_slash; runp != path; --runp)
4cd4899e
         {
81d882d5
             if (runp[-1] != separator)
             {
                 break;
             }
4cd4899e
         }
81d882d5
 
         /* Terminate the path.  */
         if (runp == path)
         {
             /* The last slash is the first character in the string.  We have to
              * return "/".  As a special case we have to return "//" if there
              * are exactly two slashes at the beginning of the string.  See
              * XBD 4.10 Path Name Resolution for more information.  */
             if (last_slash == path + 1)
             {
                 ++last_slash;
             }
             else
             {
                 last_slash = path + 1;
             }
         }
         else
         {
             last_slash = runp;
         }
 
         last_slash[0] = '\0';
     }
     else
     {
         /* This assignment is ill-designed but the XPG specs require to
          * return a string containing "." in any case no directory part is
          * found and so a static and constant string is required.  */
         path = (char *) dot;
ec302f70
     }
 
81d882d5
     return path;
ec302f70
 }
 
dc81e743
 #endif /* HAVE_DIRNAME */