NeoMutt  2023-05-17-33-gce4425
Teaching an old dog new tricks
DOXYGEN
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 "email/lib.h"
#include "core/lib.h"
#include "mailcap.h"
#include "attach/lib.h"
#include "muttlib.h"
#include "protos.h"
+ Include dependency graph for mailcap.c:

Go to the source code of this file.

Functions

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

Detailed Description

RFC1524 Mailcap routines.

Authors
  • Michael R. Elkins

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 a,
const char *  filename,
const char *  type,
struct Buffer command 
)

Expand expandos in a command.

Parameters
aEmail 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 67 of file mailcap.c.

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

165{
166 if (!s)
167 return NULL;
168
169 char *ch = NULL;
170
171 while ((ch = strpbrk(s, ";\\")))
172 {
173 if (*ch == '\\')
174 {
175 s = ch + 1;
176 if (*s)
177 s++;
178 }
179 else
180 {
181 *ch = '\0';
182 ch = mutt_str_skip_email_wsp(ch + 1);
183 break;
184 }
185 }
187 return ch;
188}
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:637
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:680
+ 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 200 of file mailcap.c.

202{
203 field = mutt_str_skip_whitespace(field);
204 if (*field == '=')
205 {
206 if (entry)
207 {
208 field++;
209 field = mutt_str_skip_whitespace(field);
210 mutt_str_replace(entry, field);
211 }
212 return 1;
213 }
214 else
215 {
216 mutt_error(_("Improperly formatted entry for type %s in \"%s\" line %d"),
217 type, filename, line);
218 return 0;
219 }
220}
#define mutt_error(...)
Definition: logging2.h:87
#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:623
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
+ 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 a,
const char *  filename,
const char *  type,
struct MailcapEntry entry,
enum MailcapLookup  opt 
)
static

Parse a mailcap entry.

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

Definition at line 232 of file mailcap.c.

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

438{
439 return mutt_mem_calloc(1, sizeof(struct MailcapEntry));
440}
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
A mailcap entry.
Definition: mailcap.h:36
+ Here is the call graph for this function:
+ 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 446 of file mailcap.c.

447{
448 if (!ptr || !*ptr)
449 return;
450
451 struct MailcapEntry *me = *ptr;
452
453 FREE(&me->command);
454 FREE(&me->testcommand);
455 FREE(&me->composecommand);
457 FREE(&me->editcommand);
458 FREE(&me->printcommand);
459 FREE(&me->nametemplate);
460 FREE(ptr);
461}
char * testcommand
Definition: mailcap.h:38
+ Here is the caller graph for this function:

◆ mailcap_lookup()

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

Find given type in the list of mailcap files.

Parameters
aMessage 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 475 of file mailcap.c.

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

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