NeoMutt  2024-12-12-14-g7b49f7
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 char rpath[PATH_MAX + 1] = { 0 };
351 const char *result = realpath(buf_string(abs_path), rpath);
352 buf_pool_release(&abs_path);
353 if (!result)
354 {
355 if (errno != ENOENT)
356 {
357 mutt_perror(_("Error: converting path to absolute"));
358 }
359 return false;
360 }
361 else
362 {
363 mutt_str_copy(path, rpath, PATH_MAX);
364 }
365
366 return true;
367}
368
377size_t mutt_path_realpath(struct Buffer *path)
378{
379 if (buf_is_empty(path))
380 return 0;
381
382 char s[PATH_MAX] = { 0 };
383
384 if (!realpath(buf_string(path), s))
385 return 0;
386
387 return buf_strcpy(path, s);
388}
389
399bool mutt_path_abbr_folder(struct Buffer *path, const char *folder)
400{
401 if (buf_is_empty(path) || !folder)
402 return false;
403
404 size_t flen = mutt_str_len(folder);
405 if (flen < 2)
406 return false;
407
408 if (folder[flen - 1] == '/')
409 flen--;
410
411 if (!mutt_strn_equal(buf_string(path), folder, flen))
412 return false;
413
414 if (buf_at(path, flen + 1) == '\0') // Don't abbreviate to '=/'
415 return false;
416
417 size_t rlen = mutt_str_len(path->data + flen + 1);
418
419 path->data[0] = '=';
420 memmove(path->data + 1, path->data + flen + 1, rlen + 1);
421 buf_fix_dptr(path);
422
423 return true;
424}
425
433char *mutt_path_escape(const char *src)
434{
435 if (!src)
436 return NULL;
437
438 static char dest[STR_COMMAND];
439 char *destp = dest;
440 int destsize = 0;
441
442 while (*src && (destsize < sizeof(dest) - 1))
443 {
444 if (*src != '\'')
445 {
446 *destp++ = *src++;
447 destsize++;
448 }
449 else
450 {
451 /* convert ' into '\'' */
452 if ((destsize + 4) < sizeof(dest))
453 {
454 *destp++ = *src++;
455 *destp++ = '\\';
456 *destp++ = '\'';
457 *destp++ = '\'';
458 destsize += 4;
459 }
460 else
461 {
462 break; // LCOV_EXCL_LINE
463 }
464 }
465 }
466 *destp = '\0';
467
468 return dest;
469}
470
476const char *mutt_path_getcwd(struct Buffer *cwd)
477{
478 if (!cwd)
479 return NULL;
480
481 buf_alloc(cwd, PATH_MAX);
482 char *rc = getcwd(cwd->data, cwd->dsize);
483 while (!rc && (errno == ERANGE))
484 {
485 buf_alloc(cwd, cwd->dsize + 256); // LCOV_EXCL_LINE
486 rc = getcwd(cwd->data, cwd->dsize); // LCOV_EXCL_LINE
487 }
488 if (rc)
489 buf_fix_dptr(cwd);
490 else
491 buf_reset(cwd); // LCOV_EXCL_LINE
492
493 return rc;
494}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:182
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition: buffer.c:670
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
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:256
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:337
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:55
#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 a relative path to its absolute form.
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:433
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:399
size_t mutt_path_realpath(struct Buffer *path)
Resolve path, unraveling symlinks.
Definition: path.c:377
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:476
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:425
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
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:581
#define PATH_MAX
Definition: mutt.h:42
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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