1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailcap.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/mailcap.h"
21 #include "../pith/init.h"
22 #include "../pith/conf.h"
23 #include "../pith/mimetype.h"
24 #include "../pith/mimedesc.h"
25 #include "../pith/status.h"
26 #include "../pith/util.h"
27 #include "../pith/readfile.h"
30 * We've decided not to implement the RFC1524 standard minimum path, because
31 * some of us think it is harder to debug a problem when you may be misled
32 * into looking at the wrong mailcap entry. Likewise for MIME.Types files.
34 #if defined(DOS) || defined(OS2)
35 #define MC_PATH_SEPARATOR ';'
36 #define MC_USER_FILE "MAILCAP"
37 #define MC_STDPATH NULL
39 #define MC_PATH_SEPARATOR ':'
40 #define MC_USER_FILE NULL
42 ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"
46 #define MC_ADD_TMP " %s"
48 #define MC_ADD_TMP " < %s"
51 typedef struct mcap_entry
{
52 struct mcap_entry
*next
;
57 char *label
; /* unused */
58 char *printcommand
; /* unused */
62 MailcapEntry
*head
, **tail
;
66 #define MC_TOKEN_MAX 64
73 void mc_process_file(char *);
74 void mc_parse_file(char *);
75 int mc_parse_line(char **, char **);
76 int mc_comment(char **);
77 int mc_token(char **, char **);
78 void mc_build_entry(char **);
79 int mc_sane_command(char *);
80 MailcapEntry
*mc_get_command(int, char *, BODY
*, int, int *);
81 int mc_ctype_match(int, char *, char *);
82 int mc_passes_test(MailcapEntry
*, int, char *, BODY
*);
83 char *mc_bld_test_cmd(char *, int, char *, BODY
*);
84 char *mc_cmd_bldr(char *, int, char *, BODY
*, char *, char **);
85 MailcapEntry
*mc_new_entry(void);
86 void mc_free_entry(MailcapEntry
**);
90 mc_conf_path(char *def_path
, char *env_path
, char *user_file
, int separator
, char *stdpath
)
94 /* We specify MIMETYPES as a path override */
96 /* there may need to be an override specific to pine */
97 path
= cpystr(def_path
);
99 path
= cpystr(env_path
);
101 #if defined(DOS) || defined(OS2)
105 * This gets interesting. Since we don't have any standard location
106 * for config/data files, look in the same directory as the PINERC
107 * and the same dir as PINE.EXE. This is similar to the UNIX
108 * situation with personal config info coming before
109 * potentially shared config data...
111 if(s
= last_cmpnt(ps_global
->pinerc
)){
112 strncpy(tmp_20k_buf
+1000, ps_global
->pinerc
, MIN(s
- ps_global
->pinerc
,SIZEOF_20KBUF
-1000));
113 tmp_20k_buf
[1000+MIN(s
- ps_global
->pinerc
,SIZEOF_20KBUF
-1000-1)] = '\0';
116 strncpy(tmp_20k_buf
+1000, ".\\", SIZEOF_20KBUF
-1000);
118 /* pinerc directory version of file */
119 build_path(tmp_20k_buf
+2000, tmp_20k_buf
+1000, user_file
, SIZEOF_20KBUF
-2000);
120 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
122 /* pine.exe directory version of file */
123 build_path(tmp_20k_buf
+3000, ps_global
->pine_dir
, user_file
, SIZEOF_20KBUF
-3000);
124 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
127 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%c%s", tmp_20k_buf
+2000, separator
, tmp_20k_buf
+3000);
130 build_path(tmp_20k_buf
, ps_global
->home_dir
, stdpath
, SIZEOF_20KBUF
);
132 path
= cpystr(tmp_20k_buf
);
140 * mc_init - Run down the path gathering all the mailcap entries.
141 * Returns with the Mailcap list built.
149 image_viewer
[MAILTMPLEN
];
151 if(MailcapData
.raw
) /* already have the file? */
154 MailcapData
.tail
= &MailcapData
.head
;
156 dprint((5, "- mc_init -\n"));
158 pathcopy
= mc_conf_path(ps_global
->VAR_MAILCAP_PATH
, getenv("MAILCAPS"),
159 MC_USER_FILE
, MC_PATH_SEPARATOR
, MC_STDPATH
);
161 path
= pathcopy
; /* overloaded "path" */
164 * Insert an entry for the image-viewer variable from .pinerc, if present.
166 if(ps_global
->VAR_IMAGE_VIEWER
&& *ps_global
->VAR_IMAGE_VIEWER
){
167 MailcapEntry
*mc
= mc_new_entry();
169 snprintf(image_viewer
, sizeof(image_viewer
), "%s %%s", ps_global
->VAR_IMAGE_VIEWER
);
171 MailcapData
.raw
= mail_newstringlist();
172 MailcapData
.raw
->text
.data
= (unsigned char *) cpystr(image_viewer
);
173 mc
->command
= (char *) MailcapData
.raw
->text
.data
;
174 mc
->contenttype
= "image/*";
175 mc
->label
= "Alpine Image Viewer";
176 dprint((5, "mailcap: using image-viewer=%s\n",
177 ps_global
->VAR_IMAGE_VIEWER
178 ? ps_global
->VAR_IMAGE_VIEWER
: "?"));
181 dprint((7, "mailcap: path: %s\n", path
? path
: "?"));
183 s
= strindex(path
, MC_PATH_SEPARATOR
);
186 mc_process_file(path
);
191 fs_give((void **)&pathcopy
);
198 dprint((11, "Collected mailcap entries\n"));
199 for(mc
= MailcapData
.head
; mc
; mc
= mc
->next
){
201 dprint((11, "%d: ", i
++));
203 dprint((11, "%s\n", mc
->label
? mc
->label
: "?"));
206 mc
->contenttype
? mc
->contenttype
: "?"));
208 dprint((11, " command: %s\n",
209 mc
->command
? mc
->command
: "?"));
211 dprint((11, " testcommand: %s",
212 mc
->testcommand
? mc
->testcommand
: "?"));
214 dprint((11, " printcommand: %s",
215 mc
->printcommand
? mc
->printcommand
: "?"));
216 dprint((11, " needsterminal %d\n", mc
->needsterminal
));
224 * Add all the entries from this file onto the Mailcap list.
227 mc_process_file(char *file
)
229 char filebuf
[MAXPATH
+1], *file_data
;
231 dprint((5, "mailcap: process_file: %s\n", file
? file
: "?"));
233 (void)strncpy(filebuf
, file
, MAXPATH
);
234 filebuf
[MAXPATH
] = '\0';
235 file
= fnexpand(filebuf
, sizeof(filebuf
));
236 dprint((7, "mailcap: processing file: %s\n", file
? file
: "?"));
237 switch(is_writable_dir(file
)){
238 case 0: case 1: /* is a directory */
239 dprint((1, "mailcap: %s is a directory, should be a file\n",
246 case 3: /* doesn't exist */
247 dprint((5, "mailcap: %s doesn't exist\n", file
? file
: "?"));
251 alpine_panic("Programmer botch in mc_process_file");
255 if((file_data
= read_file(file
, READ_FROM_LOCALE
)) != NULL
){
256 STRINGLIST
*newsl
, **sl
;
258 /* Create a new container */
259 newsl
= mail_newstringlist();
260 newsl
->text
.data
= (unsigned char *) file_data
;
262 /* figure out where in the list it should go */
263 for(sl
= &MailcapData
.raw
; *sl
; sl
= &((*sl
)->next
))
266 *sl
= newsl
; /* Add it to the list */
268 mc_parse_file(file_data
); /* the process mailcap data */
271 dprint((5, "mailcap: %s can't be read\n", file
? file
: "?"));
276 mc_parse_file(char *file
)
278 char *tokens
[MC_TOKEN_MAX
];
280 while(mc_parse_line(&file
, tokens
))
281 mc_build_entry(tokens
);
286 mc_parse_line(char **line
, char **tokens
)
288 char **tokenp
= tokens
;
290 while(mc_comment(line
)) /* skip comment lines */
293 while(mc_token(tokenp
, line
)) /* collect ';' delim'd tokens */
294 if(++tokenp
- tokens
>= MC_TOKEN_MAX
)
295 fatal("Ran out of tokens parsing mailcap file"); /* outch! */
297 *++tokenp
= NULL
; /* tie off list */
298 return(*tokens
!= NULL
);
303 * Retuns 1 if line is a comment, 0 otherwise
306 mc_comment(char **line
)
308 if(**line
== '\n'){ /* blank line is a comment, too */
314 while(**line
) /* !EOF */
315 if(*++(*line
) == '\n'){ /* EOL? */
328 * Retuns 0 if EOL, 1 otherwise
331 mc_token(char **token
, char **line
)
334 char *start
, *wsp
= NULL
;
336 *token
= NULL
; /* init the slot for this token */
338 /* skip leading white space */
339 while(**line
&& isspace((unsigned char) **line
))
344 /* Then see what's left */
347 case ';' : /* End-Of-Token */
348 rv
= 1; /* let caller know more follows */
349 case '\n' : /* EOL */
351 *wsp
= '\0'; /* truncate white space? */
353 *start
= '\0'; /* if we have a token, tie it off */
355 (*line
)++; /* and get ready to parse next one */
357 if(rv
== 1){ /* ignore trailing semicolon */
362 if(isspace((unsigned char) **line
))
369 case '\0' : /* EOF */
372 case '\\' : /* Quoted char */
374 #if defined(DOS) || defined(OS2)
376 * RFC 1524 says that backslash is used to quote
377 * the next character, but since backslash is part of pathnames
378 * on DOS we're afraid people will not put double backslashes
379 * in their mailcap files. Therefore, we violate the RFC by
380 * looking ahead to the next character. If it looks like it
381 * is just part of a pathname, then we consider a single
382 * backslash to *not* be a quoting character, but a literal
386 * If next char is any of these, treat the backslash
387 * that preceded it like a regular character.
389 if(**line
&& isascii(**line
)
390 && (isalnum((unsigned char) **line
) || strchr("_+-=~" , **line
))){
398 if(**line
== '\n'){ /* quoted line break */
400 (*line
)++; /* just move on */
401 while(isspace((unsigned char) **line
))
406 else if(**line
== '%') /* quoted '%' becomes "%%" */
407 *--(*line
) = '%'; /* overwrite '\' !! */
409 /* Fall thru and copy/advance pointers*/
416 wsp
= (isspace((unsigned char) *start
) && !wsp
) ? start
: NULL
;
424 mc_build_entry(char **tokens
)
429 dprint((5, "mailcap: missing content type!\n"));
432 else if(!tokens
[1] || !mc_sane_command(tokens
[1])){
433 dprint((5, "mailcap: missing/bogus command!\n"));
438 mc
->contenttype
= *tokens
++;
439 mc
->command
= *tokens
++;
441 dprint((9, "mailcap: content type: %s\n command: %s\n",
442 mc
->contenttype
? mc
->contenttype
: "?",
443 mc
->command
? mc
->command
: "?"));
446 for( ; *tokens
; tokens
++){
450 if(!isalnum((unsigned char) **tokens
)){
451 dprint((5, "Unknown parameter = \"%s\"", *tokens
));
455 if((arg
= strindex(*tokens
, '=')) != NULL
){
457 while(arg
> *tokens
&& isspace((unsigned char) arg
[-1]))
460 *arg
++ = '\0'; /* tie off parm arg */
461 while(*arg
&& isspace((unsigned char) *arg
))
468 if(!strucmp(*tokens
, "needsterminal")){
469 mc
->needsterminal
= 1;
470 dprint((9, "mailcap: set needsterminal\n"));
472 else if(!strucmp(*tokens
, "copiousoutput")){
473 mc
->needsterminal
= 2;
474 dprint((9, "mailcap: set copiousoutput\n"));
476 else if(arg
&& !strucmp(*tokens
, "test")){
477 mc
->testcommand
= arg
;
478 dprint((9, "mailcap: testcommand=%s\n",
479 mc
->testcommand
? mc
->testcommand
: "?"));
481 else if(arg
&& !strucmp(*tokens
, "description")){
483 dprint((9, "mailcap: label=%s\n",
484 mc
->label
? mc
->label
: "?"));
486 else if(arg
&& !strucmp(*tokens
, "print")){
487 mc
->printcommand
= arg
;
488 dprint((9, "mailcap: printcommand=%s\n",
489 mc
->printcommand
? mc
->printcommand
: "?"));
491 else if(arg
&& !strucmp(*tokens
, "compose")){
493 dprint((9, "mailcap: not using compose=%s\n",
496 else if(arg
&& !strucmp(arg
, "composetyped")){
498 dprint((9, "mailcap: not using composetyped=%s\n",
501 else if(arg
&& !strucmp(arg
, "textualnewlines")){
504 "mailcap: not using texttualnewlines=%s\n",
507 else if(arg
&& !strucmp(arg
, "edit")){
509 dprint((9, "mailcap: not using edit=%s\n",
512 else if(arg
&& !strucmp(arg
, "x11-bitmap")){
514 dprint((9, "mailcap: not using x11-bitmap=%s\n",
518 dprint((9, "mailcap: ignoring unknown flag: %s\n",
525 * Tests for mailcap defined command's sanity
528 mc_sane_command(char *command
)
530 /* First, test that a command string actually exists */
531 if(command
&& *command
){
534 * NOTE: Maybe we'll do this later. The problem is when the
535 * mailcap's been misconfigured. We then end up supressing
536 * valuable output when the user actually tries to launch the
540 /* Second, Make sure we can get at it */
541 if(can_access_in_path(getenv("PATH"), command
, EXECUTE_ACCESS
) >= 0)
546 return(0); /* failed! */
551 * Returns the mailcap entry for type/subtype from the successfull
552 * mailcap entry, or NULL if none. Command string still contains % stuff.
555 mc_get_command(int type
, char *subtype
, BODY
*body
,
556 int check_extension
, int *sp_handlingp
)
559 char tmp_subtype
[256], tmp_ext
[16], *ext
= NULL
;
561 dprint((5, "- mc_get_command(%s/%s) -\n",
562 body_type_names(type
),
563 subtype
? subtype
: "?"));
566 && (!subtype
|| !strucmp(subtype
, "plain"))
567 && F_ON(F_SHOW_TEXTPLAIN_INT
, ps_global
))
577 * Special handling for when we're looking at what's likely
578 * binary application data. Look for a file name extension
579 * that we might use to hook a helper app to.
581 * NOTE: This used to preclude an "app/o-s" mailcap entry
582 * since this took precedence. Now that there are
583 * typically two scans through the check_extension
584 * mechanism, the mailcap entry now takes precedence.
586 if((fname
= get_filename_parameter(NULL
, 0, body
, &e2b
.from
.ext
)) != NULL
587 && e2b
.from
.ext
&& e2b
.from
.ext
[0]){
588 if(strlen(e2b
.from
.ext
) < sizeof(tmp_ext
) - 2){
589 strncpy(ext
= tmp_ext
, e2b
.from
.ext
- 1, sizeof(tmp_ext
)); /* remember it */
590 tmp_ext
[sizeof(tmp_ext
)-1] = '\0';
591 if(mt_srch_mime_type(mt_srch_by_ext
, &e2b
)){
592 type
= e2b
.to
.mime
.type
; /* mapped type */
593 strncpy(subtype
= tmp_subtype
, e2b
.to
.mime
.subtype
,
594 sizeof(tmp_subtype
)-1);
595 tmp_subtype
[sizeof(tmp_subtype
)-1] = '\0';
596 fs_give((void **) &e2b
.to
.mime
.subtype
);
597 body
= NULL
; /* the params no longer apply */
601 fs_give((void **) &fname
);
605 fs_give((void **) &fname
);
611 for(mc
= MailcapData
.head
; mc
; mc
= mc
->next
)
612 if(mc_ctype_match(type
, subtype
, mc
->contenttype
)
613 && mc_passes_test(mc
, type
, subtype
, body
)){
615 "mc_get_command: type=%s/%s, command=%s\n",
616 body_type_names(type
),
617 subtype
? subtype
: "?",
618 mc
->command
? mc
->command
: "?"));
622 if(mime_os_specific_access()){
623 static MailcapEntry fake_mc
;
624 static char fake_cmd
[1024];
625 char tmp_mime_type
[256];
627 memset(&fake_mc
, 0, sizeof(MailcapEntry
));
629 fake_mc
.command
= fake_cmd
;
631 snprintf(tmp_mime_type
, sizeof(tmp_mime_type
), "%s/%s", body_types
[type
], subtype
);
632 if(mime_get_os_mimetype_command(tmp_mime_type
, ext
, fake_cmd
,
633 sizeof(fake_cmd
), check_extension
, sp_handlingp
))
642 * Check whether the pattern "pat" matches this type/subtype.
643 * Returns 1 if it does, 0 if not.
646 mc_ctype_match(int type
, char *subtype
, char *pat
)
648 char *type_name
= body_type_names(type
);
649 int len
= strlen(type_name
);
651 dprint((5, "mc_ctype_match: %s == %s / %s ?\n",
653 type_name
? type_name
: "?",
654 subtype
? subtype
: "?"));
656 return(!struncmp(type_name
, pat
, len
)
658 && (!pat
[len
+1] || pat
[len
+1] == '*'
659 || !strucmp(subtype
, &pat
[len
+1])))
665 * Run the test command for entry mc to see if this entry currently applies to
666 * applies to this type/subtype.
668 * Returns 1 if it does pass test (exits with status 0), 0 otherwise.
671 mc_passes_test(MailcapEntry
*mc
, int type
, char *subtype
, BODY
*body
)
676 dprint((5, "- mc_passes_test -\n"));
680 && !(cmd
= mc_bld_test_cmd(mc
->testcommand
, type
, subtype
, body
)))
681 return(FALSE
); /* couldn't be built */
683 if(!mc
->testcommand
|| !cmd
|| !*cmd
){
685 fs_give((void **)&cmd
);
687 dprint((7, "no test command, so Pass\n"));
691 rv
= exec_mailcap_test_cmd(cmd
);
692 dprint((7, "mc_passes_test: \"%s\" %s (rv=%d)\n",
693 cmd
? cmd
: "?", rv
? "Failed" : "Passed", rv
)) ;
695 fs_give((void **)&cmd
);
702 mailcap_can_display(int type
, char *subtype
, BODY
*body
, int check_extension
)
704 dprint((5, "- mailcap_can_display -\n"));
706 return(mc_get_command(type
, subtype
, body
,
707 check_extension
, NULL
) != NULL
);
712 mailcap_build_command(int type
, char *subtype
, BODY
*body
,
713 char *tmp_file
, int *needsterm
, int chk_extension
)
716 char *command
, *err
= NULL
;
717 MCAP_CMD_S
*mc_cmd
= NULL
;
720 dprint((5, "- mailcap_build_command -\n"));
722 mc
= mc_get_command(type
, subtype
, body
, chk_extension
, &sp_handling
);
724 q_status_message(SM_ORDER
, 3, 4, "Error constructing viewer command");
726 "mailcap_build_command: no command string for %s/%s\n",
727 body_type_names(type
), subtype
? subtype
: "?"));
728 return((MCAP_CMD_S
*)NULL
);
732 *needsterm
= mc
->needsterminal
;
735 command
= cpystr(mc
->command
);
736 else if(!(command
= mc_cmd_bldr(mc
->command
, type
, subtype
, body
, tmp_file
, &err
)) && err
&& *err
)
737 q_status_message(SM_ORDER
, 5, 5, err
);
739 dprint((5, "built command: %s\n", command
? command
: "?"));
742 mc_cmd
= (MCAP_CMD_S
*)fs_get(sizeof(MCAP_CMD_S
));
743 mc_cmd
->command
= command
;
744 mc_cmd
->special_handling
= sp_handling
;
751 * mc_bld_test_cmd - build the command to test if the given type flies
753 * mc_cmd_bldr's tmp_file argument is NULL as we're not going to
754 * decode and write each and every MIME segment's data to a temp file
755 * when no test's going to use the data anyway.
758 mc_bld_test_cmd(char *controlstring
, int type
, char *subtype
, BODY
*body
)
760 return(mc_cmd_bldr(controlstring
, type
, subtype
, body
, NULL
, NULL
));
765 * mc_cmd_bldr - construct a command string to execute
767 * If tmp_file is null, then the contents of the given MIME segment
768 * is not provided. This is useful for building the "test=" string
769 * as it doesn't operate on the segment's data.
771 * The return value is an alloc'd copy of the command to be executed.
774 mc_cmd_bldr(char *controlstring
, int type
, char *subtype
,
775 BODY
*body
, char *tmp_file
, char **err
)
777 char *from
, *to
, *s
, *parm
;
778 int prefixed
= 0, used_tmp_file
= 0;
780 dprint((8, "- mc_cmd_bldr -\n"));
782 for(from
= controlstring
, to
= tmp_20k_buf
; *from
; ++from
){
783 if(prefixed
){ /* previous char was % */
786 case '%': /* turned \% into this earlier */
787 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
792 case 's': /* insert tmp_file name in cmd */
795 sstrncpy(&to
, tmp_file
, SIZEOF_20KBUF
-(to
-tmp_20k_buf
));
799 "mc_cmd_bldr: %%s in cmd but not supplied!\n"));
803 case 't': /* insert MIME type/subtype */
804 /* quote to prevent funny business */
805 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
808 sstrncpy(&to
, body_type_names(type
), SIZEOF_20KBUF
-(to
-tmp_20k_buf
));
810 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
813 sstrncpy(&to
, subtype
, SIZEOF_20KBUF
-(to
-tmp_20k_buf
));
815 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
820 case '{': /* insert requested MIME param */
821 if(F_OFF(F_DO_MAILCAP_PARAM_SUBST
, ps_global
)){
824 dprint((2, "mc_cmd_bldr: param subs %s\n",
827 if((s
= strindex(from
, '}')) != NULL
){
832 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
833 "Mailcap: see hidden feature %.200s (%%%.200s)",
834 feature_list_name(F_DO_MAILCAP_PARAM_SUBST
), from
);
843 s
= strindex(from
, '}');
845 q_status_message1(SM_ORDER
, 0, 4,
846 "Ignoring ill-formed parameter reference in mailcap file: %.200s", from
);
851 ++from
; /* from is the part inside the brackets now */
853 parm
= parameter_val(body
? body
->parameter
: NULL
, from
);
856 "mc_cmd_bldr: parameter %s = %s\n",
857 from
? from
: "?", parm
? parm
: "(not found)"));
860 * Quote parameter values for /bin/sh.
861 * Put single quotes around the whole thing but every time
862 * there is an actual single quote put it outside of the
863 * single quotes with a backslash in front of it. So the
864 * parameter value fred's car
865 * turns into 'fred'\''s car'
867 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
868 *to
++ = '\''; /* opening quote */
874 * Copy value, but quote single quotes for /bin/sh
875 * Backslash quote is ignored inside single quotes so
876 * have to put those outside of the single quotes.
877 * (The parm+1000 nonsense is to protect against
878 * malicious mail trying to overflow our buffer.)
880 * TCH - Change 2/8/1999
881 * Also quote the ` to prevent execution of arbitrary code
883 for(p
= parm
; *p
&& p
< parm
+1000; p
++){
884 if((*p
== '\'') || (*p
== '`')){
885 if(to
-tmp_20k_buf
+4 < SIZEOF_20KBUF
){
886 *to
++ = '\''; /* closing quote */
888 *to
++ = *p
; /* quoted character */
889 *to
++ = '\''; /* opening quote */
892 else if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
896 fs_give((void **) &parm
);
899 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
900 *to
++ = '\''; /* closing quote for /bin/sh */
902 *s
= '}'; /* restore */
907 * %n and %F are used by metamail to support otherwise
908 * unrecognized multipart Content-Types. Pine does
909 * not use these since we're only dealing with the individual
910 * parts at this point.
916 "Ignoring %s format code in mailcap file: %%%c\n",
917 (*from
== 'n' || *from
== 'F') ? "unimplemented"
923 else if(*from
== '%') /* next char is special */
925 else if(to
-tmp_20k_buf
< SIZEOF_20KBUF
) /* regular character, just copy */
929 if(to
-tmp_20k_buf
< SIZEOF_20KBUF
)
932 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
935 * file not specified, redirect to stdin
937 if(!used_tmp_file
&& tmp_file
)
938 snprintf(to
, SIZEOF_20KBUF
-(to
-tmp_20k_buf
), MC_ADD_TMP
, tmp_file
);
940 return(cpystr(tmp_20k_buf
));
950 MailcapEntry
*mc
= (MailcapEntry
*) fs_get(sizeof(MailcapEntry
));
951 memset(mc
, 0, sizeof(MailcapEntry
));
952 *MailcapData
.tail
= mc
;
953 MailcapData
.tail
= &mc
->next
;
959 * Free a list of mailcap entries
962 mc_free_entry(MailcapEntry
**mc
)
965 mc_free_entry(&(*mc
)->next
);
966 fs_give((void **) mc
);
974 mail_free_stringlist(&MailcapData
.raw
);
975 mc_free_entry(&MailcapData
.head
);