2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/mailcap.h"
17 #include "../pith/init.h"
18 #include "../pith/conf.h"
19 #include "../pith/mimetype.h"
20 #include "../pith/mimedesc.h"
21 #include "../pith/status.h"
22 #include "../pith/util.h"
23 #include "../pith/readfile.h"
26 * We've decided not to implement the RFC1524 standard minimum path, because
27 * some of us think it is harder to debug a problem when you may be misled
28 * into looking at the wrong mailcap entry. Likewise for MIME.Types files.
30 #if defined(DOS) || defined(OS2)
31 #define MC_PATH_SEPARATOR ';'
32 #define MC_USER_FILE "MAILCAP"
33 #define MC_STDPATH NULL
35 #define MC_PATH_SEPARATOR ':'
36 #define MC_USER_FILE NULL
38 ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"
42 #define MC_ADD_TMP " %s"
44 #define MC_ADD_TMP " < %s"
47 typedef struct mcap_entry
{
48 struct mcap_entry
*next
;
53 char *label
; /* unused */
54 char *printcommand
; /* unused */
58 MailcapEntry
*head
, **tail
;
62 #define MC_TOKEN_MAX 64
69 void mc_process_file(char *);
70 void mc_parse_file(char *);
71 int mc_parse_line(char **, char **);
72 int mc_comment(char **);
73 int mc_token(char **, char **);
74 void mc_build_entry(char **);
75 int mc_sane_command(char *);
76 MailcapEntry
*mc_get_command(int, char *, BODY
*, int, int *);
77 int mc_ctype_match(int, char *, char *);
78 int mc_passes_test(MailcapEntry
*, int, char *, BODY
*);
79 char *mc_bld_test_cmd(char *, int, char *, BODY
*);
80 char *mc_cmd_bldr(char *, int, char *, BODY
*, char *, char **);
81 MailcapEntry
*mc_new_entry(void);
82 void mc_free_entry(MailcapEntry
**);
86 mc_conf_path(char *def_path
, char *env_path
, char *user_file
, int separator
, char *stdpath
)
90 /* We specify MIMETYPES as a path override */
92 /* there may need to be an override specific to pine */
93 path
= cpystr(def_path
);
95 path
= cpystr(env_path
);
97 #if defined(DOS) || defined(OS2)
101 * This gets interesting. Since we don't have any standard location
102 * for config/data files, look in the same directory as the PINERC
103 * and the same dir as PINE.EXE. This is similar to the UNIX
104 * situation with personal config info coming before
105 * potentially shared config data...
107 if(s
= last_cmpnt(ps_global
->pinerc
)){
108 strncpy(tmp_20k_buf
+1000, ps_global
->pinerc
, MIN(s
- ps_global
->pinerc
,SIZEOF_20KBUF
-1000));
109 tmp_20k_buf
[1000+MIN(s
- ps_global
->pinerc
,SIZEOF_20KBUF
-1000-1)] = '\0';
112 strncpy(tmp_20k_buf
+1000, ".\\", SIZEOF_20KBUF
-1000);
114 /* pinerc directory version of file */
115 build_path(tmp_20k_buf
+2000, tmp_20k_buf
+1000, user_file
, SIZEOF_20KBUF
-2000);
116 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
118 /* pine.exe directory version of file */
119 build_path(tmp_20k_buf
+3000, ps_global
->pine_dir
, user_file
, SIZEOF_20KBUF
-3000);
120 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
123 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%c%s", tmp_20k_buf
+2000, separator
, tmp_20k_buf
+3000);
126 build_path(tmp_20k_buf
, ps_global
->home_dir
, stdpath
, SIZEOF_20KBUF
);
128 path
= cpystr(tmp_20k_buf
);
136 * mc_init - Run down the path gathering all the mailcap entries.
137 * Returns with the Mailcap list built.
145 image_viewer
[MAILTMPLEN
];
147 if(MailcapData
.raw
) /* already have the file? */
150 MailcapData
.tail
= &MailcapData
.head
;
152 dprint((5, "- mc_init -\n"));
154 pathcopy
= mc_conf_path(ps_global
->VAR_MAILCAP_PATH
, getenv("MAILCAPS"),
155 MC_USER_FILE
, MC_PATH_SEPARATOR
, MC_STDPATH
);
157 path
= pathcopy
; /* overloaded "path" */
160 * Insert an entry for the image-viewer variable from .pinerc, if present.
162 if(ps_global
->VAR_IMAGE_VIEWER
&& *ps_global
->VAR_IMAGE_VIEWER
){
163 MailcapEntry
*mc
= mc_new_entry();
165 snprintf(image_viewer
, sizeof(image_viewer
), "%s %%s", ps_global
->VAR_IMAGE_VIEWER
);
167 MailcapData
.raw
= mail_newstringlist();
168 MailcapData
.raw
->text
.data
= (unsigned char *) cpystr(image_viewer
);
169 mc
->command
= (char *) MailcapData
.raw
->text
.data
;
170 mc
->contenttype
= "image/*";
171 mc
->label
= "Alpine Image Viewer";
172 dprint((5, "mailcap: using image-viewer=%s\n",
173 ps_global
->VAR_IMAGE_VIEWER
174 ? ps_global
->VAR_IMAGE_VIEWER
: "?"));
177 dprint((7, "mailcap: path: %s\n", path
? path
: "?"));
179 s
= strindex(path
, MC_PATH_SEPARATOR
);
182 mc_process_file(path
);
187 fs_give((void **)&pathcopy
);
194 dprint((11, "Collected mailcap entries\n"));
195 for(mc
= MailcapData
.head
; mc
; mc
= mc
->next
){
197 dprint((11, "%d: ", i
++));
199 dprint((11, "%s\n", mc
->label
? mc
->label
: "?"));
202 mc
->contenttype
? mc
->contenttype
: "?"));
204 dprint((11, " command: %s\n",
205 mc
->command
? mc
->command
: "?"));
207 dprint((11, " testcommand: %s",
208 mc
->testcommand
? mc
->testcommand
: "?"));
210 dprint((11, " printcommand: %s",
211 mc
->printcommand
? mc
->printcommand
: "?"));
212 dprint((11, " needsterminal %d\n", mc
->needsterminal
));
220 * Add all the entries from this file onto the Mailcap list.
223 mc_process_file(char *file
)
225 char filebuf
[MAXPATH
+1], *file_data
;
227 dprint((5, "mailcap: process_file: %s\n", file
? file
: "?"));
229 (void)strncpy(filebuf
, file
, MAXPATH
);
230 filebuf
[MAXPATH
] = '\0';
231 file
= fnexpand(filebuf
, sizeof(filebuf
));
232 dprint((7, "mailcap: processing file: %s\n", file
? file
: "?"));
233 switch(is_writable_dir(file
)){
234 case 0: case 1: /* is a directory */
235 dprint((1, "mailcap: %s is a directory, should be a file\n",
242 case 3: /* doesn't exist */
243 dprint((5, "mailcap: %s doesn't exist\n", file
? file
: "?"));
247 alpine_panic("Programmer botch in mc_process_file");
251 if((file_data
= read_file(file
, READ_FROM_LOCALE
)) != NULL
){
252 STRINGLIST
*newsl
, **sl
;
254 /* Create a new container */
255 newsl
= mail_newstringlist();
256 newsl
->text
.data
= (unsigned char *) file_data
;
258 /* figure out where in the list it should go */
259 for(sl
= &MailcapData
.raw
; *sl
; sl
= &((*sl
)->next
))
262 *sl
= newsl
; /* Add it to the list */
264 mc_parse_file(file_data
); /* the process mailcap data */
267 dprint((5, "mailcap: %s can't be read\n", file
? file
: "?"));
272 mc_parse_file(char *file
)
274 char *tokens
[MC_TOKEN_MAX
];
277 if(mc_parse_line(&file
, tokens
)) mc_build_entry(tokens
);
282 mc_parse_line(char **line
, char **tokens
)
284 char **tokenp
= tokens
;
286 while(mc_comment(line
)) /* skip comment lines */
289 while(mc_token(tokenp
, line
)) /* collect ';' delim'd tokens */
290 if(++tokenp
- tokens
>= MC_TOKEN_MAX
)
291 fatal("Ran out of tokens parsing mailcap file"); /* outch! */
293 *++tokenp
= NULL
; /* tie off list */
294 return(*tokens
!= NULL
);
299 * Returns 1 if line is a comment, 0 otherwise
302 mc_comment(char **line
)
304 if(**line
== '\n'){ /* blank line is a comment, too */
310 while(**line
) /* !EOF */
311 if(*++(*line
) == '\n'){ /* EOL? */
324 * Returns 0 if EOL, 1 otherwise
327 mc_token(char **token
, char **line
)
330 char *start
, *wsp
= NULL
;
332 *token
= NULL
; /* init the slot for this token */
334 /* skip leading white space */
335 while(**line
&& isspace((unsigned char) **line
))
340 /* Then see what's left */
343 case ';' : /* End-Of-Token */
344 rv
= 1; /* let caller know more follows */
345 case '\n' : /* EOL */
347 *wsp
= '\0'; /* truncate white space? */
349 *start
= '\0'; /* if we have a token, tie it off */
351 (*line
)++; /* and get ready to parse next one */
353 if(rv
== 1){ /* ignore trailing semicolon */
358 if(isspace((unsigned char) **line
))
365 case '\0' : /* EOF */
368 case '\\' : /* Quoted char */
370 #if defined(DOS) || defined(OS2)
372 * RFC 1524 says that backslash is used to quote
373 * the next character, but since backslash is part of pathnames
374 * on DOS we're afraid people will not put double backslashes
375 * in their mailcap files. Therefore, we violate the RFC by
376 * looking ahead to the next character. If it looks like it
377 * is just part of a pathname, then we consider a single
378 * backslash to *not* be a quoting character, but a literal
382 * If next char is any of these, treat the backslash
383 * that preceded it like a regular character.
385 if(**line
&& isascii(**line
)
386 && (isalnum((unsigned char) **line
) || strchr("_+-=~" , **line
))){
394 if(**line
== '\n'){ /* quoted line break */
396 (*line
)++; /* just move on */
397 while(isspace((unsigned char) **line
))
402 else if(**line
== '%') /* quoted '%' becomes "%%" */
403 *--(*line
) = '%'; /* overwrite '\' !! */
405 /* Fall thru and copy/advance pointers*/
412 wsp
= (isspace((unsigned char) *start
) && !wsp
) ? start
: NULL
;
420 mc_build_entry(char **tokens
)
425 dprint((5, "mailcap: missing content type!\n"));
428 else if(!tokens
[1] || !mc_sane_command(tokens
[1])){
429 dprint((5, "mailcap: missing/bogus command!\n"));
434 mc
->contenttype
= *tokens
++;
435 mc
->command
= *tokens
++;
437 dprint((9, "mailcap: content type: %s\n command: %s\n",
438 mc
->contenttype
? mc
->contenttype
: "?",
439 mc
->command
? mc
->command
: "?"));
442 for( ; *tokens
; tokens
++){
446 if(!isalnum((unsigned char) **tokens
)){
447 dprint((5, "Unknown parameter = \"%s\"", *tokens
));
451 if((arg
= strindex(*tokens
, '=')) != NULL
){
453 while(arg
> *tokens
&& isspace((unsigned char) arg
[-1]))
456 *arg
++ = '\0'; /* tie off parm arg */
457 while(*arg
&& isspace((unsigned char) *arg
))
464 if(!strucmp(*tokens
, "needsterminal")){
465 mc
->needsterminal
= 1;
466 dprint((9, "mailcap: set needsterminal\n"));
468 else if(!strucmp(*tokens
, "copiousoutput")){
469 mc
->needsterminal
= 2;
470 dprint((9, "mailcap: set copiousoutput\n"));
472 else if(arg
&& !strucmp(*tokens
, "test")){
473 mc
->testcommand
= arg
;
474 dprint((9, "mailcap: testcommand=%s\n",
475 mc
->testcommand
? mc
->testcommand
: "?"));
477 else if(arg
&& !strucmp(*tokens
, "description")){
479 dprint((9, "mailcap: label=%s\n",
480 mc
->label
? mc
->label
: "?"));
482 else if(arg
&& !strucmp(*tokens
, "print")){
483 mc
->printcommand
= arg
;
484 dprint((9, "mailcap: printcommand=%s\n",
485 mc
->printcommand
? mc
->printcommand
: "?"));
487 else if(arg
&& !strucmp(*tokens
, "compose")){
489 dprint((9, "mailcap: not using compose=%s\n",
492 else if(arg
&& !strucmp(arg
, "composetyped")){
494 dprint((9, "mailcap: not using composetyped=%s\n",
497 else if(arg
&& !strucmp(arg
, "textualnewlines")){
500 "mailcap: not using texttualnewlines=%s\n",
503 else if(arg
&& !strucmp(arg
, "edit")){
505 dprint((9, "mailcap: not using edit=%s\n",
508 else if(arg
&& !strucmp(arg
, "x11-bitmap")){
510 dprint((9, "mailcap: not using x11-bitmap=%s\n",
514 dprint((9, "mailcap: ignoring unknown flag: %s\n",
521 * Tests for mailcap defined command's sanity
524 mc_sane_command(char *command
)
526 /* First, test that a command string actually exists */
527 if(command
&& *command
){
530 * NOTE: Maybe we'll do this later. The problem is when the
531 * mailcap's been misconfigured. We then end up suppressing
532 * valuable output when the user actually tries to launch the
536 /* Second, Make sure we can get at it */
537 if(can_access_in_path(getenv("PATH"), command
, EXECUTE_ACCESS
) >= 0)
542 return(0); /* failed! */
547 * Returns the mailcap entry for type/subtype from the successful
548 * mailcap entry, or NULL if none. Command string still contains % stuff.
551 mc_get_command(int type
, char *subtype
, BODY
*body
,
552 int check_extension
, int *sp_handlingp
)
555 char tmp_subtype
[256], tmp_ext
[16], *ext
= NULL
;
557 dprint((5, "- mc_get_command(%s/%s) -\n",
558 body_type_names(type
),
559 subtype
? subtype
: "?"));
562 && (!subtype
|| !strucmp(subtype
, "plain"))
563 && F_ON(F_SHOW_TEXTPLAIN_INT
, ps_global
))
573 * Special handling for when we're looking at what's likely
574 * binary application data. Look for a file name extension
575 * that we might use to hook a helper app to.
577 * NOTE: This used to preclude an "app/o-s" mailcap entry
578 * since this took precedence. Now that there are
579 * typically two scans through the check_extension
580 * mechanism, the mailcap entry now takes precedence.
582 if((fname
= get_filename_parameter(NULL
, 0, body
, &e2b
.from
.ext
)) != NULL
583 && e2b
.from
.ext
&& e2b
.from
.ext
[0]){
584 if(strlen(e2b
.from
.ext
) < sizeof(tmp_ext
) - 2){
585 strncpy(ext
= tmp_ext
, e2b
.from
.ext
- 1, sizeof(tmp_ext
)); /* remember it */
586 tmp_ext
[sizeof(tmp_ext
)-1] = '\0';
587 if(mt_srch_mime_type(mt_srch_by_ext
, &e2b
)){
588 type
= e2b
.to
.mime
.type
; /* mapped type */
589 strncpy(subtype
= tmp_subtype
, e2b
.to
.mime
.subtype
,
590 sizeof(tmp_subtype
)-1);
591 tmp_subtype
[sizeof(tmp_subtype
)-1] = '\0';
592 fs_give((void **) &e2b
.to
.mime
.subtype
);
593 body
= NULL
; /* the params no longer apply */
597 fs_give((void **) &fname
);
601 fs_give((void **) &fname
);
607 for(mc
= MailcapData
.head
; mc
; mc
= mc
->next
)
608 if(mc_ctype_match(type
, subtype
, mc
->contenttype
)
609 && mc_passes_test(mc
, type
, subtype
, body
)){
611 "mc_get_command: type=%s/%s, command=%s\n",
612 body_type_names(type
),
613 subtype
? subtype
: "?",
614 mc
->command
? mc
->command
: "?"));
618 if(mime_os_specific_access()){
619 static MailcapEntry fake_mc
;
620 static char fake_cmd
[1024];
621 char tmp_mime_type
[256];
623 memset(&fake_mc
, 0, sizeof(MailcapEntry
));
625 fake_mc
.command
= fake_cmd
;
627 snprintf(tmp_mime_type
, sizeof(tmp_mime_type
), "%s/%s", body_types
[type
], subtype
);
628 if(mime_get_os_mimetype_command(tmp_mime_type
, ext
, fake_cmd
,
629 sizeof(fake_cmd
), check_extension
, sp_handlingp
))
638 * Check whether the pattern "pat" matches this type/subtype.
639 * Returns 1 if it does, 0 if not.
642 mc_ctype_match(int type
, char *subtype
, char *pat
)
644 char *type_name
= body_type_names(type
);
645 int len
= strlen(type_name
);
647 dprint((5, "mc_ctype_match: %s == %s / %s ?\n",
649 type_name
? type_name
: "?",
650 subtype
? subtype
: "?"));
652 return(!struncmp(type_name
, pat
, len
)
654 && (!pat
[len
+1] || pat
[len
+1] == '*'
655 || !strucmp(subtype
, &pat
[len
+1])))
661 * Run the test command for entry mc to see if this entry currently applies to
662 * applies to this type/subtype.
664 * Returns 1 if it does pass test (exits with status 0), 0 otherwise.
667 mc_passes_test(MailcapEntry
*mc
, int type
, char *subtype
, BODY
*body
)
672 dprint((5, "- mc_passes_test -\n"));
676 && !(cmd
= mc_bld_test_cmd(mc
->testcommand
, type
, subtype
, body
)))
677 return(FALSE
); /* couldn't be built */
679 if(!mc
->testcommand
|| !cmd
|| !*cmd
){
681 fs_give((void **)&cmd
);
683 dprint((7, "no test command, so Pass\n"));
687 rv
= exec_mailcap_test_cmd(cmd
);
688 dprint((7, "mc_passes_test: \"%s\" %s (rv=%d)\n",
689 cmd
? cmd
: "?", rv
? "Failed" : "Passed", rv
)) ;
691 fs_give((void **)&cmd
);
698 mailcap_can_display(int type
, char *subtype
, BODY
*body
, int check_extension
)
700 dprint((5, "- mailcap_can_display -\n"));
702 return(mc_get_command(type
, subtype
, body
,
703 check_extension
, NULL
) != NULL
);
708 mailcap_build_command(int type
, char *subtype
, BODY
*body
,
709 char *tmp_file
, int *needsterm
, int chk_extension
)
712 char *command
, *err
= NULL
;
713 MCAP_CMD_S
*mc_cmd
= NULL
;
716 dprint((5, "- mailcap_build_command -\n"));
718 mc
= mc_get_command(type
, subtype
, body
, chk_extension
, &sp_handling
);
720 q_status_message(SM_ORDER
, 3, 4, "Error constructing viewer command");
722 "mailcap_build_command: no command string for %s/%s\n",
723 body_type_names(type
), subtype
? subtype
: "?"));
724 return((MCAP_CMD_S
*)NULL
);
728 *needsterm
= mc
->needsterminal
;
731 command
= cpystr(mc
->command
);
732 else if(!(command
= mc_cmd_bldr(mc
->command
, type
, subtype
, body
, tmp_file
, &err
)) && err
&& *err
)
733 q_status_message(SM_ORDER
, 5, 5, err
);
735 dprint((5, "built command: %s\n", command
? command
: "?"));
738 mc_cmd
= (MCAP_CMD_S
*)fs_get(sizeof(MCAP_CMD_S
));
739 mc_cmd
->command
= command
;
740 mc_cmd
->special_handling
= sp_handling
;
747 * mc_bld_test_cmd - build the command to test if the given type flies
749 * mc_cmd_bldr's tmp_file argument is NULL as we're not going to
750 * decode and write each and every MIME segment's data to a temp file
751 * when no test's going to use the data anyway.
754 mc_bld_test_cmd(char *controlstring
, int type
, char *subtype
, BODY
*body
)
756 return(mc_cmd_bldr(controlstring
, type
, subtype
, body
, NULL
, NULL
));
761 * mc_cmd_bldr - construct a command string to execute
763 * If tmp_file is null, then the contents of the given MIME segment
764 * is not provided. This is useful for building the "test=" string
765 * as it doesn't operate on the segment's data.
767 * The return value is an alloc'd copy of the command to be executed.
770 mc_cmd_bldr(char *controlstring
, int type
, char *subtype
,
771 BODY
*body
, char *tmp_file
, char **err
)
773 char *from
, *to
, *s
, *parm
;
774 int prefixed
= 0, used_tmp_file
= 0;
776 dprint((8, "- mc_cmd_bldr -\n"));
778 for(from
= controlstring
, to
= tmp_20k_buf
; *from
; ++from
){
779 if(prefixed
){ /* previous char was % */
782 case '%': /* turned \% into this earlier */
783 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
788 case 's': /* insert tmp_file name in cmd */
791 sstrncpy(&to
, tmp_file
, SIZEOF_20KBUF
-(to
-tmp_20k_buf
));
795 "mc_cmd_bldr: %%s in cmd but not supplied!\n"));
799 case 't': /* insert MIME type/subtype */
800 /* quote to prevent funny business */
801 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
804 sstrncpy(&to
, body_type_names(type
), SIZEOF_20KBUF
-(to
-tmp_20k_buf
));
806 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
809 sstrncpy(&to
, subtype
, SIZEOF_20KBUF
-(to
-tmp_20k_buf
));
811 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
816 case '{': /* insert requested MIME param */
817 if(F_OFF(F_DO_MAILCAP_PARAM_SUBST
, ps_global
)){
820 dprint((2, "mc_cmd_bldr: param subs %s\n",
823 if((s
= strindex(from
, '}')) != NULL
){
828 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
829 "Mailcap: see hidden feature %.200s (%%%.200s)",
830 feature_list_name(F_DO_MAILCAP_PARAM_SUBST
), from
);
839 s
= strindex(from
, '}');
841 q_status_message1(SM_ORDER
, 0, 4,
842 "Ignoring ill-formed parameter reference in mailcap file: %.200s", from
);
847 ++from
; /* from is the part inside the brackets now */
849 parm
= parameter_val(body
? body
->parameter
: NULL
, from
);
852 "mc_cmd_bldr: parameter %s = %s\n",
853 from
? from
: "?", parm
? parm
: "(not found)"));
856 * Quote parameter values for /bin/sh.
857 * Put single quotes around the whole thing but every time
858 * there is an actual single quote put it outside of the
859 * single quotes with a backslash in front of it. So the
860 * parameter value fred's car
861 * turns into 'fred'\''s car'
863 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
864 *to
++ = '\''; /* opening quote */
870 * Copy value, but quote single quotes for /bin/sh
871 * Backslash quote is ignored inside single quotes so
872 * have to put those outside of the single quotes.
873 * (The parm+1000 nonsense is to protect against
874 * malicious mail trying to overflow our buffer.)
876 * TCH - Change 2/8/1999
877 * Also quote the ` to prevent execution of arbitrary code
879 for(p
= parm
; *p
&& p
< parm
+1000; p
++){
880 if((*p
== '\'') || (*p
== '`')){
881 if(to
-tmp_20k_buf
+4 < SIZEOF_20KBUF
){
882 *to
++ = '\''; /* closing quote */
884 *to
++ = *p
; /* quoted character */
885 *to
++ = '\''; /* opening quote */
888 else if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
892 fs_give((void **) &parm
);
895 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
896 *to
++ = '\''; /* closing quote for /bin/sh */
898 *s
= '}'; /* restore */
903 * %n and %F are used by metamail to support otherwise
904 * unrecognized multipart Content-Types. Pine does
905 * not use these since we're only dealing with the individual
906 * parts at this point.
912 "Ignoring %s format code in mailcap file: %%%c\n",
913 (*from
== 'n' || *from
== 'F') ? "unimplemented"
919 else if(*from
== '%') /* next char is special */
921 else if(to
-tmp_20k_buf
< SIZEOF_20KBUF
) /* regular character, just copy */
925 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
928 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
931 * file not specified, redirect to stdin
933 if(!used_tmp_file
&& tmp_file
)
934 snprintf(to
, SIZEOF_20KBUF
-(to
-tmp_20k_buf
), MC_ADD_TMP
, tmp_file
);
936 return(cpystr(tmp_20k_buf
));
946 MailcapEntry
*mc
= (MailcapEntry
*) fs_get(sizeof(MailcapEntry
));
947 memset(mc
, 0, sizeof(MailcapEntry
));
948 *MailcapData
.tail
= mc
;
949 MailcapData
.tail
= &mc
->next
;
955 * Free a list of mailcap entries
958 mc_free_entry(MailcapEntry
**mc
)
961 mc_free_entry(&(*mc
)->next
);
962 fs_give((void **) mc
);
970 mail_free_stringlist(&MailcapData
.raw
);
971 mc_free_entry(&MailcapData
.head
);