NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
path.c File Reference

Path manipulation functions. More...

#include "config.h"
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "path.h"
#include "buffer.h"
#include "logging2.h"
#include "memory.h"
#include "message.h"
#include "string2.h"
+ Include dependency graph for path.c:

Go to the source code of this file.

Functions

bool mutt_path_tidy_slash (char *buf, bool is_dir)
 Remove unnecessary slashes and dots.
 
bool mutt_path_tidy_dotdot (char *buf)
 Remove dot-dot-slash from a path.
 
bool mutt_path_tidy (struct Buffer *path, bool is_dir)
 Remove unnecessary parts of a path.
 
bool mutt_path_pretty (struct Buffer *path, const char *homedir, bool is_dir)
 Tidy a filesystem path.
 
bool mutt_path_tilde (struct Buffer *path, const char *homedir)
 Expand '~' in a path.
 
bool mutt_path_canon (struct Buffer *path, const char *homedir, bool is_dir)
 Create the canonical version of a path.
 
const char * mutt_path_basename (const char *path)
 Find the last component for a pathname.
 
char * mutt_path_concat (char *dest, const char *dir, const char *file, size_t dlen)
 Join a directory name and a filename.
 
char * mutt_path_dirname (const char *path)
 Return a path up to, but not including, the final '/'.
 
bool mutt_path_to_absolute (char *path, const char *reference)
 Convert relative filepath to an absolute path.
 
size_t mutt_path_realpath (struct Buffer *path)
 Resolve path, unraveling symlinks.
 
bool mutt_path_parent (struct Buffer *path)
 Find the parent of a path.
 
bool mutt_path_abbr_folder (struct Buffer *path, const char *folder)
 Create a folder abbreviation.
 
char * mutt_path_escape (const char *src)
 Escapes single quotes in a path for a command string.
 
const char * mutt_path_getcwd (struct Buffer *cwd)
 Get the current working directory.
 

Detailed Description

Path manipulation functions.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file path.c.

Function Documentation

◆ mutt_path_tidy_slash()

bool mutt_path_tidy_slash ( char *  buf,
bool  is_dir 
)

Remove unnecessary slashes and dots.

Parameters
[in,out]bufPath to modify
[in]is_dirShould a trailing / be removed?
Return values
trueSuccess

Collapse repeated '//' and '/./'

Definition at line 54 of file path.c.

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}
+ Here is the caller graph for this function:

◆ mutt_path_tidy_dotdot()

bool mutt_path_tidy_dotdot ( char *  buf)

Remove dot-dot-slash from a path.

Parameters
[in,out]bufPath to modify
Return values
trueSuccess

Collapse dot-dot patterns, like '/dir/../'

Definition at line 105 of file path.c.

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}
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
+ Here is the caller graph for this function:

◆ mutt_path_tidy()

bool mutt_path_tidy ( struct Buffer path,
bool  is_dir 
)

Remove unnecessary parts of a path.

Parameters
[in,out]pathPath to modify
[in]is_dirIs the path a directory?
Return values
trueSuccess

Remove unnecessary dots and slashes from a path

Definition at line 165 of file path.c.

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}
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
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:105
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:54
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_pretty()

bool mutt_path_pretty ( struct Buffer path,
const char *  homedir,
bool  is_dir 
)

Tidy a filesystem path.

Parameters
pathPath to modify
homedirHome directory for '~' substitution
is_dirIs the path a directory?
Return values
trueSuccess

Tidy a path and replace a home directory with '~'

Definition at line 188 of file path.c.

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}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
bool mutt_path_tidy(struct Buffer *path, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:165
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_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_tilde()

bool mutt_path_tilde ( struct Buffer path,
const char *  homedir 
)

Expand '~' in a path.

Parameters
pathPath to modify
homedirHome directory for '~' substitution
Return values
trueSuccess

Behaviour:

  • ~/dir (~ expanded)
  • ~realuser/dir (~realuser expanded)
  • ~nonuser/dir (~nonuser not changed)

Definition at line 226 of file path.c.

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}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define MIN(a, b)
Definition: memory.h:32
#define PATH_MAX
Definition: mutt.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_canon()

bool mutt_path_canon ( struct Buffer path,
const char *  homedir,
bool  is_dir 
)

Create the canonical version of a path.

Parameters
pathPath to modify
homedirHome directory for '~' substitution
is_dirIs the path a directory?
Return values
trueSuccess

Remove unnecessary dots and slashes from a path and expand '~'.

Definition at line 280 of file path.c.

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}
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
bool mutt_path_tilde(struct Buffer *path, const char *homedir)
Expand '~' in a path.
Definition: path.c:226
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_basename()

const char * mutt_path_basename ( const char *  path)

Find the last component for a pathname.

Parameters
pathString to be examined
Return values
ptrPart of pathname after last '/' character
Note
Basename of / is /

Definition at line 314 of file path.c.

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}
+ Here is the caller graph for this function:

◆ mutt_path_concat()

char * mutt_path_concat ( char *  dest,
const char *  dir,
const char *  file,
size_t  dlen 
)

Join a directory name and a filename.

Parameters
destBuffer for the result
dirDirectory name
fileFile name
dlenLength of buffer
Return values
ptrDestination buffer

If both dir and file are supplied, they are separated with '/'. If either is missing, then the other will be copied exactly.

Definition at line 342 of file path.c.

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}
+ Here is the caller graph for this function:

◆ mutt_path_dirname()

char * mutt_path_dirname ( const char *  path)

Return a path up to, but not including, the final '/'.

Parameters
pathPath
Return values
ptrThe directory containing p

Unlike the IEEE Std 1003.1-2001 specification of dirname(3), this implementation does not modify its parameter, so callers need not manually copy their paths into a modifiable buffer prior to calling this function.

Note
Dirname of / is /
The caller must free the returned string

Definition at line 381 of file path.c.

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}
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_to_absolute()

bool mutt_path_to_absolute ( char *  path,
const char *  reference 
)

Convert relative filepath to an absolute path.

Parameters
pathRelative path
referenceAbsolute path that path is relative to
Return values
trueSuccess
falseFailure

Use POSIX functions to convert a path to absolute, relatively to another path

Note
path should be at least of PATH_MAX length

Definition at line 402 of file path.c.

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}
#define mutt_perror(...)
Definition: logging2.h:93
#define FREE(x)
Definition: memory.h:45
#define _(a)
Definition: message.h:28
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:381
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:295
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_realpath()

size_t mutt_path_realpath ( struct Buffer path)

Resolve path, unraveling symlinks.

Parameters
pathBuffer containing path
Return values
numString length of resolved path
0Error, buf is not overwritten

Resolve and overwrite the path in buf.

Definition at line 443 of file path.c.

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}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_parent()

bool mutt_path_parent ( struct Buffer path)

Find the parent of a path.

Parameters
pathBuffer for the result
Return values
trueSuccess

Definition at line 461 of file path.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_abbr_folder()

bool mutt_path_abbr_folder ( struct Buffer path,
const char *  folder 
)

Create a folder abbreviation.

Parameters
pathPath to modify
folderBase path for '=' substitution
Return values
truePath was abbreviated

Abbreviate a path using '=' to represent the 'folder'. If the folder path is passed, it won't be abbreviated to just '='

Definition at line 494 of file path.c.

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}
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_escape()

char * mutt_path_escape ( const char *  src)

Escapes single quotes in a path for a command string.

Parameters
srcthe path to escape
Return values
ptrThe escaped string
Note
Do not free the returned string

Definition at line 528 of file path.c.

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}
#define STR_COMMAND
Enough space for a long command line.
Definition: string2.h:35
+ Here is the caller graph for this function:

◆ mutt_path_getcwd()

const char * mutt_path_getcwd ( struct Buffer cwd)

Get the current working directory.

Parameters
cwdBuffer for the result
Return values
ptrString of current working directory

Definition at line 571 of file path.c.

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}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
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
size_t dsize
Length of data.
Definition: buffer.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function: