NeoMutt  2022-04-29-70-g0c028c
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 = mutt_buffer_pool_get();
72  struct Buffer *quoted = mutt_buffer_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 = mutt_buffer_string(command);
78  while (*cptr)
79  {
80  if (*cptr == '\\')
81  {
82  cptr++;
83  if (*cptr)
84  mutt_buffer_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  mutt_buffer_reset(param);
95  else
96  param = mutt_buffer_pool_get();
97 
98  /* Copy parameter name into param buffer */
99  cptr++;
100  while (*cptr && (*cptr != '}'))
101  mutt_buffer_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(mutt_buffer_string(param), "charset") && a->charset && !a->noconv)
107  pvalue2 = a->charset;
108  else
109  pvalue2 = mutt_param_get(&a->parameter, mutt_buffer_string(param));
110 
111  /* Now copy the parameter value into param buffer */
112  if (c_mailcap_sanitize)
113  mutt_buffer_sanitize_filename(param, NONULL(pvalue2), false);
114  else
115  mutt_buffer_strcpy(param, NONULL(pvalue2));
116 
117  mutt_buffer_quote_filename(quoted, mutt_buffer_string(param), true);
119  }
120  else if ((*cptr == 's') && filename)
121  {
122  mutt_buffer_quote_filename(quoted, filename, true);
124  needspipe = false;
125  }
126  else if (*cptr == 't')
127  {
128  if (!type2)
129  {
130  type2 = mutt_buffer_pool_get();
131  if (c_mailcap_sanitize)
132  mutt_buffer_sanitize_filename(type2, type, false);
133  else
134  mutt_buffer_strcpy(type2, type);
135  }
136  mutt_buffer_quote_filename(quoted, mutt_buffer_string(type2), true);
138  }
139 
140  if (*cptr)
141  cptr++;
142  }
143  else
144  mutt_buffer_addch(buf, *cptr++);
145  }
146  mutt_buffer_copy(command, buf);
147 
149  mutt_buffer_pool_release(&quoted);
150  mutt_buffer_pool_release(&param);
151  mutt_buffer_pool_release(&type2);
152 
153  return needspipe;
154 }
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:447
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
void mutt_buffer_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
Quote a filename to survive the shell's quoting rules.
Definition: file.c:891
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:796
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1650
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#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 162 of file mailcap.c.

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

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

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

436 {
437  return mutt_mem_calloc(1, sizeof(struct MailcapEntry));
438 }
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 444 of file mailcap.c.

445 {
446  if (!ptr || !*ptr)
447  return;
448 
449  struct MailcapEntry *me = *ptr;
450 
451  FREE(&me->command);
452  FREE(&me->testcommand);
453  FREE(&me->composecommand);
454  FREE(&me->composetypecommand);
455  FREE(&me->editcommand);
456  FREE(&me->printcommand);
457  FREE(&me->nametemplate);
458  FREE(ptr);
459 }
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 473 of file mailcap.c.

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

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