NeoMutt  2020-08-07-1-gab41a1
Teaching an old dog new tricks
DOXYGEN
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 "logging.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. More...
 
bool mutt_path_tidy_dotdot (char *buf)
 Remove dot-dot-slash from a path. More...
 
bool mutt_path_tidy (char *buf, bool is_dir)
 Remove unnecessary parts of a path. More...
 
bool mutt_path_pretty (char *buf, size_t buflen, const char *homedir, bool is_dir)
 Tidy a filesystem path. More...
 
bool mutt_path_tilde (char *buf, size_t buflen, const char *homedir)
 Expand '~' in a path. More...
 
bool mutt_path_canon (char *buf, size_t buflen, const char *homedir, bool is_dir)
 Create the canonical version of a path. More...
 
const char * mutt_path_basename (const char *f)
 Find the last component for a pathname. More...
 
char * mutt_path_concat (char *d, const char *dir, const char *fname, size_t l)
 Join a directory name and a filename. More...
 
char * mutt_path_dirname (const char *path)
 Return a path up to, but not including, the final '/'. More...
 
bool mutt_path_to_absolute (char *path, const char *reference)
 Convert relative filepath to an absolute path. More...
 
size_t mutt_path_realpath (char *buf)
 resolve path, unraveling symlinks More...
 
bool mutt_path_parent (char *buf, size_t buflen)
 Find the parent of a path. More...
 
bool mutt_path_abbr_folder (char *buf, size_t buflen, const char *folder)
 Create a folder abbreviation. More...
 
char * mutt_path_escape (const char *src)
 Escapes single quotes in a path for a command string. More...
 
const char * mutt_path_getcwd (struct Buffer *cwd)
 Get the current working directory. More...
 

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: logging.h:81
Log at debug level 3.
Definition: logging.h:42
+ Here is the caller graph for this function:

◆ mutt_path_tidy()

bool mutt_path_tidy ( char *  buf,
bool  is_dir 
)

Remove unnecessary parts of a path.

Parameters
[in,out]bufPath 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 || (buf[0] != '/'))
168  return false;
169 
170  if (!mutt_path_tidy_slash(buf, is_dir))
171  return false;
172 
173  return mutt_path_tidy_dotdot(buf);
174 }
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:54
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:105
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_pretty()

bool mutt_path_pretty ( char *  buf,
size_t  buflen,
const char *  homedir,
bool  is_dir 
)

Tidy a filesystem path.

Parameters
bufPath to modify
buflenLength of the buffer
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 186 of file path.c.

187 {
188  if (!buf)
189  return false;
190 
191  mutt_path_tidy(buf, is_dir);
192 
193  size_t len = mutt_str_startswith(buf, homedir);
194  if (len == 0)
195  return false;
196 
197  if ((buf[len] != '/') && (buf[len] != '\0'))
198  return false;
199 
200  buf[0] = '~';
201  if (buf[len] == '\0')
202  {
203  buf[1] = '\0';
204  return true;
205  }
206 
207  mutt_str_copy(buf + 1, buf + len, buflen - len);
208  return true;
209 }
bool mutt_path_tidy(char *buf, 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:165
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:721
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_tilde()

bool mutt_path_tilde ( char *  buf,
size_t  buflen,
const char *  homedir 
)

Expand '~' in a path.

Parameters
bufPath to modify
buflenLength of the buffer
homedirHome directory for '~' substitution
Return values
trueSuccess

Behaviour:

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

Definition at line 223 of file path.c.

224 {
225  if (!buf || (buf[0] != '~'))
226  return false;
227 
228  char result[PATH_MAX] = { 0 };
229  char *dir = NULL;
230  size_t len = 0;
231 
232  if ((buf[1] == '/') || (buf[1] == '\0'))
233  {
234  if (!homedir)
235  {
236  mutt_debug(LL_DEBUG3, "no homedir\n");
237  return false;
238  }
239 
240  len = mutt_str_copy(result, homedir, sizeof(result));
241  dir = buf + 1;
242  }
243  else
244  {
245  char user[128];
246  dir = strchr(buf + 1, '/');
247  if (dir)
248  mutt_str_copy(user, buf + 1, MIN(dir - buf, (unsigned) sizeof(user)));
249  else
250  mutt_str_copy(user, buf + 1, sizeof(user));
251 
252  struct passwd *pw = getpwnam(user);
253  if (!pw || !pw->pw_dir)
254  {
255  mutt_debug(LL_DEBUG1, "no such user: %s\n", user);
256  return false;
257  }
258 
259  len = mutt_str_copy(result, pw->pw_dir, sizeof(result));
260  }
261 
262  size_t dirlen = mutt_str_len(dir);
263  if ((len + dirlen) >= buflen)
264  {
265  mutt_debug(LL_DEBUG3, "result too big for the buffer %ld >= %ld\n", len + dirlen, buflen);
266  return false;
267  }
268 
269  mutt_str_copy(result + len, dir, sizeof(result) - len);
270  mutt_str_copy(buf, result, buflen);
271 
272  return true;
273 }
#define MIN(a, b)
Definition: memory.h:31
#define PATH_MAX
Definition: mutt.h:44
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
Log at debug level 1.
Definition: logging.h:40
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:721
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 3.
Definition: logging.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 ( char *  buf,
size_t  buflen,
const char *  homedir,
bool  is_dir 
)

Create the canonical version of a path.

Parameters
bufPath to modify
buflenLength of the buffer
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 285 of file path.c.

286 {
287  if (!buf)
288  return false;
289 
290  char result[PATH_MAX] = { 0 };
291 
292  if (buf[0] == '~')
293  {
294  mutt_path_tilde(buf, buflen, homedir);
295  }
296  else if (buf[0] != '/')
297  {
298  if (!getcwd(result, sizeof(result)))
299  {
300  mutt_debug(LL_DEBUG1, "getcwd failed: %s (%d)\n", strerror(errno), errno);
301  return false;
302  }
303 
304  size_t cwdlen = mutt_str_len(result);
305  size_t dirlen = mutt_str_len(buf);
306  if ((cwdlen + dirlen + 1) >= buflen)
307  {
308  mutt_debug(LL_DEBUG3, "result too big for the buffer %ld >= %ld\n",
309  cwdlen + dirlen + 1, buflen);
310  return false;
311  }
312 
313  result[cwdlen] = '/';
314  mutt_str_copy(result + cwdlen + 1, buf, sizeof(result) - cwdlen - 1);
315  mutt_str_copy(buf, result, buflen);
316  }
317 
318  if (!mutt_path_tidy(buf, is_dir))
319  return false;
320 
321  return true;
322 }
bool mutt_path_tidy(char *buf, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:165
bool mutt_path_tilde(char *buf, size_t buflen, const char *homedir)
Expand &#39;~&#39; in a path.
Definition: path.c:223
#define PATH_MAX
Definition: mutt.h:44
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
Log at debug level 1.
Definition: logging.h:40
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:721
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Log at debug level 3.
Definition: logging.h:42
+ 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 *  f)

Find the last component for a pathname.

Parameters
fString to be examined
Return values
ptrPart of pathname after last '/' character

Definition at line 329 of file path.c.

330 {
331  if (!f)
332  return NULL;
333 
334  const char *p = strrchr(f, '/');
335  if (p)
336  return p + 1;
337  return f;
338 }
+ Here is the caller graph for this function:

◆ mutt_path_concat()

char* mutt_path_concat ( char *  d,
const char *  dir,
const char *  fname,
size_t  l 
)

Join a directory name and a filename.

Parameters
dBuffer for the result
dirDirectory name
fnameFile name
lLength of buffer
Return values
ptrDestination buffer

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

Definition at line 351 of file path.c.

352 {
353  if (!d || !dir || !fname)
354  return NULL;
355 
356  const char *fmt = "%s/%s";
357 
358  if ((fname[0] == '\0') || ((dir[0] != '\0') && (dir[strlen(dir) - 1] == '/')))
359  fmt = "%s%s";
360 
361  snprintf(d, l, fmt, dir, fname);
362  return d;
363 }
static int const char * fmt
Definition: acutest.h:488
+ 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
The caller must free the returned string

Definition at line 376 of file path.c.

377 {
378  if (!path)
379  return NULL;
380 
381  char buf[PATH_MAX] = { 0 };
382  mutt_str_copy(buf, path, sizeof(buf));
383  return mutt_str_dup(dirname(buf));
384 }
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
#define PATH_MAX
Definition: mutt.h:44
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:721
+ 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 397 of file path.c.

398 {
399  if (!path || !reference)
400  return false;
401 
402  char abs_path[PATH_MAX];
403  int path_len;
404 
405  /* if path is already absolute, don't do anything */
406  if ((strlen(path) > 1) && (path[0] == '/'))
407  {
408  return true;
409  }
410 
411  char *dirpath = mutt_path_dirname(reference);
412  mutt_str_copy(abs_path, dirpath, sizeof(abs_path));
413  FREE(&dirpath);
414  mutt_strn_cat(abs_path, sizeof(abs_path), "/", 1); /* append a / at the end of the path */
415 
416  path_len = sizeof(abs_path) - strlen(path);
417 
418  mutt_strn_cat(abs_path, sizeof(abs_path), path, (path_len > 0) ? path_len : 0);
419 
420  path = realpath(abs_path, path);
421  if (!path && (errno != ENOENT))
422  {
423  mutt_perror(_("Error: converting path to absolute"));
424  return false;
425  }
426 
427  return true;
428 }
#define mutt_perror(...)
Definition: logging.h:85
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:419
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final &#39;/&#39;.
Definition: path.c:376
#define _(a)
Definition: message.h:28
#define PATH_MAX
Definition: mutt.h:44
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:721
#define FREE(x)
Definition: memory.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_realpath()

size_t mutt_path_realpath ( char *  buf)

resolve path, unraveling symlinks

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

Resolve and overwrite the path in buf.

Note
Size of buf should be at least PATH_MAX bytes.

Definition at line 440 of file path.c.

441 {
442  if (!buf)
443  return 0;
444 
445  char s[PATH_MAX];
446 
447  if (!realpath(buf, s))
448  return 0;
449 
450  return mutt_str_copy(buf, s, sizeof(s));
451 }
#define PATH_MAX
Definition: mutt.h:44
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:721
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_path_parent()

bool mutt_path_parent ( char *  buf,
size_t  buflen 
)

Find the parent of a path.

Parameters
bufBuffer for the result
buflenLength of buffer
Return values
trueSuccess

Definition at line 459 of file path.c.

460 {
461  if (!buf)
462  return false;
463 
464  int n = mutt_str_len(buf);
465  if (n < 2)
466  return false;
467 
468  if (buf[n - 1] == '/')
469  n--;
470 
471  // Find the previous '/'
472  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
473  ; // do nothing
474 
475  if (n == 0) // Always keep at least one '/'
476  n++;
477 
478  buf[n] = '\0';
479  return true;
480 }
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
int n
Definition: acutest.h:492
+ 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 ( char *  buf,
size_t  buflen,
const char *  folder 
)

Create a folder abbreviation.

Parameters
bufPath to modify
buflenLength of buffer
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 492 of file path.c.

493 {
494  if (!buf || !folder)
495  return false;
496 
497  size_t flen = mutt_str_len(folder);
498  if (flen < 2)
499  return false;
500 
501  if (folder[flen - 1] == '/')
502  flen--;
503 
504  if (!mutt_strn_equal(buf, folder, flen))
505  return false;
506 
507  if (buf[flen + 1] == '\0') // Don't abbreviate to '=/'
508  return false;
509 
510  size_t rlen = mutt_str_len(buf + flen + 1);
511 
512  buf[0] = '=';
513  memmove(buf + 1, buf + flen + 1, rlen + 1);
514  return true;
515 }
bool mutt_strn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:598
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
+ 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

Definition at line 522 of file path.c.

523 {
524  if (!src)
525  return NULL;
526 
527  static char dest[STR_COMMAND];
528  char *destp = dest;
529  int destsize = 0;
530 
531  while (*src && (destsize < sizeof(dest) - 1))
532  {
533  if (*src != '\'')
534  {
535  *destp++ = *src++;
536  destsize++;
537  }
538  else
539  {
540  /* convert ' into '\'' */
541  if (destsize + 4 < sizeof(dest))
542  {
543  *destp++ = *src++;
544  *destp++ = '\\';
545  *destp++ = '\'';
546  *destp++ = '\'';
547  destsize += 4;
548  }
549  else
550  break;
551  }
552  }
553  *destp = '\0';
554 
555  return dest;
556 }
#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 563 of file path.c.

564 {
565  if (!cwd)
566  return NULL;
567 
569  char *retval = getcwd(cwd->data, cwd->dsize);
570  while (!retval && (errno == ERANGE))
571  {
572  mutt_buffer_alloc(cwd, cwd->dsize + 256);
573  retval = getcwd(cwd->data, cwd->dsize);
574  }
575  if (retval)
577  else
578  mutt_buffer_reset(cwd);
579 
580  return retval;
581 }
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
size_t dsize
Length of data.
Definition: buffer.h:37
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
#define PATH_MAX
Definition: mutt.h:44
char * data
Pointer to data.
Definition: buffer.h:35
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
+ Here is the call graph for this function:
+ Here is the caller graph for this function: