NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
path.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <errno.h>
31#include <libgen.h>
32#include <limits.h>
33#include <pwd.h>
34#include <stdbool.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include "path.h"
40#include "buffer.h"
41#include "logging2.h"
42#include "memory.h"
43#include "message.h"
44#include "string2.h"
45
54bool mutt_path_tidy_slash(char *buf, bool is_dir)
55{
56 if (!buf)
57 return false;
58
59 char *r = buf;
60 char *w = buf;
61
62 while (*r != '\0')
63 {
64 *w++ = *r++;
65
66 if (r[-1] == '/') /* After a '/' ... */
67 {
68 for (; (r[0] == '/') || (r[0] == '.'); r++)
69 {
70 if (r[0] == '/') /* skip multiple /s */
71 continue;
72 if (r[0] == '.')
73 {
74 if (r[1] == '/') /* skip /./ */
75 {
76 r++;
77 continue;
78 }
79 else if (r[1] == '\0') /* skip /.$ */
80 {
81 r[0] = '\0';
82 }
83 break; /* dot-anything-else isn't special */
84 }
85 }
86 }
87 }
88
89 /* Strip a trailing / as long as it's not the only character */
90 if (is_dir && (w > (buf + 1)) && (w[-1] == '/'))
91 w--;
92
93 *w = '\0';
94
95 return true;
96}
97
106{
107 if (!buf || (buf[0] != '/'))
108 return false;
109
110 char *dd = buf;
111
112 mutt_debug(LL_DEBUG3, "Collapse path: %s\n", buf);
113 while ((dd = strstr(dd, "/..")))
114 {
115 if (dd[3] == '/') /* paths follow dots */
116 {
117 char *dest = NULL;
118 if (dd != buf) /* not at start of string */
119 {
120 dd[0] = '\0';
121 dest = strrchr(buf, '/');
122 }
123 if (!dest)
124 dest = buf;
125
126 memmove(dest, dd + 3, strlen(dd + 3) + 1);
127 }
128 else if (dd[3] == '\0') /* dots at end of string */
129 {
130 if (dd == buf) /* at start of string */
131 {
132 dd[1] = '\0';
133 }
134 else
135 {
136 dd[0] = '\0';
137 char *s = strrchr(buf, '/');
138 if (s == buf)
139 s[1] = '\0';
140 else if (s)
141 s[0] = '\0';
142 }
143 }
144 else
145 {
146 dd += 3; /* skip over '/..dir/' */
147 continue;
148 }
149
150 dd = buf; /* restart at the beginning */
151 }
152
153 mutt_debug(LL_DEBUG3, "Collapsed to: %s\n", buf);
154 return true;
155}
156
165bool mutt_path_tidy(struct Buffer *path, bool is_dir)
166{
167 if (buf_is_empty(path) || (buf_at(path, 0) != '/'))
168 return false;
169
170 if (!mutt_path_tidy_slash(path->data, is_dir))
171 return false; // LCOV_EXCL_LINE
172
174 buf_fix_dptr(path);
175
176 return true;
177}
178
188bool mutt_path_pretty(struct Buffer *path, const char *homedir, bool is_dir)
189{
190 if (buf_is_empty(path))
191 return false;
192
193 mutt_path_tidy(path, is_dir);
194
195 size_t len = mutt_str_startswith(path->data, homedir);
196 if (len == 0)
197 return false;
198
199 if ((buf_at(path, len) != '/') && (buf_at(path, len) != '\0'))
200 return false;
201
202 path->data[0] = '~';
203 if (buf_len(path) == len)
204 {
205 path->data[1] = '\0';
206 buf_fix_dptr(path);
207 return true;
208 }
209
210 mutt_str_copy(path->data + 1, path->data + len, buf_len(path) + 1 - len);
211 buf_fix_dptr(path);
212 return true;
213}
214
226bool mutt_path_tilde(struct Buffer *path, const char *homedir)
227{
228 if (buf_is_empty(path) || (buf_at(path, 0) != '~'))
229 return false;
230
231 char result[PATH_MAX] = { 0 };
232 char *dir = NULL;
233 size_t len = 0;
234
235 if ((buf_at(path, 1) == '/') || (buf_at(path, 1) == '\0'))
236 {
237 if (!homedir)
238 {
239 mutt_debug(LL_DEBUG3, "no homedir\n");
240 return false;
241 }
242
243 len = mutt_str_copy(result, homedir, sizeof(result));
244 dir = path->data + 1;
245 }
246 else
247 {
248 char user[128] = { 0 };
249 dir = strchr(path->data + 1, '/');
250 if (dir)
251 mutt_str_copy(user, path->data + 1, MIN(dir - path->data, (unsigned) sizeof(user)));
252 else
253 mutt_str_copy(user, path->data + 1, sizeof(user));
254
255 struct passwd *pw = getpwnam(user);
256 if (!pw || !pw->pw_dir)
257 {
258 mutt_debug(LL_DEBUG1, "no such user: %s\n", user);
259 return false;
260 }
261
262 len = mutt_str_copy(result, pw->pw_dir, sizeof(result));
263 }
264
265 mutt_str_copy(result + len, dir, sizeof(result) - len);
266 buf_strcpy(path, result);
267
268 return true;
269}
270
280bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
281{
282 if (buf_is_empty(path))
283 return false;
284
285 if (buf_at(path, 0) == '~')
286 {
287 mutt_path_tilde(path, homedir);
288 }
289 else if (buf_at(path, 0) != '/')
290 {
291 char cwd[PATH_MAX] = { 0 };
292 if (!getcwd(cwd, sizeof(cwd)))
293 {
294 mutt_debug(LL_DEBUG1, "getcwd failed: %s (%d)\n", strerror(errno), errno); // LCOV_EXCL_LINE
295 return false; // LCOV_EXCL_LINE
296 }
297
298 size_t cwd_len = mutt_str_len(cwd);
299 cwd[cwd_len] = '/';
300 cwd[cwd_len + 1] = '\0';
301 buf_insert(path, 0, cwd);
302 }
303
304 return mutt_path_tidy(path, is_dir);
305}
306
314const char *mutt_path_basename(const char *path)
315{
316 if (!path)
317 return NULL;
318
319 const char *p = strrchr(path, '/');
320 if (p)
321 {
322 if (p[1] == '\0')
323 return path;
324
325 return p + 1;
326 }
327
328 return path;
329}
330
342char *mutt_path_concat(char *dest, const char *dir, const char *file, size_t dlen)
343{
344 if (!dest || (!dir && !file))
345 return NULL;
346
347 if (dir && (!file || (file[0] == '\0')))
348 {
349 strncpy(dest, dir, dlen);
350 return dest;
351 }
352
353 if (file && (!dir || (dir[0] == '\0')))
354 {
355 strncpy(dest, file, dlen);
356 return dest;
357 }
358
359 const char *fmt = "%s/%s";
360
361 if (dir[strlen(dir) - 1] == '/')
362 fmt = "%s%s";
363
364 snprintf(dest, dlen, fmt, dir, file);
365 return dest;
366}
367
381char *mutt_path_dirname(const char *path)
382{
383 if (!path)
384 return NULL;
385
386 char buf[PATH_MAX] = { 0 };
387 mutt_str_copy(buf, path, sizeof(buf));
388 return mutt_str_dup(dirname(buf));
389}
390
402bool mutt_path_to_absolute(char *path, const char *reference)
403{
404 if (!path || !reference)
405 return false;
406
407 char abs_path[PATH_MAX] = { 0 };
408 int path_len;
409
410 /* if path is already absolute, don't do anything */
411 if ((strlen(path) > 1) && (path[0] == '/'))
412 {
413 return true;
414 }
415
416 char *dirpath = mutt_path_dirname(reference);
417 mutt_str_copy(abs_path, dirpath, sizeof(abs_path));
418 FREE(&dirpath);
419 mutt_strn_cat(abs_path, sizeof(abs_path), "/", 1); /* append a / at the end of the path */
420
421 path_len = sizeof(abs_path) - strlen(path);
422
423 mutt_strn_cat(abs_path, sizeof(abs_path), path, (path_len > 0) ? path_len : 0);
424
425 path = realpath(abs_path, path);
426 if (!path && (errno != ENOENT))
427 {
428 mutt_perror(_("Error: converting path to absolute"));
429 return false;
430 }
431
432 return true;
433}
434
443size_t mutt_path_realpath(struct Buffer *path)
444{
445 if (buf_is_empty(path))
446 return 0;
447
448 char s[PATH_MAX] = { 0 };
449
450 if (!realpath(buf_string(path), s))
451 return 0;
452
453 return buf_strcpy(path, s);
454}
455
461bool mutt_path_parent(struct Buffer *path)
462{
463 if (buf_is_empty(path))
464 return false;
465
466 int n = buf_len(path);
467 if (n < 2)
468 return false;
469
470 if (buf_at(path, n - 1) == '/')
471 n--;
472
473 // Find the previous '/'
474 for (n--; ((n >= 0) && (buf_at(path, n) != '/')); n--)
475 ; // do nothing
476
477 if (n == 0) // Always keep at least one '/'
478 n++;
479
480 path->data[n] = '\0';
481 buf_fix_dptr(path);
482 return true;
483}
484
494bool mutt_path_abbr_folder(struct Buffer *path, const char *folder)
495{
496 if (buf_is_empty(path) || !folder)
497 return false;
498
499 size_t flen = mutt_str_len(folder);
500 if (flen < 2)
501 return false;
502
503 if (folder[flen - 1] == '/')
504 flen--;
505
506 if (!mutt_strn_equal(buf_string(path), folder, flen))
507 return false;
508
509 if (buf_at(path, flen + 1) == '\0') // Don't abbreviate to '=/'
510 return false;
511
512 size_t rlen = mutt_str_len(path->data + flen + 1);
513
514 path->data[0] = '=';
515 memmove(path->data + 1, path->data + flen + 1, rlen + 1);
516 buf_fix_dptr(path);
517
518 return true;
519}
520
528char *mutt_path_escape(const char *src)
529{
530 if (!src)
531 return NULL;
532
533 static char dest[STR_COMMAND];
534 char *destp = dest;
535 int destsize = 0;
536
537 while (*src && (destsize < sizeof(dest) - 1))
538 {
539 if (*src != '\'')
540 {
541 *destp++ = *src++;
542 destsize++;
543 }
544 else
545 {
546 /* convert ' into '\'' */
547 if ((destsize + 4) < sizeof(dest))
548 {
549 *destp++ = *src++;
550 *destp++ = '\\';
551 *destp++ = '\'';
552 *destp++ = '\'';
553 destsize += 4;
554 }
555 else
556 {
557 break; // LCOV_EXCL_LINE
558 }
559 }
560 }
561 *destp = '\0';
562
563 return dest;
564}
565
571const char *mutt_path_getcwd(struct Buffer *cwd)
572{
573 if (!cwd)
574 return NULL;
575
576 buf_alloc(cwd, PATH_MAX);
577 char *rc = getcwd(cwd->data, cwd->dsize);
578 while (!rc && (errno == ERANGE))
579 {
580 buf_alloc(cwd, cwd->dsize + 256); // LCOV_EXCL_LINE
581 rc = getcwd(cwd->data, cwd->dsize); // LCOV_EXCL_LINE
582 }
583 if (rc)
584 buf_fix_dptr(cwd);
585 else
586 buf_reset(cwd); // LCOV_EXCL_LINE
587
588 return rc;
589}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:194
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition: buffer.c:638
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
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:268
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
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:93
#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:105
const char * mutt_path_basename(const char *path)
Find the last component for a pathname.
Definition: path.c:314
bool mutt_path_tidy(struct Buffer *path, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:165
bool mutt_path_to_absolute(char *path, const char *reference)
Convert relative filepath to an absolute path.
Definition: path.c:402
bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:280
char * mutt_path_escape(const char *src)
Escapes single quotes in a path for a command string.
Definition: path.c:528
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:381
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:54
bool mutt_path_abbr_folder(struct Buffer *path, const char *folder)
Create a folder abbreviation.
Definition: path.c:494
bool mutt_path_parent(struct Buffer *path)
Find the parent of a path.
Definition: path.c:461
size_t mutt_path_realpath(struct Buffer *path)
Resolve path, unraveling symlinks.
Definition: path.c:443
bool mutt_path_tilde(struct Buffer *path, const char *homedir)
Expand '~' in a path.
Definition: path.c:226
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:571
char * mutt_path_concat(char *dest, const char *dir, const char *file, size_t dlen)
Join a directory name and a filename.
Definition: path.c:342
bool mutt_path_pretty(struct Buffer *path, const char *homedir, bool is_dir)
Tidy a filesystem path.
Definition: path.c:188
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
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:497
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:295
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
#define PATH_MAX
Definition: mutt.h:41
Path manipulation functions.
String manipulation functions.
#define STR_COMMAND
Enough space for a long command line.
Definition: string2.h:35
String manipulation buffer.
Definition: buffer.h:34
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35