NeoMutt  2024-04-16-36-g75b6fb
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
path.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <errno.h>
34#include <libgen.h>
35#include <limits.h>
36#include <pwd.h>
37#include <stdbool.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include "path.h"
43#include "buffer.h"
44#include "logging2.h"
45#include "memory.h"
46#include "message.h"
47#include "pool.h"
48#include "string2.h"
49
58bool mutt_path_tidy_slash(char *buf, bool is_dir)
59{
60 if (!buf)
61 return false;
62
63 char *r = buf;
64 char *w = buf;
65
66 while (*r != '\0')
67 {
68 *w++ = *r++;
69
70 if (r[-1] == '/') /* After a '/' ... */
71 {
72 for (; (r[0] == '/') || (r[0] == '.'); r++)
73 {
74 if (r[0] == '/') /* skip multiple /s */
75 continue;
76 if (r[0] == '.')
77 {
78 if (r[1] == '/') /* skip /./ */
79 {
80 r++;
81 continue;
82 }
83 else if (r[1] == '\0') /* skip /.$ */
84 {
85 r[0] = '\0';
86 }
87 break; /* dot-anything-else isn't special */
88 }
89 }
90 }
91 }
92
93 /* Strip a trailing / as long as it's not the only character */
94 if (is_dir && (w > (buf + 1)) && (w[-1] == '/'))
95 w--;
96
97 *w = '\0';
98
99 return true;
100}
101
110{
111 if (!buf || (buf[0] != '/'))
112 return false;
113
114 char *dd = buf;
115
116 mutt_debug(LL_DEBUG3, "Collapse path: %s\n", buf);
117 while ((dd = strstr(dd, "/..")))
118 {
119 if (dd[3] == '/') /* paths follow dots */
120 {
121 char *dest = NULL;
122 if (dd != buf) /* not at start of string */
123 {
124 dd[0] = '\0';
125 dest = strrchr(buf, '/');
126 }
127 if (!dest)
128 dest = buf;
129
130 memmove(dest, dd + 3, strlen(dd + 3) + 1);
131 }
132 else if (dd[3] == '\0') /* dots at end of string */
133 {
134 if (dd == buf) /* at start of string */
135 {
136 dd[1] = '\0';
137 }
138 else
139 {
140 dd[0] = '\0';
141 char *s = strrchr(buf, '/');
142 if (s == buf)
143 s[1] = '\0';
144 else if (s)
145 s[0] = '\0';
146 }
147 }
148 else
149 {
150 dd += 3; /* skip over '/..dir/' */
151 continue;
152 }
153
154 dd = buf; /* restart at the beginning */
155 }
156
157 mutt_debug(LL_DEBUG3, "Collapsed to: %s\n", buf);
158 return true;
159}
160
169bool mutt_path_tidy(struct Buffer *path, bool is_dir)
170{
171 if (buf_is_empty(path) || (buf_at(path, 0) != '/'))
172 return false;
173
174 if (!mutt_path_tidy_slash(path->data, is_dir))
175 return false; // LCOV_EXCL_LINE
176
178 buf_fix_dptr(path);
179
180 return true;
181}
182
194bool mutt_path_tilde(struct Buffer *path, const char *homedir)
195{
196 if (buf_is_empty(path) || (buf_at(path, 0) != '~'))
197 return false;
198
199 char result[PATH_MAX] = { 0 };
200 char *dir = NULL;
201 size_t len = 0;
202
203 if ((buf_at(path, 1) == '/') || (buf_at(path, 1) == '\0'))
204 {
205 if (!homedir)
206 {
207 mutt_debug(LL_DEBUG3, "no homedir\n");
208 return false;
209 }
210
211 len = mutt_str_copy(result, homedir, sizeof(result));
212 dir = path->data + 1;
213 }
214 else
215 {
216 char user[128] = { 0 };
217 dir = strchr(path->data + 1, '/');
218 if (dir)
219 mutt_str_copy(user, path->data + 1, MIN(dir - path->data, (unsigned) sizeof(user)));
220 else
221 mutt_str_copy(user, path->data + 1, sizeof(user));
222
223 struct passwd *pw = getpwnam(user);
224 if (!pw || !pw->pw_dir)
225 {
226 mutt_debug(LL_DEBUG1, "no such user: %s\n", user);
227 return false;
228 }
229
230 len = mutt_str_copy(result, pw->pw_dir, sizeof(result));
231 }
232
233 mutt_str_copy(result + len, dir, sizeof(result) - len);
234 buf_strcpy(path, result);
235
236 return true;
237}
238
248bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
249{
250 if (buf_is_empty(path))
251 return false;
252
253 if (buf_at(path, 0) == '~')
254 {
255 mutt_path_tilde(path, homedir);
256 }
257 else if (buf_at(path, 0) != '/')
258 {
259 char cwd[PATH_MAX] = { 0 };
260 if (!getcwd(cwd, sizeof(cwd)))
261 {
262 mutt_debug(LL_DEBUG1, "getcwd failed: %s (%d)\n", strerror(errno), errno); // LCOV_EXCL_LINE
263 return false; // LCOV_EXCL_LINE
264 }
265
266 size_t cwd_len = mutt_str_len(cwd);
267 cwd[cwd_len] = '/';
268 cwd[cwd_len + 1] = '\0';
269 buf_insert(path, 0, cwd);
270 }
271
272 return mutt_path_tidy(path, is_dir);
273}
274
282const char *mutt_path_basename(const char *path)
283{
284 if (!path)
285 return NULL;
286
287 const char *p = strrchr(path, '/');
288 if (p)
289 {
290 if (p[1] == '\0')
291 return path;
292
293 return p + 1;
294 }
295
296 return path;
297}
298
312char *mutt_path_dirname(const char *path)
313{
314 if (!path)
315 return NULL;
316
317 char buf[PATH_MAX] = { 0 };
318 mutt_str_copy(buf, path, sizeof(buf));
319 return mutt_str_dup(dirname(buf));
320}
321
333bool mutt_path_to_absolute(char *path, const char *reference)
334{
335 if (!path || !reference)
336 return false;
337
338 /* if path is already absolute, don't do anything */
339 if ((strlen(path) > 1) && (path[0] == '/'))
340 {
341 return true;
342 }
343
344 struct Buffer *abs_path = buf_pool_get();
345
346 char *dirpath = mutt_path_dirname(reference);
347 buf_printf(abs_path, "%s/%s", dirpath, path);
348 FREE(&dirpath);
349
350 path = realpath(buf_string(abs_path), path);
351 if (!path && (errno != ENOENT))
352 {
353 mutt_perror(_("Error: converting path to absolute"));
354 buf_pool_release(&abs_path);
355 return false;
356 }
357
358 buf_pool_release(&abs_path);
359 return true;
360}
361
370size_t mutt_path_realpath(struct Buffer *path)
371{
372 if (buf_is_empty(path))
373 return 0;
374
375 char s[PATH_MAX] = { 0 };
376
377 if (!realpath(buf_string(path), s))
378 return 0;
379
380 return buf_strcpy(path, s);
381}
382
392bool mutt_path_abbr_folder(struct Buffer *path, const char *folder)
393{
394 if (buf_is_empty(path) || !folder)
395 return false;
396
397 size_t flen = mutt_str_len(folder);
398 if (flen < 2)
399 return false;
400
401 if (folder[flen - 1] == '/')
402 flen--;
403
404 if (!mutt_strn_equal(buf_string(path), folder, flen))
405 return false;
406
407 if (buf_at(path, flen + 1) == '\0') // Don't abbreviate to '=/'
408 return false;
409
410 size_t rlen = mutt_str_len(path->data + flen + 1);
411
412 path->data[0] = '=';
413 memmove(path->data + 1, path->data + flen + 1, rlen + 1);
414 buf_fix_dptr(path);
415
416 return true;
417}
418
426char *mutt_path_escape(const char *src)
427{
428 if (!src)
429 return NULL;
430
431 static char dest[STR_COMMAND];
432 char *destp = dest;
433 int destsize = 0;
434
435 while (*src && (destsize < sizeof(dest) - 1))
436 {
437 if (*src != '\'')
438 {
439 *destp++ = *src++;
440 destsize++;
441 }
442 else
443 {
444 /* convert ' into '\'' */
445 if ((destsize + 4) < sizeof(dest))
446 {
447 *destp++ = *src++;
448 *destp++ = '\\';
449 *destp++ = '\'';
450 *destp++ = '\'';
451 destsize += 4;
452 }
453 else
454 {
455 break; // LCOV_EXCL_LINE
456 }
457 }
458 }
459 *destp = '\0';
460
461 return dest;
462}
463
469const char *mutt_path_getcwd(struct Buffer *cwd)
470{
471 if (!cwd)
472 return NULL;
473
474 buf_alloc(cwd, PATH_MAX);
475 char *rc = getcwd(cwd->data, cwd->dsize);
476 while (!rc && (errno == ERANGE))
477 {
478 buf_alloc(cwd, cwd->dsize + 256); // LCOV_EXCL_LINE
479 rc = getcwd(cwd->data, cwd->dsize); // LCOV_EXCL_LINE
480 }
481 if (rc)
482 buf_fix_dptr(cwd);
483 else
484 buf_reset(cwd); // LCOV_EXCL_LINE
485
486 return rc;
487}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:75
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:290
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition: buffer.c:669
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
size_t buf_insert(struct Buffer *buf, size_t offset, const char *s)
Add a string in the middle of a buffer.
Definition: buffer.c:255
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:336
General purpose object for storing and parsing strings.
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
Logging Dispatcher.
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
Memory management wrappers.
#define FREE(x)
Definition: memory.h:45
#define MIN(a, b)
Definition: memory.h:32
Message logging.
#define _(a)
Definition: message.h:28
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:109
const char * mutt_path_basename(const char *path)
Find the last component for a pathname.
Definition: path.c:282
bool mutt_path_tidy(struct Buffer *path, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:169
bool mutt_path_to_absolute(char *path, const char *reference)
Convert relative filepath to an absolute path.
Definition: path.c:333
bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:248
char * mutt_path_escape(const char *src)
Escapes single quotes in a path for a command string.
Definition: path.c:426
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:312
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:58
bool mutt_path_abbr_folder(struct Buffer *path, const char *folder)
Create a folder abbreviation.
Definition: path.c:392
size_t mutt_path_realpath(struct Buffer *path)
Resolve path, unraveling symlinks.
Definition: path.c:370
bool mutt_path_tilde(struct Buffer *path, const char *homedir)
Expand '~' in a path.
Definition: path.c:194
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:469
Path manipulation functions.
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:419
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:490
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:575
#define PATH_MAX
Definition: mutt.h:42
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
A global pool of Buffers.
String manipulation functions.
#define STR_COMMAND
Enough space for a long command line.
Definition: string2.h:35
String manipulation buffer.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37