]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/gen/dirname.c
Reimplement dirname(3) to be thread-safe.
[FreeBSD/FreeBSD.git] / lib / libc / gen / dirname.c
1 /*-
2  * Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <libgen.h>
30 #include <stdbool.h>
31 #include <string.h>
32
33 char *
34 dirname(char *path)
35 {
36         const char *in, *prev, *begin, *end;
37         char *out;
38         size_t prevlen;
39         bool skipslash;
40
41         /*
42          * If path is a null pointer or points to an empty string,
43          * dirname() shall return a pointer to the string ".".
44          */
45         if (path == NULL || *path == '\0')
46                 return ((char *)".");
47
48         /* Retain at least one leading slash character. */
49         in = out = *path == '/' ? path + 1 : path;
50
51         skipslash = true;
52         prev = ".";
53         prevlen = 1;
54         for (;;) {
55                 /* Extract the next pathname component. */
56                 while (*in == '/')
57                         ++in;
58                 begin = in;
59                 while (*in != '/' && *in != '\0')
60                         ++in;
61                 end = in;
62                 if (begin == end)
63                         break;
64
65                 /*
66                  * Copy over the previous pathname component, except if
67                  * it's dot. There is no point in retaining those.
68                  */
69                 if (prevlen != 1 || *prev != '.') {
70                         if (!skipslash)
71                                 *out++ = '/';
72                         skipslash = false;
73                         memmove(out, prev, prevlen);
74                         out += prevlen;
75                 }
76
77                 /* Preserve the pathname component for the next iteration. */
78                 prev = begin;
79                 prevlen = end - begin;
80         }
81
82         /*
83          * If path does not contain a '/', then dirname() shall return a
84          * pointer to the string ".".
85          */
86         if (out == path)
87                 *out++ = '.';
88         *out = '\0';
89         return (path);
90 }