NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mailcap.c File Reference

RFC1524 Mailcap routines. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "mailcap.h"
#include "attach/lib.h"
#include "body.h"
#include "muttlib.h"
#include "parameter.h"
+ Include dependency graph for mailcap.c:

Go to the source code of this file.

Functions

int mailcap_expand_command (struct Body *b, const char *filename, const char *type, struct Buffer *command)
 Expand expandos in a command.
 
static char * get_field (char *s)
 NUL terminate a RFC1524 field.
 
static int get_field_text (char *field, char **entry, const char *type, const char *filename, int line)
 Get the matching text from a mailcap.
 
static bool rfc1524_mailcap_parse (struct Body *b, const char *filename, const char *type, struct MailcapEntry *entry, enum MailcapLookup opt)
 Parse a mailcap entry.
 
struct MailcapEntrymailcap_entry_new (void)
 Allocate memory for a new rfc1524 entry.
 
void mailcap_entry_free (struct MailcapEntry **ptr)
 Deallocate an struct MailcapEntry.
 
bool mailcap_lookup (struct Body *b, char *type, size_t typelen, struct MailcapEntry *entry, enum MailcapLookup opt)
 Find given type in the list of mailcap files.
 
void mailcap_expand_filename (const char *nametemplate, const char *oldfile, struct Buffer *newfile)
 Expand a new filename from a template or existing filename.
 

Detailed Description

RFC1524 Mailcap routines.

Authors
  • Richard Russon
  • Federico Kircheis
  • Pietro Cerutti

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

Function Documentation

◆ mailcap_expand_command()

int mailcap_expand_command ( struct Body * b,
const char * filename,
const char * type,
struct Buffer * command )

Expand expandos in a command.

Parameters
bEmail Body
filenameFile containing the email text
typeType, e.g. "text/plain"
commandBuffer containing command
Return values
0Command works on a file
1Command works on a pipe

The command semantics include the following: s is the filename that contains the mail body data t is the content type, like text/plain %{parameter} is replaced by the parameter value from the content-type field % is % Unsupported rfc1524 parameters: these would probably require some doing by neomutt, and can probably just be done by piping the message to metamail n is the integer number of sub-parts in the multipart F is "content-type filename" repeated for each sub-part

Definition at line 70 of file mailcap.c.

72{
73 int needspipe = true;
74 struct Buffer *buf = buf_pool_get();
75 struct Buffer *quoted = buf_pool_get();
76 struct Buffer *param = NULL;
77 struct Buffer *type2 = NULL;
78
79 const bool c_mailcap_sanitize = cs_subset_bool(NeoMutt->sub, "mailcap_sanitize");
80 const char *cptr = buf_string(command);
81 while (*cptr)
82 {
83 if (*cptr == '\\')
84 {
85 cptr++;
86 if (*cptr)
87 buf_addch(buf, *cptr++);
88 }
89 else if (*cptr == '%')
90 {
91 cptr++;
92 if (*cptr == '{')
93 {
94 const char *pvalue2 = NULL;
95
96 if (param)
97 buf_reset(param);
98 else
99 param = buf_pool_get();
100
101 /* Copy parameter name into param buffer */
102 cptr++;
103 while (*cptr && (*cptr != '}'))
104 buf_addch(param, *cptr++);
105
106 /* In send mode, use the current charset, since the message hasn't
107 * been converted yet. If noconv is set, then we assume the
108 * charset parameter has the correct value instead. */
109 if (mutt_istr_equal(buf_string(param), "charset") && b->charset && !b->noconv)
110 pvalue2 = b->charset;
111 else
112 pvalue2 = mutt_param_get(&b->parameter, buf_string(param));
113
114 /* Now copy the parameter value into param buffer */
115 if (c_mailcap_sanitize)
116 buf_sanitize_filename(param, NONULL(pvalue2), false);
117 else
118 buf_strcpy(param, pvalue2);
119
120 buf_quote_filename(quoted, buf_string(param), true);
121 buf_addstr(buf, buf_string(quoted));
122 }
123 else if ((*cptr == 's') && filename)
124 {
125 buf_quote_filename(quoted, filename, true);
126 buf_addstr(buf, buf_string(quoted));
127 needspipe = false;
128 }
129 else if (*cptr == 't')
130 {
131 if (!type2)
132 {
133 type2 = buf_pool_get();
134 if (c_mailcap_sanitize)
135 buf_sanitize_filename(type2, type, false);
136 else
137 buf_strcpy(type2, type);
138 }
139 buf_quote_filename(quoted, buf_string(type2), true);
140 buf_addstr(buf, buf_string(quoted));
141 }
142
143 if (*cptr)
144 cptr++;
145 }
146 else
147 {
148 buf_addch(buf, *cptr++);
149 }
150 }
151 buf_copy(command, buf);
152
153 buf_pool_release(&buf);
154 buf_pool_release(&quoted);
155 buf_pool_release(&param);
156 buf_pool_release(&type2);
157
158 return needspipe;
159}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:601
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
void buf_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
Quote a filename to survive the shell's quoting rules.
Definition file.c:807
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
void buf_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition muttlib.c:912
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition parameter.c:85
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
bool noconv
Don't do character set conversion.
Definition body.h:46
char * charset
Send mode: charset of attached file as stored on disk.
Definition body.h:79
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
String manipulation buffer.
Definition buffer.h:36
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_field()

static char * get_field ( char * s)
static

NUL terminate a RFC1524 field.

Parameters
sString to alter
Return values
ptrStart of next field
NULLError

Definition at line 167 of file mailcap.c.

168{
169 if (!s)
170 return NULL;
171
172 char *ch = NULL;
173
174 while ((ch = strpbrk(s, ";\\")))
175 {
176 if (*ch == '\\')
177 {
178 s = ch + 1;
179 if (*s)
180 s++;
181 }
182 else
183 {
184 *ch = '\0';
185 ch = mutt_str_skip_email_wsp(ch + 1);
186 break;
187 }
188 }
190 return ch;
191}
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:567
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:610
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_field_text()

static int get_field_text ( char * field,
char ** entry,
const char * type,
const char * filename,
int line )
static

Get the matching text from a mailcap.

Parameters
fieldString to parse
entrySave the entry here
typeType, e.g. "text/plain"
filenameMailcap filename
lineMailcap line
Return values
1Success
0Failure

Definition at line 203 of file mailcap.c.

205{
206 field = mutt_str_skip_whitespace(field);
207 if (*field == '=')
208 {
209 if (entry)
210 {
211 field++;
212 field = mutt_str_skip_whitespace(field);
213 mutt_str_replace(entry, field);
214 }
215 return 1;
216 }
217 else
218 {
219 mutt_error(_("Improperly formatted entry for type %s in \"%s\" line %d"),
220 type, filename, line);
221 return 0;
222 }
223}
#define mutt_error(...)
Definition logging2.h:94
#define _(a)
Definition message.h:28
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition string.c:553
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rfc1524_mailcap_parse()

static bool rfc1524_mailcap_parse ( struct Body * b,
const char * filename,
const char * type,
struct MailcapEntry * entry,
enum MailcapLookup opt )
static

Parse a mailcap entry.

Parameters
bEmail Body
filenameFilename
typeType, e.g. "text/plain"
entryEntry, e.g. "compose"
optOption, see MailcapLookup
Return values
trueSuccess
falseFailure

Definition at line 235 of file mailcap.c.

237{
238 char *buf = NULL;
239 bool found = false;
240 int line = 0;
241
242 /* rfc1524 mailcap file is of the format:
243 * base/type; command; extradefs
244 * type can be * for matching all
245 * base with no /type is an implicit wild
246 * command contains a %s for the filename to pass, default to pipe on stdin
247 * extradefs are of the form:
248 * def1="definition"; def2="define \;";
249 * line wraps with a \ at the end of the line
250 * # for comments */
251
252 /* find length of basetype */
253 char *ch = strchr(type, '/');
254 if (!ch)
255 return false;
256 const int btlen = ch - type;
257
258 FILE *fp = mutt_file_fopen(filename, "r");
259 if (fp)
260 {
261 size_t buflen;
262 while (!found && (buf = mutt_file_read_line(buf, &buflen, fp, &line, MUTT_RL_CONT)))
263 {
264 /* ignore comments */
265 if (*buf == '#')
266 continue;
267 mutt_debug(LL_DEBUG2, "mailcap entry: %s\n", buf);
268
269 /* check type */
270 ch = get_field(buf);
271 if (!mutt_istr_equal(buf, type) && (!mutt_istrn_equal(buf, type, btlen) ||
272 ((buf[btlen] != '\0') && /* implicit wild */
273 !mutt_str_equal(buf + btlen, "/*")))) /* wildsubtype */
274 {
275 continue;
276 }
277
278 /* next field is the viewcommand */
279 char *field = ch;
280 ch = get_field(ch);
281 if (entry)
282 entry->command = mutt_str_dup(field);
283
284 /* parse the optional fields */
285 found = true;
286 bool copiousoutput = false;
287 bool composecommand = false;
288 bool editcommand = false;
289 bool printcommand = false;
290
291 while (ch)
292 {
293 field = ch;
294 ch = get_field(ch);
295 mutt_debug(LL_DEBUG2, "field: %s\n", field);
296 size_t plen;
297
298 if (mutt_istr_equal(field, "needsterminal"))
299 {
300 if (entry)
301 entry->needsterminal = true;
302 }
303 else if (mutt_istr_equal(field, "copiousoutput"))
304 {
305 copiousoutput = true;
306 if (entry)
307 entry->copiousoutput = true;
308 }
309 else if ((plen = mutt_istr_startswith(field, "composetyped")))
310 {
311 /* this compare most occur before compose to match correctly */
312 if (get_field_text(field + plen, entry ? &entry->composetypecommand : NULL,
313 type, filename, line))
314 {
315 composecommand = true;
316 }
317 }
318 else if ((plen = mutt_istr_startswith(field, "compose")))
319 {
320 if (get_field_text(field + plen, entry ? &entry->composecommand : NULL,
321 type, filename, line))
322 {
323 composecommand = true;
324 }
325 }
326 else if ((plen = mutt_istr_startswith(field, "print")))
327 {
328 if (get_field_text(field + plen, entry ? &entry->printcommand : NULL,
329 type, filename, line))
330 {
331 printcommand = true;
332 }
333 }
334 else if ((plen = mutt_istr_startswith(field, "edit")))
335 {
336 if (get_field_text(field + plen, entry ? &entry->editcommand : NULL,
337 type, filename, line))
338 {
339 editcommand = true;
340 }
341 }
342 else if ((plen = mutt_istr_startswith(field, "nametemplate")))
343 {
344 get_field_text(field + plen, entry ? &entry->nametemplate : NULL,
345 type, filename, line);
346 }
347 else if ((plen = mutt_istr_startswith(field, "x-convert")))
348 {
349 get_field_text(field + plen, entry ? &entry->convert : NULL, type, filename, line);
350 }
351 else if ((plen = mutt_istr_startswith(field, "test")))
352 {
353 /* This routine executes the given test command to determine
354 * if this is the right entry. */
355 char *test_command = NULL;
356
357 if (get_field_text(field + plen, &test_command, type, filename, line) && test_command)
358 {
359 struct Buffer *command = buf_pool_get();
360 struct Buffer *afilename = buf_pool_get();
361 buf_strcpy(command, test_command);
362 const bool c_mailcap_sanitize = cs_subset_bool(NeoMutt->sub, "mailcap_sanitize");
363 if (c_mailcap_sanitize)
364 buf_sanitize_filename(afilename, NONULL(b->filename), true);
365 else
366 buf_strcpy(afilename, b->filename);
367 if (mailcap_expand_command(b, buf_string(afilename), type, command) == 1)
368 {
369 mutt_debug(LL_DEBUG1, "mailcap command needs a pipe: %s\n",
370 buf_string(command));
371 }
372
373 if (mutt_system(buf_string(command)))
374 {
375 /* a non-zero exit code means test failed */
376 found = false;
377 }
378 FREE(&test_command);
379 buf_pool_release(&command);
380 buf_pool_release(&afilename);
381 }
382 }
383 else if (mutt_istr_startswith(field, "x-neomutt-keep"))
384 {
385 if (entry)
386 entry->xneomuttkeep = true;
387 }
388 else if (mutt_istr_startswith(field, "x-neomutt-nowrap"))
389 {
390 if (entry)
391 entry->xneomuttnowrap = true;
392 b->nowrap = true;
393 }
394 } /* while (ch) */
395
396 if (opt == MUTT_MC_AUTOVIEW)
397 {
398 if (!copiousoutput)
399 found = false;
400 }
401 else if (opt == MUTT_MC_COMPOSE)
402 {
403 if (!composecommand)
404 found = false;
405 }
406 else if (opt == MUTT_MC_EDIT)
407 {
408 if (!editcommand)
409 found = false;
410 }
411 else if (opt == MUTT_MC_PRINT)
412 {
413 if (!printcommand)
414 found = false;
415 }
416
417 if (!found)
418 {
419 /* reset */
420 if (entry)
421 {
422 FREE(&entry->command);
423 FREE(&entry->composecommand);
424 FREE(&entry->composetypecommand);
425 FREE(&entry->editcommand);
426 FREE(&entry->printcommand);
427 FREE(&entry->nametemplate);
428 FREE(&entry->convert);
429 entry->needsterminal = false;
430 entry->copiousoutput = false;
431 entry->xneomuttkeep = false;
432 }
433 }
434 }
435 mutt_file_fclose(&fp);
436 }
437
438 FREE(&buf);
439 return found;
440}
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition file.c:682
#define MUTT_RL_CONT
-continuation
Definition file.h:41
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
static int get_field_text(char *field, char **entry, const char *type, const char *filename, int line)
Get the matching text from a mailcap.
Definition mailcap.c:203
static char * get_field(char *s)
NUL terminate a RFC1524 field.
Definition mailcap.c:167
int mailcap_expand_command(struct Body *b, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition mailcap.c:70
@ MUTT_MC_PRINT
Mailcap print field.
Definition mailcap.h:60
@ MUTT_MC_EDIT
Mailcap edit field.
Definition mailcap.h:58
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition mailcap.h:61
@ MUTT_MC_COMPOSE
Mailcap compose field.
Definition mailcap.h:59
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition string.c:457
int mutt_system(const char *cmd)
Run an external command.
Definition system.c:51
bool nowrap
Do not wrap the output in the pager.
Definition body.h:89
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
char * composecommand
Compose command.
Definition mailcap.h:40
bool needsterminal
endwin() and system
Definition mailcap.h:46
char * nametemplate
Filename template.
Definition mailcap.h:44
char * printcommand
Print command.
Definition mailcap.h:43
char * composetypecommand
Compose type command.
Definition mailcap.h:41
char * editcommand
Edit command.
Definition mailcap.h:42
char * command
Command to run.
Definition mailcap.h:38
bool copiousoutput
needs pager, basically
Definition mailcap.h:47
bool xneomuttkeep
do not remove the file on command exit
Definition mailcap.h:48
char * convert
Conversion command.
Definition mailcap.h:45
bool xneomuttnowrap
do not wrap the output in the pager
Definition mailcap.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mailcap_entry_new()

struct MailcapEntry * mailcap_entry_new ( void )

Allocate memory for a new rfc1524 entry.

Return values
ptrAn un-initialized struct MailcapEntry

Definition at line 446 of file mailcap.c.

447{
448 return MUTT_MEM_CALLOC(1, struct MailcapEntry);
449}
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
A mailcap entry.
Definition mailcap.h:37
+ Here is the caller graph for this function:

◆ mailcap_entry_free()

void mailcap_entry_free ( struct MailcapEntry ** ptr)

Deallocate an struct MailcapEntry.

Parameters
[out]ptrMailcapEntry to deallocate

Definition at line 455 of file mailcap.c.

456{
457 if (!ptr || !*ptr)
458 return;
459
460 struct MailcapEntry *me = *ptr;
461
462 FREE(&me->command);
463 FREE(&me->testcommand);
464 FREE(&me->composecommand);
466 FREE(&me->editcommand);
467 FREE(&me->printcommand);
468 FREE(&me->nametemplate);
469 FREE(ptr);
470}
char * testcommand
Test command.
Definition mailcap.h:39
+ Here is the caller graph for this function:

◆ mailcap_lookup()

bool mailcap_lookup ( struct Body * b,
char * type,
size_t typelen,
struct MailcapEntry * entry,
enum MailcapLookup opt )

Find given type in the list of mailcap files.

Parameters
bMessage body
typeText type in "type/subtype" format
typelenLength of the type
entrystruct MailcapEntry to populate with results
optType of mailcap entry to lookup, see MailcapLookup
Return values
trueIf *entry is not NULL it populates it with the mailcap entry
falseNo matching entry is found

Find the given type in the list of mailcap files.

Definition at line 484 of file mailcap.c.

486{
487 /* rfc1524 specifies that a path of mailcap files should be searched.
488 * joy. They say
489 * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc
490 * and overridden by the MAILCAPS environment variable, and, just to be nice,
491 * we'll make it specifiable in .neomuttrc */
492 const struct Slist *c_mailcap_path = cs_subset_slist(NeoMutt->sub, "mailcap_path");
493 if (!c_mailcap_path || (c_mailcap_path->count == 0))
494 {
495 /* L10N:
496 Mutt is trying to look up a mailcap value, but $mailcap_path is empty.
497 We added a reference to the MAILCAPS environment variable as a hint too.
498
499 Because the variable is automatically populated by Mutt, this
500 should only occur if the user deliberately runs in their shell:
501 export MAILCAPS=
502
503 or deliberately runs inside Mutt or their .muttrc:
504 set mailcap_path=""
505 -or-
506 unset mailcap_path
507 */
508 mutt_error(_("Neither mailcap_path nor MAILCAPS specified"));
509 return false;
510 }
511
512 mutt_check_lookup_list(b, type, typelen);
513
514 struct Buffer *path = buf_pool_get();
515 bool found = false;
516
517 struct ListNode *np = NULL;
518 STAILQ_FOREACH(np, &c_mailcap_path->head, entries)
519 {
520 buf_strcpy(path, np->data);
521 expand_path(path, false);
522
523 mutt_debug(LL_DEBUG2, "Checking mailcap file: %s\n", buf_string(path));
524 found = rfc1524_mailcap_parse(b, buf_string(path), type, entry, opt);
525 if (found)
526 break;
527 }
528
529 buf_pool_release(&path);
530
531 if (entry && !found)
532 mutt_error(_("mailcap entry for type %s not found"), type);
533
534 return found;
535}
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition helpers.c:242
static bool rfc1524_mailcap_parse(struct Body *b, const char *filename, const char *type, struct MailcapEntry *entry, enum MailcapLookup opt)
Parse a mailcap entry.
Definition mailcap.c:235
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:121
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
String list.
Definition slist.h:37
struct ListHead head
List containing values.
Definition slist.h:38
size_t count
Number of values in list.
Definition slist.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mailcap_expand_filename()

void mailcap_expand_filename ( const char * nametemplate,
const char * oldfile,
struct Buffer * newfile )

Expand a new filename from a template or existing filename.

Parameters
nametemplateTemplate
oldfileOriginal filename
newfileBuffer for new filename

If there is no nametemplate, the stripped oldfile name is used as the template for newfile.

If there is no oldfile, the stripped nametemplate name is used as the template for newfile.

If both a nametemplate and oldfile are specified, the template is checked for a "%s". If none is found, the nametemplate is used as the template for newfile. The first path component of the nametemplate and oldfile are ignored.

Definition at line 553 of file mailcap.c.

555{
556 int i, j, k;
557 char *s = NULL;
558 bool lmatch = false, rmatch = false;
559
560 buf_reset(newfile);
561
562 /* first, ignore leading path components */
563
564 if (nametemplate && (s = strrchr(nametemplate, '/')))
565 nametemplate = s + 1;
566
567 if (oldfile && (s = strrchr(oldfile, '/')))
568 oldfile = s + 1;
569
570 if (!nametemplate)
571 {
572 if (oldfile)
573 buf_strcpy(newfile, oldfile);
574 }
575 else if (!oldfile)
576 {
577 mutt_file_expand_fmt(newfile, nametemplate, "neomutt");
578 }
579 else /* oldfile && nametemplate */
580 {
581 /* first, compare everything left from the "%s"
582 * (if there is one). */
583
584 lmatch = true;
585 bool ps = false;
586 for (i = 0; nametemplate[i]; i++)
587 {
588 if ((nametemplate[i] == '%') && (nametemplate[i + 1] == 's'))
589 {
590 ps = true;
591 break;
592 }
593
594 /* note that the following will _not_ read beyond oldfile's end. */
595
596 if (lmatch && (nametemplate[i] != oldfile[i]))
597 lmatch = false;
598 }
599
600 if (ps)
601 {
602 /* If we had a "%s", check the rest. */
603
604 /* now, for the right part: compare everything right from
605 * the "%s" to the final part of oldfile.
606 *
607 * The logic here is as follows:
608 *
609 * - We start reading from the end.
610 * - There must be a match _right_ from the "%s",
611 * thus the i + 2.
612 * - If there was a left hand match, this stuff
613 * must not be counted again. That's done by the
614 * condition (j >= (lmatch ? i : 0)). */
615
616 rmatch = true;
617
618 for (j = mutt_str_len(oldfile) - 1, k = mutt_str_len(nametemplate) - 1;
619 (j >= (lmatch ? i : 0)) && (k >= (i + 2)); j--, k--)
620 {
621 if (nametemplate[k] != oldfile[j])
622 {
623 rmatch = false;
624 break;
625 }
626 }
627
628 /* Now, check if we had a full match. */
629
630 if (k >= i + 2)
631 rmatch = false;
632
633 struct Buffer *left = buf_pool_get();
634 struct Buffer *right = buf_pool_get();
635
636 if (!lmatch)
637 buf_strcpy_n(left, nametemplate, i);
638 if (!rmatch)
639 buf_strcpy(right, nametemplate + i + 2);
640 buf_printf(newfile, "%s%s%s", buf_string(left), oldfile, buf_string(right));
641
642 buf_pool_release(&left);
643 buf_pool_release(&right);
644 }
645 else
646 {
647 /* no "%s" in the name template. */
648 buf_strcpy(newfile, nametemplate);
649 }
650 }
651
652 mutt_adv_mktemp(newfile);
653}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
size_t buf_strcpy_n(struct Buffer *buf, const char *s, size_t len)
Copy a string into a Buffer.
Definition buffer.c:416
void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition file.c:1361
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition muttlib.c:83
+ Here is the call graph for this function:
+ Here is the caller graph for this function: