NeoMutt  2024-12-12-14-g7b49f7
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 "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 *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 69 of file mailcap.c.

71{
72 int needspipe = true;
73 struct Buffer *buf = buf_pool_get();
74 struct Buffer *quoted = buf_pool_get();
75 struct Buffer *param = NULL;
76 struct Buffer *type2 = NULL;
77
78 const bool c_mailcap_sanitize = cs_subset_bool(NeoMutt->sub, "mailcap_sanitize");
79 const char *cptr = buf_string(command);
80 while (*cptr)
81 {
82 if (*cptr == '\\')
83 {
84 cptr++;
85 if (*cptr)
86 buf_addch(buf, *cptr++);
87 }
88 else if (*cptr == '%')
89 {
90 cptr++;
91 if (*cptr == '{')
92 {
93 const char *pvalue2 = NULL;
94
95 if (param)
96 buf_reset(param);
97 else
98 param = buf_pool_get();
99
100 /* Copy parameter name into param buffer */
101 cptr++;
102 while (*cptr && (*cptr != '}'))
103 buf_addch(param, *cptr++);
104
105 /* In send mode, use the current charset, since the message hasn't
106 * been converted yet. If noconv is set, then we assume the
107 * charset parameter has the correct value instead. */
108 if (mutt_istr_equal(buf_string(param), "charset") && b->charset && !b->noconv)
109 pvalue2 = b->charset;
110 else
111 pvalue2 = mutt_param_get(&b->parameter, buf_string(param));
112
113 /* Now copy the parameter value into param buffer */
114 if (c_mailcap_sanitize)
115 buf_sanitize_filename(param, NONULL(pvalue2), false);
116 else
117 buf_strcpy(param, pvalue2);
118
119 buf_quote_filename(quoted, buf_string(param), true);
120 buf_addstr(buf, buf_string(quoted));
121 }
122 else if ((*cptr == 's') && filename)
123 {
124 buf_quote_filename(quoted, filename, true);
125 buf_addstr(buf, buf_string(quoted));
126 needspipe = false;
127 }
128 else if (*cptr == 't')
129 {
130 if (!type2)
131 {
132 type2 = buf_pool_get();
133 if (c_mailcap_sanitize)
134 buf_sanitize_filename(type2, type, false);
135 else
136 buf_strcpy(type2, type);
137 }
138 buf_quote_filename(quoted, buf_string(type2), true);
139 buf_addstr(buf, buf_string(quoted));
140 }
141
142 if (*cptr)
143 cptr++;
144 }
145 else
146 {
147 buf_addch(buf, *cptr++);
148 }
149 }
150 buf_copy(command, buf);
151
152 buf_pool_release(&buf);
153 buf_pool_release(&quoted);
154 buf_pool_release(&param);
155 buf_pool_release(&type2);
156
157 return needspipe;
158}
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:933
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:672
void buf_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:982
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:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
#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: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:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
+ 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 166 of file mailcap.c.

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

204{
205 field = mutt_str_skip_whitespace(field);
206 if (*field == '=')
207 {
208 if (entry)
209 {
210 field++;
211 field = mutt_str_skip_whitespace(field);
212 mutt_str_replace(entry, field);
213 }
214 return 1;
215 }
216 else
217 {
218 mutt_error(_("Improperly formatted entry for type %s in \"%s\" line %d"),
219 type, filename, line);
220 return 0;
221 }
222}
#define mutt_error(...)
Definition: logging2.h:92
#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:551
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:280
+ 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 234 of file mailcap.c.

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

446{
447 return MUTT_MEM_CALLOC(1, struct MailcapEntry);
448}
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
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 454 of file mailcap.c.

455{
456 if (!ptr || !*ptr)
457 return;
458
459 struct MailcapEntry *me = *ptr;
460
461 FREE(&me->command);
462 FREE(&me->testcommand);
463 FREE(&me->composecommand);
465 FREE(&me->editcommand);
466 FREE(&me->printcommand);
467 FREE(&me->nametemplate);
468 FREE(ptr);
469}
char * testcommand
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 483 of file mailcap.c.

485{
486 /* rfc1524 specifies that a path of mailcap files should be searched.
487 * joy. They say
488 * $HOME/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap, etc
489 * and overridden by the MAILCAPS environment variable, and, just to be nice,
490 * we'll make it specifiable in .neomuttrc */
491 const struct Slist *c_mailcap_path = cs_subset_slist(NeoMutt->sub, "mailcap_path");
492 if (!c_mailcap_path || (c_mailcap_path->count == 0))
493 {
494 /* L10N:
495 Mutt is trying to look up a mailcap value, but $mailcap_path is empty.
496 We added a reference to the MAILCAPS environment variable as a hint too.
497
498 Because the variable is automatically populated by Mutt, this
499 should only occur if the user deliberately runs in their shell:
500 export MAILCAPS=
501
502 or deliberately runs inside Mutt or their .muttrc:
503 set mailcap_path=""
504 -or-
505 unset mailcap_path
506 */
507 mutt_error(_("Neither mailcap_path nor MAILCAPS specified"));
508 return false;
509 }
510
511 mutt_check_lookup_list(b, type, typelen);
512
513 struct Buffer *path = buf_pool_get();
514 bool found = false;
515
516 struct ListNode *np = NULL;
517 STAILQ_FOREACH(np, &c_mailcap_path->head, entries)
518 {
519 buf_strcpy(path, np->data);
520 buf_expand_path(path);
521
522 mutt_debug(LL_DEBUG2, "Checking mailcap file: %s\n", buf_string(path));
523 found = rfc1524_mailcap_parse(b, buf_string(path), type, entry, opt);
524 if (found)
525 break;
526 }
527
528 buf_pool_release(&path);
529
530 if (entry && !found)
531 mutt_error(_("mailcap entry for type %s not found"), type);
532
533 return found;
534}
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:234
void mutt_check_lookup_list(struct Body *b, char *type, size_t len)
Update the mime type.
Definition: mutt_attach.c:343
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:315
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
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 552 of file mailcap.c.

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