NeoMutt  2024-10-02-37-gfa9146
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 "pool.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_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_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 a relative path to its absolute form.
 
size_t mutt_path_realpath (struct Buffer *path)
 Resolve path, unraveling symlinks.
 
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
  • Ian Zimmerman
  • Richard Russon
  • Pietro Cerutti
  • Dennis Schön

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 58 of file path.c.

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}
+ 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 109 of file path.c.

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}
#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 169 of file path.c.

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}
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
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:109
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:58
char * data
Pointer to data.
Definition: buffer.h:37
+ 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 194 of file path.c.

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}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define MIN(a, b)
Definition: memory.h:32
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
+ 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 248 of file path.c.

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}
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
bool mutt_path_tidy(struct Buffer *path, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:169
bool mutt_path_tilde(struct Buffer *path, const char *homedir)
Expand '~' in a path.
Definition: path.c:194
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
+ 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 282 of file path.c.

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}
+ 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 312 of file path.c.

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}
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
+ 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 a relative path to its absolute form.

Parameters
[in,out]pathRelative path
[in]referenceAbsolute path that path is relative to
Return values
trueSuccess, path was changed into its absolute form
falseFailure, path is untouched

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

Note
path should be able to store PATH_MAX characters + the terminator.

Definition at line 333 of file path.c.

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}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
#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:312
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
String manipulation buffer.
Definition: buffer.h:36
+ 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 377 of file path.c.

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}
+ 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 399 of file path.c.

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}
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
+ Here is the call 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 433 of file path.c.

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}
#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 476 of file path.c.

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