xref: /freebsd/lib/libc/gen/dirname.c (revision 7be8de4271d5cb5d441e2757912c1824f6c3dc3b)
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 }
91