NeoMutt  2019-12-07-60-g0cfa53
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 
53 bool mutt_path_tidy_slash(char *buf)
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 }
96 
104 bool mutt_path_tidy_dotdot(char *buf)
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 }
155 
163 bool mutt_path_tidy(char *buf)
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 }
173 
183 bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir)
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 }
207 
221 bool mutt_path_canon(char *buf, size_t buflen, const char *homedir)
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 }
301 
307 const char *mutt_path_basename(const char *f)
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 }
317 
329 char *mutt_path_concat(char *d, const char *dir, const char *fname, size_t l)
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 }
342 
354 char *mutt_path_dirname(const char *path)
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 }
363 
375 bool mutt_path_to_absolute(char *path, const char *reference)
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 }
407 
418 size_t mutt_path_realpath(char *buf)
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 }
430 
437 bool mutt_path_parent(char *buf, size_t buflen)
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 }
459 
470 bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
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 }
494 
500 char *mutt_path_escape(const char *src)
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 }
535 
541 const char *mutt_path_getcwd(struct Buffer *cwd)
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 }
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:329
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_path_dirname(const char *path)
Return a path up to, but not including, the final &#39;/&#39;.
Definition: path.c:354
String manipulation buffer.
Definition: buffer.h:33
#define _(a)
Definition: message.h:28
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
char * mutt_str_strncat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:424
Logging Dispatcher.
String manipulation functions.
size_t dsize
Length of data.
Definition: buffer.h:37
bool mutt_path_tidy_dotdot(char *buf)
Remove dot-dot-slash from a path.
Definition: path.c:104
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:307
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
Message logging.
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:541
bool mutt_path_canon(char *buf, size_t buflen, const char *homedir)
Create the canonical version of a path.
Definition: path.c:221
#define PATH_MAX
Definition: mutt.h:50
bool mutt_path_to_absolute(char *path, const char *reference)
Convert relative filepath to an absolute path.
Definition: path.c:375
bool mutt_path_tidy(char *buf)
Remove unnecessary parts of a path.
Definition: path.c:163
char * data
Pointer to data.
Definition: buffer.h:35
size_t mutt_path_realpath(char *buf)
resolve path, unraveling symlinks
Definition: path.c:418
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
Path manipulation functions.
bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
Create a folder abbreviation.
Definition: path.c:470
bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir)
Tidy a filesystem path.
Definition: path.c:183
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
#define STR_COMMAND
Enough space for a long command line.
Definition: string2.h:35
General purpose object for storing and parsing strings.
Log at debug level 1.
Definition: logging.h:40
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
int mutt_str_strncmp(const char *a, const char *b, size_t l)
Compare two strings (to a maximum), safely.
Definition: string.c:642
#define FREE(x)
Definition: memory.h:40
bool mutt_path_tidy_slash(char *buf)
Remove unnecessary slashes and dots.
Definition: path.c:53
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
char * mutt_path_escape(const char *src)
Escapes single quotes in a path for a command string.
Definition: path.c:500
Log at debug level 3.
Definition: logging.h:42
bool mutt_path_parent(char *buf, size_t buflen)
Find the parent of a path.
Definition: path.c:437
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