NeoMutt  2020-08-07-1-gab41a1
Teaching an old dog new tricks
DOXYGEN
path.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <errno.h>
31 #include <libgen.h>
32 #include <limits.h>
33 #include <pwd.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include "path.h"
40 #include "buffer.h"
41 #include "logging.h"
42 #include "memory.h"
43 #include "message.h"
44 #include "string2.h"
45 
54 bool mutt_path_tidy_slash(char *buf, bool is_dir)
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 }
97 
105 bool mutt_path_tidy_dotdot(char *buf)
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 }
156 
165 bool mutt_path_tidy(char *buf, bool is_dir)
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 }
175 
186 bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir, bool is_dir)
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 }
210 
223 bool mutt_path_tilde(char *buf, size_t buflen, const char *homedir)
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 }
274 
285 bool mutt_path_canon(char *buf, size_t buflen, const char *homedir, bool is_dir)
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 }
323 
329 const char *mutt_path_basename(const char *f)
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 }
339 
351 char *mutt_path_concat(char *d, const char *dir, const char *fname, size_t l)
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 }
364 
376 char *mutt_path_dirname(const char *path)
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 }
385 
397 bool mutt_path_to_absolute(char *path, const char *reference)
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 }
429 
440 size_t mutt_path_realpath(char *buf)
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 }
452 
459 bool mutt_path_parent(char *buf, size_t buflen)
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 }
481 
492 bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
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 }
516 
522 char *mutt_path_escape(const char *src)
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 }
557 
563 const char *mutt_path_getcwd(struct Buffer *cwd)
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
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
Memory management wrappers.
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:419
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
bool mutt_path_parent(char *buf, size_t buflen)
Find the parent of a path.
Definition: path.c:459
static int const char * fmt
Definition: acutest.h:488
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
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
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:563
bool mutt_path_tidy(char *buf, bool is_dir)
Remove unnecessary parts of a path.
Definition: path.c:165
Logging Dispatcher.
bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir, bool is_dir)
Tidy a filesystem path.
Definition: path.c:186
String manipulation functions.
size_t dsize
Length of data.
Definition: buffer.h:37
size_t mutt_path_realpath(char *buf)
resolve path, unraveling symlinks
Definition: path.c:440
bool mutt_path_tilde(char *buf, size_t buflen, const char *homedir)
Expand &#39;~&#39; in a path.
Definition: path.c:223
bool mutt_path_tidy_slash(char *buf, bool is_dir)
Remove unnecessary slashes and dots.
Definition: path.c:54
char * mutt_path_escape(const char *src)
Escapes single quotes in a path for a command string.
Definition: path.c:522
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
Message logging.
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:165
#define PATH_MAX
Definition: mutt.h:44
char * data
Pointer to data.
Definition: buffer.h:35
bool mutt_path_to_absolute(char *path, const char *reference)
Convert relative filepath to an absolute path.
Definition: path.c:397
bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
Create a folder abbreviation.
Definition: path.c:492
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
#define STR_COMMAND
Enough space for a long command line.
Definition: string2.h:35
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:636
General purpose object for storing and parsing strings.
bool mutt_path_canon(char *buf, size_t buflen, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:285
Log at debug level 1.
Definition: logging.h:40
int n
Definition: acutest.h:492
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
char * mutt_path_concat(char *d, const char *dir, const char *fname, size_t l)
Join a directory name and a filename.
Definition: path.c:351
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Path manipulation functions.
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:105
Log at debug level 3.
Definition: logging.h:42
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