summaryrefslogtreecommitdiffstats
path: root/src/compat/compat-dirname.c
blob: 887859508376aaed1a43ac17568e2c17bb63b6e6 (plain)
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
/*
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program (see the file COPYING included with this
 *  distribution); if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif


#ifndef HAVE_DIRNAME

#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)
{
  const char *end = str;

  end += n - 1; /* Go to the end of the string */
  while (end >= str) {
    if(c == *end)
      return end;
    else
      end--;
  }
  return NULL;
}

/* 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 *
dirname (char *path)
{
  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)
	if (runp[-1] != separator)
	  break;

      /* 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)
	if (runp[-1] != separator)
	  break;

      /* 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;

  return path;
}

#endif /* HAVE_DIRNAME */