1 /*
2  * libdpkg - Debian packaging suite library routines
3  * path.c - path handling functions
4  *
5  * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6  * Copyright © 2008-2012 Guillem Jover <guillem@debian.org>
7  *
8  * This is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <compat.h>
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 
29 #include <dpkg/dpkg.h>
30 #include <dpkg/string.h>
31 #include <dpkg/path.h>
32 
33 /**
34  * Trim ‘/’ and ‘/.’ from the end of a pathname.
35  *
36  * The given string will get NUL-terminatd.
37  *
38  * @param path The pathname to trim.
39  *
40  * @return The size of the trimmed pathname.
41  */
42 size_t
path_trim_slash_slashdot(char * path)43 path_trim_slash_slashdot(char *path)
44 {
45 	char *end;
46 
47 	if (str_is_unset(path))
48 		return 0;
49 
50 	for (end = path + strlen(path) - 1; end - path >= 1; end--) {
51 		if (*end == '/' || (*(end - 1) == '/' && *end == '.'))
52 			*end = '\0';
53 		else
54 			break;
55 	}
56 
57 	return end - path + 1;
58 }
59 
60 /**
61  * Skip ‘/’ and ‘./’ from the beginning of a pathname.
62  *
63  * @param path The pathname to skip.
64  *
65  * @return The new beginning of the pathname.
66  */
67 const char *
path_skip_slash_dotslash(const char * path)68 path_skip_slash_dotslash(const char *path)
69 {
70 	while (path[0] == '/' || (path[0] == '.' && path[1] == '/'))
71 		path++;
72 
73 	return path;
74 }
75 
76 /**
77  * Return the last component of a pathname.
78  *
79  * @param path The pathname to get the base name from.
80  *
81  * @return A pointer to the last component inside pathname.
82  */
83 const char *
path_basename(const char * path)84 path_basename(const char *path)
85 {
86 	const char *last_slash;
87 
88 	last_slash = strrchr(path, '/');
89 	if (last_slash == NULL)
90 		return path;
91 	else
92 		return last_slash + 1;
93 }
94 
95 /**
96  * Create a template for a temporary pathname.
97  *
98  * @param suffix The suffix to use for the template string.
99  *
100  * @return An allocated string with the created template.
101  */
102 char *
path_make_temp_template(const char * suffix)103 path_make_temp_template(const char *suffix)
104 {
105 	const char *tmpdir;
106 
107 	tmpdir = getenv("TMPDIR");
108 	if (!tmpdir)
109 		tmpdir = P_tmpdir;
110 
111 	return str_fmt("%s/%s.XXXXXX", tmpdir, suffix);
112 }
113 
114 /**
115  * Escape characters in a pathname for safe locale printing.
116  *
117  * We need to quote paths so that they do not cause problems when printing
118  * them, for example with snprintf(3) which does not work if the format
119  * string contains %s and an argument has invalid characters for the
120  * current locale, it will then return -1.
121  *
122  * To simplify things, we just escape all 8 bit characters, instead of
123  * just invalid characters.
124  *
125  * @param dst The escaped destination string.
126  * @param src The source string to escape.
127  * @param n The size of the destination buffer.
128  *
129  * @return The destination string.
130  */
131 char *
path_quote_filename(char * dst,const char * src,size_t n)132 path_quote_filename(char *dst, const char *src, size_t n)
133 {
134 	char *r = dst;
135 	ssize_t size = (ssize_t)n;
136 
137 	if (size == 0)
138 		return r;
139 
140 	while (*src) {
141 		if (*src == '\\') {
142 			size -= 2;
143 			if (size <= 0)
144 				break;
145 
146 			*dst++ = '\\';
147 			*dst++ = '\\';
148 			src++;
149 		} else if (((*src) & 0x80) == '\0') {
150 			size--;
151 			if (size <= 0)
152 				break;
153 
154 			*dst++ = *src++;
155 		} else {
156 			size -= 4;
157 			if (size <= 0)
158 				break;
159 
160 			sprintf(dst, "\\%03o",
161 			        *(const unsigned char *)src);
162 			dst += 4;
163 			src++;
164 		}
165 	}
166 
167 	*dst = '\0';
168 
169 	return r;
170 }
171