NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
muttlib.c File Reference

Some miscellaneous functions. More...

#include "config.h"
#include <errno.h>
#include <limits.h>
#include <pwd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "alias/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "muttlib.h"
#include "browser/lib.h"
#include "editor/lib.h"
#include "history/lib.h"
#include "hooks/lib.h"
#include "imap/lib.h"
#include "ncrypt/lib.h"
#include "question/lib.h"
#include "globals.h"
#include "mx.h"
Include dependency graph for muttlib.c:

Go to the source code of this file.

Functions

void mutt_adv_mktemp_cfg (struct Buffer *buf, const char *cfg)
 Create a temporary file.
void expand_path (struct Buffer *buf, bool regex)
 Create the canonical path.
char * mutt_gecos_name (char *dest, size_t destlen, struct passwd *pw)
 Lookup a user's real name in /etc/passwd.
bool mutt_needs_mailcap (struct Body *b)
 Does this type need a mailcap entry do display.
bool mutt_is_text_part (const struct Body *b)
 Is this part of an email in plain text?
void pretty_mailbox (struct Buffer *buf)
 Shorten a mailbox path using '~' or '='.
int mutt_check_overwrite (const char *attname, const char *path, struct Buffer *fname, enum SaveAttach *opt, char **directory)
 Ask the user if overwriting is necessary.
void generate_save_path (struct Buffer *dest, const struct Address *a)
 Make a safe filename from an email address.
FILE * mutt_open_read (const char *path, pid_t *thepid)
 Run a command to read from.
int mutt_save_confirm (const char *s, struct stat *st)
 Ask the user to save.
void mutt_sleep (short s)
 Sleep for a while.
void mutt_encode_path (struct Buffer *buf, const char *src)
 Convert a path to 'us-ascii'.
int mutt_set_xdg_path (enum XdgType type, struct Buffer *buf)
 Find an XDG path or its fallback.
void mutt_get_parent_path (const char *path, char *buf, size_t buflen)
 Find the parent of a path (or mailbox).
void buf_sanitize_filename (struct Buffer *buf, const char *path, short slash)
 Replace unsafe characters in a filename.
int mutt_str_pretty_size (struct Buffer *buf, size_t num)
 Display an abbreviated size, like 3.4K.
void mutt_exit (int code)
 Leave NeoMutt NOW.

Variables

static const char * XdgEnvVars []
 Accepted XDG environment variables.
static const char * XdgDefaults []
 XDG default locations.

Detailed Description

Some miscellaneous functions.

Authors
  • Michael R. Elkins
  • Thomas Roessler
  • Richard Russon
  • Aleksa Sarai
  • Pietro Cerutti
  • Thomas Klausner

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 muttlib.c.

Function Documentation

◆ mutt_adv_mktemp_cfg()

void mutt_adv_mktemp_cfg ( struct Buffer * buf,
const char * cfg )

Create a temporary file.

Parameters
bufBuffer for the name
cfgConfig option for the temp directory

Accept a "suggestion" for file name. If that file exists, then construct one with unique name but keep any extension. This might fail, I guess.

Definition at line 84 of file muttlib.c.

85{
86 if (buf_is_empty(buf))
87 {
88 buf_mktemp_full(buf, "neomutt", NULL, __FILE__, __LINE__, cfg);
89 }
90 else
91 {
92 struct Buffer *prefix = buf_pool_get();
93 buf_strcpy(prefix, buf->data);
94 mutt_file_sanitize_filename(prefix->data, true);
95 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, cfg);
96 buf_printf(buf, "%s/%s", NONULL(c_tmp_dir), buf_string(prefix));
97
98 struct stat st = { 0 };
99 if ((lstat(buf_string(buf), &st) == -1) && (errno == ENOENT))
100 goto out;
101
102 char *suffix = strchr(prefix->data, '.');
103 if (suffix)
104 {
105 *suffix = '\0';
106 suffix++;
107 }
108 buf_mktemp_full(buf, prefix->data, suffix, __FILE__, __LINE__, cfg);
109
110 out:
111 buf_pool_release(&prefix);
112 }
113}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition file.c:582
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
void buf_mktemp_full(struct Buffer *buf, const char *prefix, const char *suffix, const char *src, int line, const char *cfg)
Create a temporary file.
Definition tmp.c:52
Here is the call graph for this function:

◆ expand_path()

void expand_path ( struct Buffer * buf,
bool regex )

Create the canonical path.

Parameters
bufBuffer with path
regexIf true, escape regex special characters
Note
The path is expanded in-place

Definition at line 122 of file muttlib.c.

123{
124 const char *s = NULL;
125 const char *tail = "";
126
127 bool recurse = false;
128
129 struct Buffer *p = buf_pool_get();
130 struct Buffer *q = buf_pool_get();
131 struct Buffer *tmp = buf_pool_get();
132
133 do
134 {
135 recurse = false;
136 s = buf_string(buf);
137
138 switch (*s)
139 {
140 case '~':
141 {
142 if ((s[1] == '/') || (s[1] == '\0'))
143 {
145 tail = s + 1;
146 }
147 else
148 {
149 char *t = strchr(s + 1, '/');
150 if (t)
151 *t = '\0';
152
153 struct passwd *pw = getpwnam(s + 1);
154 if (pw)
155 {
156 buf_strcpy(p, pw->pw_dir);
157 if (t)
158 {
159 *t = '/';
160 tail = t;
161 }
162 else
163 {
164 tail = "";
165 }
166 }
167 else
168 {
169 /* user not found! */
170 if (t)
171 *t = '/';
172 buf_reset(p);
173 tail = s;
174 }
175 }
176 break;
177 }
178
179 case '=':
180 case '+':
181 {
182 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
183 enum MailboxType mb_type = mx_path_probe(c_folder);
184
185 /* if folder = {host} or imap[s]://host/: don't append slash */
186 if ((mb_type == MUTT_IMAP) && ((c_folder[strlen(c_folder) - 1] == '}') ||
187 (c_folder[strlen(c_folder) - 1] == '/')))
188 {
189 buf_strcpy(p, c_folder);
190 }
191 else if (mb_type == MUTT_NOTMUCH)
192 {
193 buf_strcpy(p, c_folder);
194 }
195 else if (c_folder && (c_folder[strlen(c_folder) - 1] == '/'))
196 {
197 buf_strcpy(p, c_folder);
198 }
199 else
200 {
201 buf_printf(p, "%s/", NONULL(c_folder));
202 }
203
204 tail = s + 1;
205 break;
206 }
207
208 /* elm compatibility, @ expands alias to user name */
209
210 case '@':
211 {
212 struct AddressList *al = alias_lookup(s + 1);
213 if (al && !TAILQ_EMPTY(al))
214 {
215 struct Email *e = email_new();
216 e->env = mutt_env_new();
217 mutt_addrlist_copy(&e->env->from, al, false);
218 mutt_addrlist_copy(&e->env->to, al, false);
219
221 mutt_default_save(p, e);
222
223 email_free(&e);
224 /* Avoid infinite recursion if the resulting folder starts with '@' */
225 if (*p->data != '@')
226 recurse = true;
227
228 tail = "";
229 }
230 break;
231 }
232
233 case '>':
234 {
235 const char *const c_mbox = cs_subset_string(NeoMutt->sub, "mbox");
236 buf_strcpy(p, c_mbox);
237 tail = s + 1;
238 break;
239 }
240
241 case '<':
242 {
243 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
244 buf_strcpy(p, c_record);
245 tail = s + 1;
246 break;
247 }
248
249 case '!':
250 {
251 if (s[1] == '!')
252 {
254 tail = s + 2;
255 }
256 else
257 {
258 const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
259 buf_strcpy(p, c_spool_file);
260 tail = s + 1;
261 }
262 break;
263 }
264
265 case '-':
266 {
268 tail = s + 1;
269 break;
270 }
271
272 case '^':
273 {
275 tail = s + 1;
276 break;
277 }
278
279 default:
280 {
281 buf_reset(p);
282 tail = s;
283 }
284 }
285
286 if (regex && *(buf_string(p)) && !recurse)
287 {
289 buf_printf(tmp, "%s%s", buf_string(q), tail);
290 }
291 else
292 {
293 buf_printf(tmp, "%s%s", buf_string(p), tail);
294 }
295
296 buf_copy(buf, tmp);
297 } while (recurse);
298
301 buf_pool_release(&tmp);
302
303 /* Rewrite IMAP path in canonical form - aids in string comparisons of
304 * folders. May possibly fail, in which case buf should be the same. */
305 if (imap_path_probe(buf_string(buf), NULL) == MUTT_IMAP)
306 imap_path_canon(buf);
307}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition address.c:776
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition alias.c:274
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:607
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:342
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
MailboxType
Supported mailbox formats.
Definition mailbox.h:40
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition mailbox.h:50
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition envelope.c:45
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition file.c:624
char * LastFolder
Previously selected mailbox.
Definition globals.c:39
char * CurrentFolder
Currently selected mailbox.
Definition globals.c:38
int imap_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition imap.c:2697
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2683
void mutt_default_save(struct Buffer *path, struct Email *e)
Find the default save path for an email.
Definition exec.c:218
#define PATH_MAX
Definition mutt.h:49
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition mx.c:1326
#define TAILQ_EMPTY(head)
Definition queue.h:778
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
char * home_dir
User's home directory.
Definition neomutt.h:55
Here is the call graph for this function:

◆ mutt_gecos_name()

char * mutt_gecos_name ( char * dest,
size_t destlen,
struct passwd * pw )

Lookup a user's real name in /etc/passwd.

Parameters
destBuffer for the result
destlenLength of buffer
pwPasswd entry
Return values
ptrResult buffer on success

Extract the real name from /etc/passwd's GECOS field. When set, honor the regular expression in $gecos_mask, otherwise assume that the GECOS field is a comma-separated list. Replace "&" by a capitalized version of the user's login name.

Definition at line 321 of file muttlib.c.

322{
323 regmatch_t pat_match[1] = { 0 };
324 char *p = NULL;
325
326 if (!pw || !pw->pw_gecos)
327 return NULL;
328
329 memset(dest, 0, destlen);
330
331 const struct Regex *c_gecos_mask = cs_subset_regex(NeoMutt->sub, "gecos_mask");
332 if (mutt_regex_capture(c_gecos_mask, pw->pw_gecos, 1, pat_match))
333 {
334 mutt_str_copy(dest, pw->pw_gecos + pat_match[0].rm_so,
335 MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
336 }
337 else if ((p = strchr(pw->pw_gecos, ',')))
338 {
339 mutt_str_copy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
340 }
341 else
342 {
343 mutt_str_copy(dest, pw->pw_gecos, destlen);
344 }
345
346 size_t pwnl = strlen(pw->pw_name);
347
348 for (int idx = 0; dest[idx]; idx++)
349 {
350 if (dest[idx] == '&')
351 {
352 memmove(&dest[idx + pwnl], &dest[idx + 1],
353 MAX((ssize_t) (destlen - idx - pwnl - 1), 0));
354 memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
355 dest[idx] = mutt_toupper(dest[idx]);
356 }
357 }
358
359 return dest;
360}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
int mutt_toupper(int arg)
Wrapper for toupper(3).
Definition ctype.c:139
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition regex.c:604
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:587
Cached regular expression.
Definition regex3.h:85
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_needs_mailcap()

bool mutt_needs_mailcap ( struct Body * b)

Does this type need a mailcap entry do display.

Parameters
bAttachment body to be displayed
Return values
trueNeoMutt requires a mailcap entry to display
falseotherwise

Definition at line 368 of file muttlib.c.

369{
370 switch (b->type)
371 {
372 case TYPE_TEXT:
373 if (mutt_istr_equal("plain", b->subtype))
374 return false;
375 break;
376 case TYPE_APPLICATION:
378 return false;
380 return false;
381 break;
382
383 case TYPE_MULTIPART:
384 case TYPE_MESSAGE:
385 return false;
386
387 default:
388 break;
389 }
390
391 return true;
392}
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition crypt.c:610
SecurityFlags mutt_is_application_pgp(const struct Body *b)
Does the message use PGP?
Definition crypt.c:549
@ TYPE_MESSAGE
Type: 'message/*'.
Definition mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition mime.h:38
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:106
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition lib.h:107
#define WithCrypto
Definition lib.h:132
char * subtype
content-type subtype
Definition body.h:61
unsigned int type
content-type primary type, ContentType
Definition body.h:40
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_is_text_part()

bool mutt_is_text_part ( const struct Body * b)

Is this part of an email in plain text?

Parameters
bPart of an email
Return values
truePart is in plain text

Definition at line 399 of file muttlib.c.

400{
401 int t = b->type;
402 char *s = b->subtype;
403
405 return false;
406
407 if (t == TYPE_TEXT)
408 return true;
409
410 if (t == TYPE_MESSAGE)
411 {
412 if (mutt_istr_equal("delivery-status", s))
413 return true;
414 }
415
416 if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
417 {
418 if (mutt_istr_equal("pgp-keys", s))
419 return true;
420 }
421
422 return false;
423}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ pretty_mailbox()

void pretty_mailbox ( struct Buffer * buf)

Shorten a mailbox path using '~' or '='.

Parameters
bufBuffer containing Mailbox name

Collapse the pathname using ~ or = when possible

Definition at line 431 of file muttlib.c.

432{
433 if (!buf || !buf->data)
434 return;
435
436 /* Ensure buffer is large enough for path manipulation operations.
437 * The function will modify the string in-place, potentially shortening it.
438 * buf_alloc() also guarantees buf->data is not NULL. */
439 buf_alloc(buf, PATH_MAX);
440
441 char *p = buf->data;
442 char *q = buf->data;
443 size_t len;
444 enum UrlScheme scheme;
445 char tmp[PATH_MAX] = { 0 };
446
447 scheme = url_check_scheme(buf->data);
448
449 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
450 if ((scheme == U_IMAP) || (scheme == U_IMAPS))
451 {
452 imap_pretty_mailbox(buf->data, buf->dsize, c_folder);
453 goto done;
454 }
455
456 if (scheme == U_NOTMUCH)
457 goto done;
458
459 /* if buf is an url, only collapse path component */
460 if (scheme != U_UNKNOWN)
461 {
462 p = strchr(buf->data, ':') + 1;
463 if (mutt_strn_equal(p, "//", 2))
464 q = strchr(p + 2, '/');
465 if (!q)
466 q = strchr(p, '\0');
467 p = q;
468 }
469
470 /* cleanup path */
471 if (strstr(p, "//") || strstr(p, "/./"))
472 {
473 /* first attempt to collapse the pathname, this is more
474 * lightweight than realpath() and doesn't resolve links */
475 while (*p)
476 {
477 if ((p[0] == '/') && (p[1] == '/'))
478 {
479 *q++ = '/';
480 p += 2;
481 }
482 else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
483 {
484 *q++ = '/';
485 p += 3;
486 }
487 else
488 {
489 *q++ = *p++;
490 }
491 }
492 *q = '\0';
493 }
494 else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
495 realpath(p, tmp))
496 {
497 mutt_str_copy(p, tmp, buf->dsize - (p - buf->data));
498 }
499
500 if ((len = mutt_str_startswith(buf->data, c_folder)) && (buf->data[len] == '/'))
501 {
502 buf->data[0] = '=';
503 memmove(buf->data + 1, buf->data + len, mutt_str_len(buf->data + len) + 1);
504 }
505 else if ((len = mutt_str_startswith(buf->data, NeoMutt->home_dir)) &&
506 (buf->data[len] == '/'))
507 {
508 buf->data[0] = '~';
509 memmove(buf->data + 1, buf->data + len, mutt_str_len(buf->data + len) + 1);
510 }
511
512done:
513 buf_fix_dptr(buf);
514}
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:189
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition util.c:592
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:429
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
size_t dsize
Length of data.
Definition buffer.h:39
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition url.c:229
UrlScheme
All recognised Url types.
Definition url.h:34
@ U_NOTMUCH
Url is notmuch://.
Definition url.h:46
@ U_UNKNOWN
Url wasn't recognised.
Definition url.h:35
@ U_FILE
Url is file://.
Definition url.h:36
@ U_IMAP
Url is imap://.
Definition url.h:39
@ U_IMAPS
Url is imaps://.
Definition url.h:40
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_check_overwrite()

int mutt_check_overwrite ( const char * attname,
const char * path,
struct Buffer * fname,
enum SaveAttach * opt,
char ** directory )

Ask the user if overwriting is necessary.

Parameters
[in]attnameAttachment name
[in]pathPath to save the file
[out]fnameBuffer for filename
[out]optSave option, see SaveAttach
[out]directoryDirectory to save under (OPTIONAL)
Return values
0Success
-1Abort
1Error

Definition at line 527 of file muttlib.c.

529{
530 struct stat st = { 0 };
531
532 buf_strcpy(fname, path);
533 if (access(buf_string(fname), F_OK) != 0)
534 return 0;
535 if (stat(buf_string(fname), &st) != 0)
536 return -1;
537 if (S_ISDIR(st.st_mode))
538 {
539 enum QuadOption ans = MUTT_NO;
540 if (directory)
541 {
542 switch (mw_multi_choice(
543 /* L10N: Means "The path you specified as the destination file is a directory."
544 See the msgid "Save to file: " (alias.c, recvattach.c)
545 These three letters correspond to the choices in the string. */
546 _("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
547 {
548 case 3: /* all */
549 mutt_str_replace(directory, buf_string(fname));
550 break;
551 case 1: /* yes */
552 FREE(directory);
553 break;
554 case -1: /* abort */
555 FREE(directory);
556 return -1;
557 case 2: /* no */
558 FREE(directory);
559 return 1;
560 default:
561 FREE(directory);
562 return -1;
563 }
564 }
565 /* L10N: Means "The path you specified as the destination file is a directory."
566 See the msgid "Save to file: " (alias.c, recvattach.c) */
567 else if ((ans = query_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
568 return (ans == MUTT_NO) ? 1 : -1;
569
570 struct Buffer *tmp = buf_pool_get();
571 buf_strcpy(tmp, mutt_path_basename(NONULL(attname)));
572 struct FileCompletionData cdata = { false, NULL, NULL, NULL, NULL };
573 if ((mw_get_field(_("File under directory: "), tmp, MUTT_COMP_CLEAR,
574 HC_FILE, &CompleteFileOps, &cdata) != 0) ||
575 buf_is_empty(tmp))
576 {
577 buf_pool_release(&tmp);
578 return (-1);
579 }
580 buf_concat_path(fname, path, buf_string(tmp));
581 buf_pool_release(&tmp);
582 }
583
584 if ((*opt == MUTT_SAVE_NONE) && (access(buf_string(fname), F_OK) == 0))
585 {
586 char buf[4096] = { 0 };
587 snprintf(buf, sizeof(buf), "%s - %s", buf_string(fname),
588 // L10N: Options for: File %s exists, (o)verwrite, (a)ppend, or (c)ancel?
589 _("File exists, (o)verwrite, (a)ppend, or (c)ancel?"));
590 switch (mw_multi_choice(buf, _("oac")))
591 {
592 case -1: /* abort */
593 return -1;
594 case 3: /* cancel */
595 return 1;
596
597 case 2: /* append */
598 *opt = MUTT_SAVE_APPEND;
599 break;
600 case 1: /* overwrite */
601 break;
602 default:
603 return -1;
604 }
605 }
606 return 0;
607}
const struct CompleteOps CompleteFileOps
Auto-Completion of Files.
Definition complete.c:152
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition buffer.c:515
@ MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition wdata.h:47
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:502
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition question.c:62
@ HC_FILE
Files.
Definition lib.h:59
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define _(a)
Definition message.h:28
const char * mutt_path_basename(const char *path)
Find the last component for a pathname.
Definition path.c:282
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
@ MUTT_SAVE_APPEND
Append to existing file.
Definition mutt_attach.h:60
@ MUTT_SAVE_NONE
Overwrite existing file (the default).
Definition mutt_attach.h:59
QuadOption
Possible values for a quad-option.
Definition quad.h:36
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
Input for the file completion function.
Definition curs_lib.h:39
Here is the call graph for this function:
Here is the caller graph for this function:

◆ generate_save_path()

void generate_save_path ( struct Buffer * dest,
const struct Address * a )

Make a safe filename from an email address.

Parameters
destBuffer for the result
aAddress to use

The filename will be stripped of unsafe characters (/, space, etc) to make it safe.

Definition at line 616 of file muttlib.c.

617{
618 if (a && a->mailbox)
619 {
620 buf_copy(dest, a->mailbox);
621 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
622 if (!c_save_address)
623 {
624 char *p = strpbrk(dest->data, "%@");
625 if (p)
626 {
627 *p = '\0';
628 buf_fix_dptr(dest);
629 }
630 }
631 mutt_str_lower(dest->data);
632 /* Sanitize filename by replacing unsafe characters */
633 for (char *p = dest->data; *p; p++)
634 if ((*p == '/') || mutt_isspace(*p) || !IsPrint((unsigned char) *p))
635 *p = '_';
636 }
637 else
638 {
639 buf_reset(dest);
640 }
641}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
bool mutt_isspace(int arg)
Wrapper for isspace(3).
Definition ctype.c:96
#define IsPrint(ch)
Definition mbyte.h:39
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition string.c:317
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_open_read()

FILE * mutt_open_read ( const char * path,
pid_t * thepid )

Run a command to read from.

Parameters
[in]pathPath to command
[out]thepidPID of the command
Return values
ptrFile containing output of command

This function allows the user to specify a command to read stdout from in place of a normal file. If the last character in the string is a pipe (|), then we assume it is a command to run instead of a normal file.

Definition at line 653 of file muttlib.c.

654{
655 FILE *fp = NULL;
656 struct stat st = { 0 };
657
658 size_t len = mutt_str_len(path);
659 if (len == 0)
660 {
661 return NULL;
662 }
663
664 if (path[len - 1] == '|')
665 {
666 /* read from a pipe */
667
668 char *p = mutt_str_dup(path);
669
670 p[len - 1] = 0;
671 mutt_endwin();
672 *thepid = filter_create(p, NULL, &fp, NULL, NeoMutt->env);
673 FREE(&p);
674 }
675 else
676 {
677 if (stat(path, &st) < 0)
678 return NULL;
679 if (S_ISDIR(st.st_mode))
680 {
681 errno = EINVAL;
682 return NULL;
683 }
684 fp = mutt_file_fopen(path, "r");
685 *thepid = -1;
686 }
687 return fp;
688}
void mutt_endwin(void)
Shutdown curses.
Definition curs_lib.c:152
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition filter.c:220
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
char ** env
Private copy of the environment variables.
Definition neomutt.h:57
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_save_confirm()

int mutt_save_confirm ( const char * s,
struct stat * st )

Ask the user to save.

Parameters
sSave location
stTimestamp
Return values
0OK to proceed
-1to abort
1to retry

Definition at line 698 of file muttlib.c.

699{
700 int rc = 0;
701
702 enum MailboxType type = mx_path_probe(s);
703
704 if (type == MUTT_POP)
705 {
706 mutt_error(_("Can't save message to POP mailbox"));
707 return 1;
708 }
709
710 if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
711 {
712 const bool c_confirm_append = cs_subset_bool(NeoMutt->sub, "confirm_append");
713 if (c_confirm_append)
714 {
715 struct Buffer *tmp = buf_pool_get();
716 buf_printf(tmp, _("Append messages to %s?"), s);
718 NeoMutt->sub, "confirm_append");
719 if (ans == MUTT_NO)
720 rc = 1;
721 else if (ans == MUTT_ABORT)
722 rc = -1;
723 buf_pool_release(&tmp);
724 }
725 }
726
727 if (type == MUTT_NNTP)
728 {
729 mutt_error(_("Can't save message to news server"));
730 return 0;
731 }
732
733 if (stat(s, st) != -1)
734 {
735 if (type == MUTT_MAILBOX_ERROR)
736 {
737 mutt_error(_("%s is not a mailbox"), s);
738 return 1;
739 }
740 }
741 else if (type != MUTT_IMAP)
742 {
743 st->st_mtime = 0;
744 st->st_atime = 0;
745
746 /* pathname does not exist */
747 if (errno == ENOENT)
748 {
749 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
750 if (c_confirm_create)
751 {
752 struct Buffer *tmp = buf_pool_get();
753 buf_printf(tmp, _("Create %s?"), s);
755 NeoMutt->sub, "confirm_create");
756 if (ans == MUTT_NO)
757 rc = 1;
758 else if (ans == MUTT_ABORT)
759 rc = -1;
760 buf_pool_release(&tmp);
761 }
762
763 /* user confirmed with MUTT_YES or set `$confirm_create` */
764 if (rc == 0)
765 {
766 /* create dir recursively */
767 char *tmp_path = mutt_path_dirname(s);
768 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
769 {
770 /* report failure & abort */
771 mutt_perror("%s", s);
772 FREE(&tmp_path);
773 return 1;
774 }
775 FREE(&tmp_path);
776 }
777 }
778 else
779 {
780 mutt_perror("%s", s);
781 return 1;
782 }
783 }
784
785 msgwin_clear_text(NULL);
786 return rc;
787}
@ MUTT_MAILBOX_ERROR
Error occurred examining Mailbox.
Definition mailbox.h:42
@ MUTT_POP
'POP3' Mailbox type
Definition mailbox.h:51
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition mailbox.h:43
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition file.c:844
#define mutt_error(...)
Definition logging2.h:94
#define mutt_perror(...)
Definition logging2.h:95
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:556
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition path.c:312
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition mx.c:167
@ MUTT_ABORT
User aborted the question (with Ctrl-G).
Definition quad.h:37
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition question.c:357
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_sleep()

void mutt_sleep ( short s)

Sleep for a while.

Parameters
sNumber of seconds to sleep

If the user config '$sleep_time' is larger, sleep that long instead.

Definition at line 795 of file muttlib.c.

796{
797 const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
798 if (c_sleep_time > s)
799 sleep(c_sleep_time);
800 else if (s)
801 sleep(s);
802}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_encode_path()

void mutt_encode_path ( struct Buffer * buf,
const char * src )

Convert a path to 'us-ascii'.

Parameters
bufBuffer for the result
srcPath to convert (OPTIONAL)

If src is NULL, the path in buf will be converted in-place.

Definition at line 811 of file muttlib.c.

812{
813 char *p = mutt_str_dup(src);
814 int rc = mutt_ch_convert_string(&p, cc_charset(), "us-ascii", MUTT_ICONV_NONE);
815 size_t len = buf_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
816
817 /* convert the path to POSIX "Portable Filename Character Set" */
818 for (size_t i = 0; i < len; i++)
819 {
820 if (!mutt_isalnum(buf_at(buf, i)) && !strchr("/.-_", buf_at(buf, i)))
821 {
822 buf->data[i] = '_';
823 }
824 }
825 FREE(&p);
826}
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:674
const char * cc_charset(void)
Get the cached value of $charset.
bool mutt_isalnum(int arg)
Wrapper for isalnum(3).
Definition ctype.c:40
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:819
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_set_xdg_path()

int mutt_set_xdg_path ( enum XdgType type,
struct Buffer * buf )

Find an XDG path or its fallback.

Parameters
typeType of XDG variable, e.g. XDG_CONFIG_HOME
bufBuffer to save path
Return values
1An entry was found that actually exists on disk and 0 otherwise

Process an XDG environment variable or its fallback.

Definition at line 836 of file muttlib.c.

837{
838 const char *xdg_env = mutt_str_getenv(XdgEnvVars[type]);
839 char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(XdgDefaults[type]);
840 char *x = xdg; /* mutt_str_sep() changes xdg, so free x instead later */
841 char *token = NULL;
842 int rc = 0;
843
844 while ((token = mutt_str_sep(&xdg, ":")))
845 {
846 if (buf_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
847 continue;
848 expand_path(buf, false);
849 if (access(buf_string(buf), F_OK) == 0)
850 {
851 rc = 1;
852 break;
853 }
854
855 if (buf_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
856 continue;
857 expand_path(buf, false);
858 if (access(buf_string(buf), F_OK) == 0)
859 {
860 rc = 1;
861 break;
862 }
863 }
864
865 FREE(&x);
866 return rc;
867}
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition string.c:732
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition string.c:190
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
static const char * XdgEnvVars[]
Accepted XDG environment variables.
Definition muttlib.c:64
static const char * XdgDefaults[]
XDG default locations.
Definition muttlib.c:70
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_get_parent_path()

void mutt_get_parent_path ( const char * path,
char * buf,
size_t buflen )

Find the parent of a path (or mailbox).

Parameters
pathPath to use
bufBuffer for the result
buflenLength of buffer

Definition at line 875 of file muttlib.c.

876{
877 enum MailboxType mb_type = mx_path_probe(path);
878
879 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
880 if (mb_type == MUTT_IMAP)
881 {
882 imap_get_parent_path(path, buf, buflen);
883 }
884 else if (mb_type == MUTT_NOTMUCH)
885 {
886 mutt_str_copy(buf, c_folder, buflen);
887 }
888 else
889 {
890 mutt_str_copy(buf, path, buflen);
891 int n = mutt_str_len(buf);
892 if (n == 0)
893 return;
894
895 /* remove any final trailing '/' */
896 if (buf[n - 1] == '/')
897 buf[n - 1] = '\0';
898
899 /* Remove everything until the next slash */
900 for (n--; ((n >= 0) && (buf[n] != '/')); n--)
901 ; // do nothing
902
903 if (n > 0)
904 {
905 buf[n] = '\0';
906 }
907 else
908 {
909 buf[0] = '/';
910 buf[1] = '\0';
911 }
912 }
913}
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition util.c:168
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buf_sanitize_filename()

void buf_sanitize_filename ( struct Buffer * buf,
const char * path,
short slash )

Replace unsafe characters in a filename.

Parameters
bufBuffer for the result
pathFilename to make safe
slashReplace '/' characters too

Definition at line 921 of file muttlib.c.

922{
923 if (!buf || !path)
924 return;
925
926 buf_reset(buf);
927
928 for (; *path; path++)
929 {
930 if ((slash && (*path == '/')) || !strchr(FilenameSafeChars, *path))
931 buf_addch(buf, '_');
932 else
933 buf_addch(buf, *path);
934 }
935}
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
const char FilenameSafeChars[]
Set of characters <=0x7F that are safe to use in filenames.
Definition file.c:71
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_str_pretty_size()

int mutt_str_pretty_size ( struct Buffer * buf,
size_t num )

Display an abbreviated size, like 3.4K.

Parameters
[out]bufBuffer for the result
[in]numNumber to abbreviate
Return values
numBytes written to buf

Definition at line 943 of file muttlib.c.

944{
945 if (!buf)
946 return 0;
947
948 const bool c_size_show_bytes = cs_subset_bool(NeoMutt->sub, "size_show_bytes");
949 const bool c_size_show_fractions = cs_subset_bool(NeoMutt->sub, "size_show_fractions");
950 const bool c_size_show_mb = cs_subset_bool(NeoMutt->sub, "size_show_mb");
951 const bool c_size_units_on_left = cs_subset_bool(NeoMutt->sub, "size_units_on_left");
952
953 if (c_size_show_bytes && (num < 1024))
954 {
955 return buf_add_printf(buf, "%d", (int) num);
956 }
957
958 if (num == 0)
959 {
960 return buf_addstr(buf, c_size_units_on_left ? "K0" : "0K");
961 }
962
963 if (c_size_show_fractions && (num < 10189)) /* 0.1K - 9.9K */
964 {
965 return buf_add_printf(buf, c_size_units_on_left ? "K%3.1f" : "%3.1fK",
966 (num < 103) ? 0.1 : (num / 1024.0));
967 }
968
969 if (!c_size_show_mb || (num < 1023949)) /* 10K - 999K */
970 {
971 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
972 return buf_add_printf(buf, c_size_units_on_left ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
973 }
974
975 if (c_size_show_fractions && (num < 10433332)) /* 1.0M - 9.9M */
976 {
977 return buf_add_printf(buf, c_size_units_on_left ? "M%3.1f" : "%3.1fM", num / 1048576.0);
978 }
979
980 /* 10M+ -- (10433332 + 52428) / 1048576 = 10 */
981 return buf_add_printf(buf, c_size_units_on_left ? ("M%zu") : ("%zuM"),
982 (num + 52428) / 1048576);
983}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:211
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_exit()

void mutt_exit ( int code)

Leave NeoMutt NOW.

Parameters
codeValue to return to the calling environment

Some library routines want to exit immediately on error. By having this in the library, mutt_exit() can be overridden.

See also
main.c

Definition at line 989 of file muttlib.c.

990{
991 mutt_endwin();
992#ifdef USE_DEBUG_BACKTRACE
993 if (code != 0)
995#endif
996 exit(code);
997}
void show_backtrace(void)
Log the program's call stack.
Definition backtrace.c:40
Here is the call graph for this function:

Variable Documentation

◆ XdgEnvVars

const char* XdgEnvVars[]
static
Initial value:
= {
[XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
[XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
}
@ XDG_CONFIG_HOME
XDG home dir: ~/.config.
Definition mutt.h:67
@ XDG_CONFIG_DIRS
XDG system dir: /etc/xdg.
Definition mutt.h:68

Accepted XDG environment variables.

Definition at line 64 of file muttlib.c.

64 {
65 [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
66 [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
67};

◆ XdgDefaults

const char* XdgDefaults[]
static
Initial value:
= {
[XDG_CONFIG_HOME] = "~/.config",
[XDG_CONFIG_DIRS] = "/etc/xdg",
}

XDG default locations.

Definition at line 70 of file muttlib.c.

70 {
71 [XDG_CONFIG_HOME] = "~/.config",
72 [XDG_CONFIG_DIRS] = "/etc/xdg",
73};