NeoMutt  2024-03-23-147-g885fbc
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_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 relative filepath to an absolute path.
 
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 57 of file path.c.

58{
59 if (!buf)
60 return false;
61
62 char *r = buf;
63 char *w = buf;
64
65 while (*r != '\0')
66 {
67 *w++ = *r++;
68
69 if (r[-1] == '/') /* After a '/' ... */
70 {
71 for (; (r[0] == '/') || (r[0] == '.'); r++)
72 {
73 if (r[0] == '/') /* skip multiple /s */
74 continue;
75 if (r[0] == '.')
76 {
77 if (r[1] == '/') /* skip /./ */
78 {
79 r++;
80 continue;
81 }
82 else if (r[1] == '\0') /* skip /.$ */
83 {
84 r[0] = '\0';
85 }
86 break; /* dot-anything-else isn't special */
87 }
88 }
89 }
90 }
91
92 /* Strip a trailing / as long as it's not the only character */
93 if (is_dir && (w > (buf + 1)) && (w[-1] == '/'))
94 w--;
95
96 *w = '\0';
97
98 return true;
99}
+ 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 108 of file path.c.

109{
110 if (!buf || (buf[0] != '/'))
111 return false;
112
113 char *dd = buf;
114
115 mutt_debug(LL_DEBUG3, "Collapse path: %s\n", buf);
116 while ((dd = strstr(dd, "/..")))
117 {
118 if (dd[3] == '/') /* paths follow dots */
119 {
120 char *dest = NULL;
121 if (dd != buf) /* not at start of string */
122 {
123 dd[0] = '\0';
124 dest = strrchr(buf, '/');
125 }
126 if (!dest)
127 dest = buf;
128
129 memmove(dest, dd + 3, strlen(dd + 3) + 1);
130 }
131 else if (dd[3] == '\0') /* dots at end of string */
132 {
133 if (dd == buf) /* at start of string */
134 {
135 dd[1] = '\0';
136 }
137 else
138 {
139 dd[0] = '\0';
140 char *s = strrchr(buf, '/');
141 if (s == buf)
142 s[1] = '\0';
143 else if (s)
144 s[0] = '\0';
145 }
146 }
147 else
148 {
149 dd += 3; /* skip over '/..dir/' */
150 continue;
151 }
152
153 dd = buf; /* restart at the beginning */
154 }
155
156 mutt_debug(LL_DEBUG3, "Collapsed to: %s\n", buf);
157 return true;
158}
#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 168 of file path.c.

169{
170 if (buf_is_empty(path) || (buf_at(path, 0) != '/'))
171 return false;
172
173 if (!mutt_path_tidy_slash(path->data, is_dir))
174 return false; // LCOV_EXCL_LINE
175
177 buf_fix_dptr(path);
178
179 return true;
180}
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
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:108
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:57
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 193 of file path.c.

194{
195 if (buf_is_empty(path) || (buf_at(path, 0) != '~'))
196 return false;
197
198 char result[PATH_MAX] = { 0 };
199 char *dir = NULL;
200 size_t len = 0;
201
202 if ((buf_at(path, 1) == '/') || (buf_at(path, 1) == '\0'))
203 {
204 if (!homedir)
205 {
206 mutt_debug(LL_DEBUG3, "no homedir\n");
207 return false;
208 }
209
210 len = mutt_str_copy(result, homedir, sizeof(result));
211 dir = path->data + 1;
212 }
213 else
214 {
215 char user[128] = { 0 };
216 dir = strchr(path->data + 1, '/');
217 if (dir)
218 mutt_str_copy(user, path->data + 1, MIN(dir - path->data, (unsigned) sizeof(user)));
219 else
220 mutt_str_copy(user, path->data + 1, sizeof(user));
221
222 struct passwd *pw = getpwnam(user);
223 if (!pw || !pw->pw_dir)
224 {
225 mutt_debug(LL_DEBUG1, "no such user: %s\n", user);
226 return false;
227 }
228
229 len = mutt_str_copy(result, pw->pw_dir, sizeof(result));
230 }
231
232 mutt_str_copy(result + len, dir, sizeof(result) - len);
233 buf_strcpy(path, result);
234
235 return true;
236}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
@ 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:630
#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 247 of file path.c.

248{
249 if (buf_is_empty(path))
250 return false;
251
252 if (buf_at(path, 0) == '~')
253 {
254 mutt_path_tilde(path, homedir);
255 }
256 else if (buf_at(path, 0) != '/')
257 {
258 char cwd[PATH_MAX] = { 0 };
259 if (!getcwd(cwd, sizeof(cwd)))
260 {
261 mutt_debug(LL_DEBUG1, "getcwd failed: %s (%d)\n", strerror(errno), errno); // LCOV_EXCL_LINE
262 return false; // LCOV_EXCL_LINE
263 }
264
265 size_t cwd_len = mutt_str_len(cwd);
266 cwd[cwd_len] = '/';
267 cwd[cwd_len + 1] = '\0';
268 buf_insert(path, 0, cwd);
269 }
270
271 return mutt_path_tidy(path, is_dir);
272}
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
bool mutt_path_tidy(struct Buffer *path, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:168
bool mutt_path_tilde(struct Buffer *path, const char *homedir)
Expand '~' in a path.
Definition: path.c:193
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:545
+ 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 281 of file path.c.

282{
283 if (!path)
284 return NULL;
285
286 const char *p = strrchr(path, '/');
287 if (p)
288 {
289 if (p[1] == '\0')
290 return path;
291
292 return p + 1;
293 }
294
295 return path;
296}
+ 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 311 of file path.c.

312{
313 if (!path)
314 return NULL;
315
316 char buf[PATH_MAX] = { 0 };
317 mutt_str_copy(buf, path, sizeof(buf));
318 return mutt_str_dup(dirname(buf));
319}
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 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 332 of file path.c.

333{
334 if (!path || !reference)
335 return false;
336
337 char abs_path[PATH_MAX] = { 0 };
338 int path_len;
339
340 /* if path is already absolute, don't do anything */
341 if ((strlen(path) > 1) && (path[0] == '/'))
342 {
343 return true;
344 }
345
346 char *dirpath = mutt_path_dirname(reference);
347 mutt_str_copy(abs_path, dirpath, sizeof(abs_path));
348 FREE(&dirpath);
349 mutt_strn_cat(abs_path, sizeof(abs_path), "/", 1); /* append a / at the end of the path */
350
351 path_len = sizeof(abs_path) - strlen(path);
352
353 mutt_strn_cat(abs_path, sizeof(abs_path), path, (path_len > 0) ? path_len : 0);
354
355 path = realpath(abs_path, path);
356 if (!path && (errno != ENOENT))
357 {
358 mutt_perror(_("Error: converting path to absolute"));
359 return false;
360 }
361
362 return true;
363}
#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:311
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:297
+ 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 373 of file path.c.

374{
375 if (buf_is_empty(path))
376 return 0;
377
378 char s[PATH_MAX] = { 0 };
379
380 if (!realpath(buf_string(path), s))
381 return 0;
382
383 return buf_strcpy(path, s);
384}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
+ 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 395 of file path.c.

396{
397 if (buf_is_empty(path) || !folder)
398 return false;
399
400 size_t flen = mutt_str_len(folder);
401 if (flen < 2)
402 return false;
403
404 if (folder[flen - 1] == '/')
405 flen--;
406
407 if (!mutt_strn_equal(buf_string(path), folder, flen))
408 return false;
409
410 if (buf_at(path, flen + 1) == '\0') // Don't abbreviate to '=/'
411 return false;
412
413 size_t rlen = mutt_str_len(path->data + flen + 1);
414
415 path->data[0] = '=';
416 memmove(path->data + 1, path->data + flen + 1, rlen + 1);
417 buf_fix_dptr(path);
418
419 return true;
420}
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:474
+ 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 429 of file path.c.

430{
431 if (!src)
432 return NULL;
433
434 static char dest[STR_COMMAND];
435 char *destp = dest;
436 int destsize = 0;
437
438 while (*src && (destsize < sizeof(dest) - 1))
439 {
440 if (*src != '\'')
441 {
442 *destp++ = *src++;
443 destsize++;
444 }
445 else
446 {
447 /* convert ' into '\'' */
448 if ((destsize + 4) < sizeof(dest))
449 {
450 *destp++ = *src++;
451 *destp++ = '\\';
452 *destp++ = '\'';
453 *destp++ = '\'';
454 destsize += 4;
455 }
456 else
457 {
458 break; // LCOV_EXCL_LINE
459 }
460 }
461 }
462 *destp = '\0';
463
464 return dest;
465}
#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 472 of file path.c.

473{
474 if (!cwd)
475 return NULL;
476
477 buf_alloc(cwd, PATH_MAX);
478 char *rc = getcwd(cwd->data, cwd->dsize);
479 while (!rc && (errno == ERANGE))
480 {
481 buf_alloc(cwd, cwd->dsize + 256); // LCOV_EXCL_LINE
482 rc = getcwd(cwd->data, cwd->dsize); // LCOV_EXCL_LINE
483 }
484 if (rc)
485 buf_fix_dptr(cwd);
486 else
487 buf_reset(cwd); // LCOV_EXCL_LINE
488
489 return rc;
490}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:75
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
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: