NeoMutt  2019-12-07-60-g0cfa53
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)
 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)
 Remove unnecessary parts of a path. More...
 
bool mutt_path_pretty (char *buf, size_t buflen, const char *homedir)
 Tidy a filesystem path. More...
 
bool mutt_path_canon (char *buf, size_t buflen, const char *homedir)
 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)

Remove unnecessary slashes and dots.

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

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

Definition at line 53 of file path.c.

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

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

Remove unnecessary parts of a path.

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

Remove unnecessary dots and slashes from a path

Definition at line 163 of file path.c.

164 {
165  if (!buf || (buf[0] != '/'))
166  return false;
167 
168  if (!mutt_path_tidy_slash(buf))
169  return false;
170 
171  return mutt_path_tidy_dotdot(buf);
172 }
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:104
bool mutt_path_tidy_slash(char *buf)
Remove unnecessary slashes and dots.
Definition: path.c:53
+ 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 
)

Tidy a filesystem path.

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

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

Definition at line 183 of file path.c.

184 {
185  if (!buf)
186  return false;
187 
188  mutt_path_tidy(buf);
189 
190  size_t len = mutt_str_startswith(buf, homedir, CASE_MATCH);
191  if (len == 0)
192  return false;
193 
194  if ((buf[len] != '/') && (buf[len] != '\0'))
195  return false;
196 
197  buf[0] = '~';
198  if (buf[len] == '\0')
199  {
200  buf[1] = '\0';
201  return true;
202  }
203 
204  mutt_str_strfcpy(buf + 1, buf + len, buflen - len);
205  return true;
206 }
Match case when comparing strings.
Definition: string2.h:67
bool mutt_path_tidy(char *buf)
Remove unnecessary parts of a path.
Definition: path.c:163
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
+ 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 
)

Create the canonical version of a path.

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

Remove unnecessary dots and slashes from a path and expand shell '~' abbreviations:

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

Definition at line 221 of file path.c.

222 {
223  if (!buf)
224  return false;
225 
226  char result[PATH_MAX] = { 0 };
227 
228  if (buf[0] == '~')
229  {
230  char *dir = NULL;
231  size_t len = 0;
232 
233  if ((buf[1] == '/') || (buf[1] == '\0'))
234  {
235  if (!homedir)
236  {
237  mutt_debug(LL_DEBUG3, "no homedir\n");
238  return false;
239  }
240 
241  len = mutt_str_strfcpy(result, homedir, sizeof(result));
242  dir = buf + 1;
243  }
244  else
245  {
246  char user[128];
247  dir = strchr(buf + 1, '/');
248  if (dir)
249  mutt_str_strfcpy(user, buf + 1, MIN(dir - buf, (unsigned) sizeof(user)));
250  else
251  mutt_str_strfcpy(user, buf + 1, sizeof(user));
252 
253  struct passwd *pw = getpwnam(user);
254  if (!pw || !pw->pw_dir)
255  {
256  mutt_debug(LL_DEBUG1, "no such user: %s\n", user);
257  return false;
258  }
259 
260  len = mutt_str_strfcpy(result, pw->pw_dir, sizeof(result));
261  }
262 
263  size_t dirlen = mutt_str_strlen(dir);
264  if ((len + dirlen) >= buflen)
265  {
266  mutt_debug(LL_DEBUG3, "result too big for the buffer %ld >= %ld\n",
267  len + dirlen, buflen);
268  return false;
269  }
270 
271  mutt_str_strfcpy(result + len, dir, sizeof(result) - len);
272  mutt_str_strfcpy(buf, result, buflen);
273  }
274  else if (buf[0] != '/')
275  {
276  if (!getcwd(result, sizeof(result)))
277  {
278  mutt_debug(LL_DEBUG1, "getcwd failed: %s (%d)\n", strerror(errno), errno);
279  return false;
280  }
281 
282  size_t cwdlen = mutt_str_strlen(result);
283  size_t dirlen = mutt_str_strlen(buf);
284  if ((cwdlen + dirlen + 1) >= buflen)
285  {
286  mutt_debug(LL_DEBUG3, "result too big for the buffer %ld >= %ld\n",
287  cwdlen + dirlen + 1, buflen);
288  return false;
289  }
290 
291  result[cwdlen] = '/';
292  mutt_str_strfcpy(result + cwdlen + 1, buf, sizeof(result) - cwdlen - 1);
293  mutt_str_strfcpy(buf, result, buflen);
294  }
295 
296  if (!mutt_path_tidy(buf))
297  return false;
298 
299  return true;
300 }
#define MIN(a, b)
Definition: memory.h:31
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
#define PATH_MAX
Definition: mutt.h:50
bool mutt_path_tidy(char *buf)
Remove unnecessary parts of a path.
Definition: path.c:163
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
Log at debug level 1.
Definition: logging.h:40
#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 307 of file path.c.

308 {
309  if (!f)
310  return NULL;
311 
312  const char *p = strrchr(f, '/');
313  if (p)
314  return p + 1;
315  return f;
316 }
+ 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 329 of file path.c.

330 {
331  if (!d || !dir || !fname)
332  return NULL;
333 
334  const char *fmt = "%s/%s";
335 
336  if (!*fname || (*dir && (dir[strlen(dir) - 1] == '/')))
337  fmt = "%s%s";
338 
339  snprintf(d, l, fmt, dir, fname);
340  return d;
341 }
+ 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 354 of file path.c.

355 {
356  if (!path)
357  return NULL;
358 
359  char buf[PATH_MAX] = { 0 };
360  mutt_str_strfcpy(buf, path, sizeof(buf));
361  return mutt_str_strdup(dirname(buf));
362 }
#define PATH_MAX
Definition: mutt.h:50
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
+ 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 375 of file path.c.

376 {
377  if (!path || !reference)
378  return false;
379 
380  char abs_path[PATH_MAX];
381  int path_len;
382 
383  /* if path is already absolute, don't do anything */
384  if ((strlen(path) > 1) && (path[0] == '/'))
385  {
386  return true;
387  }
388 
389  char *dirpath = mutt_path_dirname(reference);
390  mutt_str_strfcpy(abs_path, dirpath, sizeof(abs_path));
391  FREE(&dirpath);
392  mutt_str_strncat(abs_path, sizeof(abs_path), "/", 1); /* append a / at the end of the path */
393 
394  path_len = sizeof(abs_path) - strlen(path);
395 
396  mutt_str_strncat(abs_path, sizeof(abs_path), path, (path_len > 0) ? path_len : 0);
397 
398  path = realpath(abs_path, path);
399  if (!path && (errno != ENOENT))
400  {
401  mutt_perror(_("Error: converting path to absolute"));
402  return false;
403  }
404 
405  return true;
406 }
#define mutt_perror(...)
Definition: logging.h:85
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final &#39;/&#39;.
Definition: path.c:354
#define _(a)
Definition: message.h:28
char * mutt_str_strncat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:424
#define PATH_MAX
Definition: mutt.h:50
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
#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 418 of file path.c.

419 {
420  if (!buf)
421  return 0;
422 
423  char s[PATH_MAX];
424 
425  if (!realpath(buf, s))
426  return 0;
427 
428  return mutt_str_strfcpy(buf, s, sizeof(s));
429 }
#define PATH_MAX
Definition: mutt.h:50
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
+ 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 437 of file path.c.

438 {
439  if (!buf)
440  return false;
441 
442  int n = mutt_str_strlen(buf);
443  if (n < 2)
444  return false;
445 
446  if (buf[n - 1] == '/')
447  n--;
448 
449  // Find the previous '/'
450  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
451  ;
452 
453  if (n == 0) // Always keep at least one '/'
454  n++;
455 
456  buf[n] = '\0';
457  return true;
458 }
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
+ 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 470 of file path.c.

471 {
472  if (!buf || !folder)
473  return false;
474 
475  size_t flen = mutt_str_strlen(folder);
476  if (flen < 2)
477  return false;
478 
479  if (folder[flen - 1] == '/')
480  flen--;
481 
482  if (mutt_str_strncmp(buf, folder, flen) != 0)
483  return false;
484 
485  if (buf[flen + 1] == '\0') // Don't abbreviate to '=/'
486  return false;
487 
488  size_t rlen = mutt_str_strlen(buf + flen + 1);
489 
490  buf[0] = '=';
491  memmove(buf + 1, buf + flen + 1, rlen + 1);
492  return true;
493 }
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
int mutt_str_strncmp(const char *a, const char *b, size_t l)
Compare two strings (to a maximum), safely.
Definition: string.c:642
+ 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 500 of file path.c.

501 {
502  if (!src)
503  return NULL;
504 
505  static char dest[STR_COMMAND];
506  char *destp = dest;
507  int destsize = 0;
508 
509  while (*src && (destsize < sizeof(dest) - 1))
510  {
511  if (*src != '\'')
512  {
513  *destp++ = *src++;
514  destsize++;
515  }
516  else
517  {
518  /* convert ' into '\'' */
519  if (destsize + 4 < sizeof(dest))
520  {
521  *destp++ = *src++;
522  *destp++ = '\\';
523  *destp++ = '\'';
524  *destp++ = '\'';
525  destsize += 4;
526  }
527  else
528  break;
529  }
530  }
531  *destp = '\0';
532 
533  return dest;
534 }
#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 541 of file path.c.

542 {
543  if (!cwd)
544  return NULL;
545 
547  char *retval = getcwd(cwd->data, cwd->dsize);
548  while (!retval && (errno == ERANGE))
549  {
550  mutt_buffer_alloc(cwd, cwd->dsize + 256);
551  retval = getcwd(cwd->data, cwd->dsize);
552  }
553  if (retval)
555  else
556  mutt_buffer_reset(cwd);
557 
558  return retval;
559 }
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:50
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: