2 8e1fb8d5 2024-06-13 thomas.ad * Copyright (c) 2024 Thomas Adam <thomas@xteddy.org>
4 8e1fb8d5 2024-06-13 thomas.ad * Permission to use, copy, modify, and distribute this software for any
5 8e1fb8d5 2024-06-13 thomas.ad * purpose with or without fee is hereby granted, provided that the above
6 8e1fb8d5 2024-06-13 thomas.ad * copyright notice and this permission notice appear in all copies.
8 8e1fb8d5 2024-06-13 thomas.ad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 8e1fb8d5 2024-06-13 thomas.ad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 8e1fb8d5 2024-06-13 thomas.ad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 8e1fb8d5 2024-06-13 thomas.ad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 8e1fb8d5 2024-06-13 thomas.ad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 8e1fb8d5 2024-06-13 thomas.ad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 8e1fb8d5 2024-06-13 thomas.ad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 8e1fb8d5 2024-06-13 thomas.ad * Handles reading mailmap files.
21 8e1fb8d5 2024-06-13 thomas.ad #include "config.h"
23 8e1fb8d5 2024-06-13 thomas.ad #include <ctype.h>
24 8e1fb8d5 2024-06-13 thomas.ad #include <errno.h>
25 8e1fb8d5 2024-06-13 thomas.ad #include <fnmatch.h>
26 8e1fb8d5 2024-06-13 thomas.ad #include <limits.h>
27 8e1fb8d5 2024-06-13 thomas.ad #include <stdio.h>
28 8e1fb8d5 2024-06-13 thomas.ad #include <stdlib.h>
29 8e1fb8d5 2024-06-13 thomas.ad #include <string.h>
31 8e1fb8d5 2024-06-13 thomas.ad #include "compat.h"
32 8e1fb8d5 2024-06-13 thomas.ad #include "mailcap.h"
33 3d89457c 2024-06-18 thomas.ad #include "xwrapper.h"
35 d37f7580 2024-06-18 op #define DEFAULT_MAILCAP_ENTRY "*/*; "DEFAULT_OPENER" %s"
37 8e1fb8d5 2024-06-13 thomas.ad #define str_unappend(ch) if (sps.off > 0 && (ch) != EOF) { sps.off--; }
39 8e1fb8d5 2024-06-13 thomas.ad struct str_parse_state {
40 8e1fb8d5 2024-06-13 thomas.ad char *buf;
41 dcdea5d1 2024-06-14 op size_t len, off;
44 8e1fb8d5 2024-06-13 thomas.ad static struct str_parse_state sps;
46 dcdea5d1 2024-06-14 op static void str_append(char **, size_t *, char);
47 dcdea5d1 2024-06-14 op static int str_getc(void);
48 dcdea5d1 2024-06-14 op static char *str_tokenise(void);
49 dcdea5d1 2024-06-14 op static int str_to_argv(char *, int *, char ***);
50 f141d2d1 2024-06-14 op static char *str_trim_whitespace(char *);
51 dcdea5d1 2024-06-14 op static void argv_free(int, char **);
53 8e1fb8d5 2024-06-13 thomas.ad struct mailcaplist mailcaps = TAILQ_HEAD_INITIALIZER(mailcaps);
55 8e1fb8d5 2024-06-13 thomas.ad static FILE *find_mailcap_file(void);
56 8e1fb8d5 2024-06-13 thomas.ad static struct mailcap *mailcap_new(void);
57 dcdea5d1 2024-06-14 op static void mailcap_expand_cmd(struct mailcap *, char *, char *);
58 dcdea5d1 2024-06-14 op static int parse_mailcap_line(char *);
59 8e1fb8d5 2024-06-13 thomas.ad static struct mailcap *mailcap_by_mimetype(const char *);
61 8e1fb8d5 2024-06-13 thomas.ad /* FIXME: $MAILCAPS can override this, but we don't check for that. */
62 8e1fb8d5 2024-06-13 thomas.ad static const char *default_mailcaps[] = {
63 8e1fb8d5 2024-06-13 thomas.ad "~/.mailcap",
64 8e1fb8d5 2024-06-13 thomas.ad "/etc/mailcap",
65 8e1fb8d5 2024-06-13 thomas.ad "/usr/etc/mailcap",
66 8e1fb8d5 2024-06-13 thomas.ad "/usr/local/etc/mailcap",
70 8e1fb8d5 2024-06-13 thomas.ad enum mailcap_section {
71 8e1fb8d5 2024-06-13 thomas.ad MAILCAP_MIME = 0,
72 8e1fb8d5 2024-06-13 thomas.ad MAILCAP_CMD,
73 52ec20ae 2024-06-14 thomas.ad MAILCAP_FLAGS,
77 8e1fb8d5 2024-06-13 thomas.ad str_trim_whitespace(char *s)
79 8e1fb8d5 2024-06-13 thomas.ad char *t;
81 8e1fb8d5 2024-06-13 thomas.ad s += strspn(s, " \t\n");
82 8e1fb8d5 2024-06-13 thomas.ad t = s + strlen(s) - 1;
84 8e1fb8d5 2024-06-13 thomas.ad while (t >= s && isspace((unsigned char)*t))
85 8e1fb8d5 2024-06-13 thomas.ad *t-- = '\0';
89 8e1fb8d5 2024-06-13 thomas.ad static void
90 8e1fb8d5 2024-06-13 thomas.ad str_append(char **buf, size_t *len, char add)
92 8e1fb8d5 2024-06-13 thomas.ad size_t al = 1;
94 8e1fb8d5 2024-06-13 thomas.ad if (al > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - al)
95 8e1fb8d5 2024-06-13 thomas.ad errx(1, "buffer is too big");
97 3d89457c 2024-06-18 thomas.ad *buf = xrealloc(*buf, (*len) + 1 + al);
98 8e1fb8d5 2024-06-13 thomas.ad memcpy((*buf) + *len, &add, al);
99 8e1fb8d5 2024-06-13 thomas.ad (*len) += al;
102 8e1fb8d5 2024-06-13 thomas.ad static int
103 8e1fb8d5 2024-06-13 thomas.ad str_getc(void)
107 8e1fb8d5 2024-06-13 thomas.ad if (sps.esc != 0) {
108 8e1fb8d5 2024-06-13 thomas.ad sps.esc--;
109 8e1fb8d5 2024-06-13 thomas.ad return ('\\');
111 8e1fb8d5 2024-06-13 thomas.ad for (;;) {
112 8e1fb8d5 2024-06-13 thomas.ad ch = EOF;
113 8e1fb8d5 2024-06-13 thomas.ad if (sps.off < sps.len)
114 8e1fb8d5 2024-06-13 thomas.ad ch = sps.buf[sps.off++];
116 8e1fb8d5 2024-06-13 thomas.ad if (ch == '\\') {
117 8e1fb8d5 2024-06-13 thomas.ad sps.esc++;
118 8e1fb8d5 2024-06-13 thomas.ad continue;
120 8e1fb8d5 2024-06-13 thomas.ad if (ch == '\n' && (sps.esc % 2) == 1) {
121 8e1fb8d5 2024-06-13 thomas.ad sps.esc--;
122 8e1fb8d5 2024-06-13 thomas.ad continue;
125 8e1fb8d5 2024-06-13 thomas.ad if (sps.esc != 0) {
126 8e1fb8d5 2024-06-13 thomas.ad str_unappend(ch);
127 8e1fb8d5 2024-06-13 thomas.ad sps.esc--;
128 8e1fb8d5 2024-06-13 thomas.ad return ('\\');
130 8e1fb8d5 2024-06-13 thomas.ad return (ch);
134 8e1fb8d5 2024-06-13 thomas.ad static char *
135 8e1fb8d5 2024-06-13 thomas.ad str_tokenise(void)
138 8e1fb8d5 2024-06-13 thomas.ad char *buf;
139 8e1fb8d5 2024-06-13 thomas.ad size_t len;
142 dcdea5d1 2024-06-14 op DOUBLE_QUOTES,
143 dcdea5d1 2024-06-14 op SINGLE_QUOTES,
144 dcdea5d1 2024-06-14 op } state = APPEND;
146 8e1fb8d5 2024-06-13 thomas.ad len = 0;
147 8e1fb8d5 2024-06-13 thomas.ad buf = calloc(1, sizeof *buf);
149 8e1fb8d5 2024-06-13 thomas.ad for (;;) {
150 8e1fb8d5 2024-06-13 thomas.ad ch = str_getc();
151 8e1fb8d5 2024-06-13 thomas.ad /* EOF or \n are always the end of the token. */
152 8e1fb8d5 2024-06-13 thomas.ad if (ch == EOF || (state == APPEND && ch == '\n'))
153 8e1fb8d5 2024-06-13 thomas.ad break;
155 8e1fb8d5 2024-06-13 thomas.ad /* Whitespace ends a token unless inside quotes. But if we've
156 8e1fb8d5 2024-06-13 thomas.ad * also been given:
158 8e1fb8d5 2024-06-13 thomas.ad * foo "" bar
160 8e1fb8d5 2024-06-13 thomas.ad * don't lose that.
162 8e1fb8d5 2024-06-13 thomas.ad if (((ch == ' ' || ch == '\t') && state == APPEND) &&
163 8e1fb8d5 2024-06-13 thomas.ad buf[0] != '\0') {
164 8e1fb8d5 2024-06-13 thomas.ad break;
167 8e1fb8d5 2024-06-13 thomas.ad if (ch == '\\' && state != SINGLE_QUOTES) {
168 8e1fb8d5 2024-06-13 thomas.ad ch = str_getc();
169 8e1fb8d5 2024-06-13 thomas.ad str_append(&buf, &len, ch);
170 8e1fb8d5 2024-06-13 thomas.ad continue;
172 8e1fb8d5 2024-06-13 thomas.ad switch (ch) {
173 8e1fb8d5 2024-06-13 thomas.ad case '\'':
174 8e1fb8d5 2024-06-13 thomas.ad if (state == APPEND) {
175 8e1fb8d5 2024-06-13 thomas.ad state = SINGLE_QUOTES;
176 8e1fb8d5 2024-06-13 thomas.ad continue;
178 8e1fb8d5 2024-06-13 thomas.ad if (state == SINGLE_QUOTES) {
179 8e1fb8d5 2024-06-13 thomas.ad state = APPEND;
180 8e1fb8d5 2024-06-13 thomas.ad continue;
182 8e1fb8d5 2024-06-13 thomas.ad break;
183 8e1fb8d5 2024-06-13 thomas.ad case '"':
184 8e1fb8d5 2024-06-13 thomas.ad if (state == APPEND) {
185 8e1fb8d5 2024-06-13 thomas.ad state = DOUBLE_QUOTES;
186 8e1fb8d5 2024-06-13 thomas.ad continue;
188 8e1fb8d5 2024-06-13 thomas.ad if (state == DOUBLE_QUOTES) {
189 8e1fb8d5 2024-06-13 thomas.ad state = APPEND;
190 8e1fb8d5 2024-06-13 thomas.ad continue;
192 8e1fb8d5 2024-06-13 thomas.ad break;
193 8e1fb8d5 2024-06-13 thomas.ad default:
194 8e1fb8d5 2024-06-13 thomas.ad /* Otherwise add the character to the buffer. */
195 8e1fb8d5 2024-06-13 thomas.ad str_append(&buf, &len, ch);
196 8e1fb8d5 2024-06-13 thomas.ad break;
200 8e1fb8d5 2024-06-13 thomas.ad str_unappend(ch);
201 8e1fb8d5 2024-06-13 thomas.ad buf[len] = '\0';
203 8e1fb8d5 2024-06-13 thomas.ad if (*buf == '\0' || state == SINGLE_QUOTES || state == DOUBLE_QUOTES) {
204 8e1fb8d5 2024-06-13 thomas.ad fprintf(stderr, "Unterminated string: <%s>, missing: %c\n",
205 8e1fb8d5 2024-06-13 thomas.ad buf, state == SINGLE_QUOTES ? '\'' :
206 8e1fb8d5 2024-06-13 thomas.ad state == DOUBLE_QUOTES ? '"' : ' ');
207 8e1fb8d5 2024-06-13 thomas.ad free(buf);
208 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
211 8e1fb8d5 2024-06-13 thomas.ad return (buf);
214 8e1fb8d5 2024-06-13 thomas.ad static int
215 8e1fb8d5 2024-06-13 thomas.ad str_to_argv(char *str, int *ret_argc, char ***ret_argv)
217 8e1fb8d5 2024-06-13 thomas.ad char *token;
218 8e1fb8d5 2024-06-13 thomas.ad int ch, next;
219 8e1fb8d5 2024-06-13 thomas.ad char **argv = NULL;
220 dcdea5d1 2024-06-14 op int argc = 0;
222 8e1fb8d5 2024-06-13 thomas.ad if (str == NULL)
223 8e1fb8d5 2024-06-13 thomas.ad return -1;
225 8e1fb8d5 2024-06-13 thomas.ad free(sps.buf);
226 5acf19ec 2024-06-22 thomas.ad memset(&sps, 0, sizeof(sps));
227 3d89457c 2024-06-18 thomas.ad sps.buf = xstrdup(str);
228 8e1fb8d5 2024-06-13 thomas.ad sps.len = strlen(sps.buf);
230 8e1fb8d5 2024-06-13 thomas.ad for (;;) {
231 8e1fb8d5 2024-06-13 thomas.ad if (str[0] == '#') {
232 8e1fb8d5 2024-06-13 thomas.ad /* Skip comment. */
233 8e1fb8d5 2024-06-13 thomas.ad next = str_getc();
234 8e1fb8d5 2024-06-13 thomas.ad while (((next = str_getc()) != EOF))
235 8e1fb8d5 2024-06-13 thomas.ad ; /* Nothing. */
238 8e1fb8d5 2024-06-13 thomas.ad ch = str_getc();
240 8e1fb8d5 2024-06-13 thomas.ad if (ch == '\n' || ch == EOF)
241 8e1fb8d5 2024-06-13 thomas.ad goto out;
242 8e1fb8d5 2024-06-13 thomas.ad if (ch == ' ' || ch == '\t')
243 8e1fb8d5 2024-06-13 thomas.ad continue;
245 8e1fb8d5 2024-06-13 thomas.ad /* Tokenise the string according to quoting rules. Note that
246 8e1fb8d5 2024-06-13 thomas.ad * the string is stepped-back by one character to make the
247 8e1fb8d5 2024-06-13 thomas.ad * tokenisation easier, and not to kick-off the state of the
248 8e1fb8d5 2024-06-13 thomas.ad * parsing from this point.
250 8e1fb8d5 2024-06-13 thomas.ad str_unappend(ch);
251 8e1fb8d5 2024-06-13 thomas.ad if ((token = str_tokenise()) == NULL) {
252 8e1fb8d5 2024-06-13 thomas.ad argv_free(argc, argv);
253 8e1fb8d5 2024-06-13 thomas.ad return -1;
256 8e1fb8d5 2024-06-13 thomas.ad /* Add to argv. */
257 3d89457c 2024-06-18 thomas.ad argv = xreallocarray(argv, argc + 1, sizeof *argv);
258 3d89457c 2024-06-18 thomas.ad argv[argc++] = xstrdup(token);
261 8e1fb8d5 2024-06-13 thomas.ad *ret_argv = argv;
262 8e1fb8d5 2024-06-13 thomas.ad *ret_argc = argc;
264 8e1fb8d5 2024-06-13 thomas.ad return 0;
268 8e1fb8d5 2024-06-13 thomas.ad argv_free(int argc, char **argv)
272 8e1fb8d5 2024-06-13 thomas.ad if (argc == 0)
273 8e1fb8d5 2024-06-13 thomas.ad return;
275 8e1fb8d5 2024-06-13 thomas.ad for (i = 0; i < argc; i++)
276 8e1fb8d5 2024-06-13 thomas.ad free(argv[i]);
277 8e1fb8d5 2024-06-13 thomas.ad free(argv);
280 8e1fb8d5 2024-06-13 thomas.ad static FILE *
281 8e1fb8d5 2024-06-13 thomas.ad find_mailcap_file(void)
283 8e1fb8d5 2024-06-13 thomas.ad FILE *fp;
284 dcdea5d1 2024-06-14 op char **entry = (char **)default_mailcaps;
285 dcdea5d1 2024-06-14 op char *home = NULL;
286 dcdea5d1 2024-06-14 op char *expanded = NULL;
288 8e1fb8d5 2024-06-13 thomas.ad for (; *entry != NULL; entry++) {
289 8e1fb8d5 2024-06-13 thomas.ad if (strncmp(*entry, "~/", 2) == 0) {
290 8e1fb8d5 2024-06-13 thomas.ad *entry += 2;
291 8e1fb8d5 2024-06-13 thomas.ad if ((home = getenv("HOME")) == NULL)
292 8e1fb8d5 2024-06-13 thomas.ad errx(1, "HOME not set");
293 8e1fb8d5 2024-06-13 thomas.ad asprintf(&expanded, "%s/%s", home, *entry);
294 8e1fb8d5 2024-06-13 thomas.ad } else
295 8e1fb8d5 2024-06-13 thomas.ad asprintf(&expanded, "%s", *entry);
297 8e1fb8d5 2024-06-13 thomas.ad fp = fopen(expanded, "r");
298 8e1fb8d5 2024-06-13 thomas.ad free(expanded);
299 8e1fb8d5 2024-06-13 thomas.ad if (fp != NULL)
300 8e1fb8d5 2024-06-13 thomas.ad return (fp);
302 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
305 8e1fb8d5 2024-06-13 thomas.ad static struct mailcap *
306 8e1fb8d5 2024-06-13 thomas.ad mailcap_new(void)
308 8e1fb8d5 2024-06-13 thomas.ad struct mailcap *mc = NULL;
310 3d89457c 2024-06-18 thomas.ad mc = xcalloc(1, sizeof *mc);
312 8e1fb8d5 2024-06-13 thomas.ad return (mc);
315 8e1fb8d5 2024-06-13 thomas.ad static int
316 8e1fb8d5 2024-06-13 thomas.ad parse_mailcap_line(char *input)
318 8e1fb8d5 2024-06-13 thomas.ad struct mailcap *mc;
319 52ec20ae 2024-06-14 thomas.ad int ms = 0;
320 dcdea5d1 2024-06-14 op char *line = NULL;
322 8e1fb8d5 2024-06-13 thomas.ad mc = mailcap_new();
324 52ec20ae 2024-06-14 thomas.ad while ((line = strsep(&input, ";")) != NULL) {
325 f141d2d1 2024-06-14 op line = str_trim_whitespace(line);
327 8e1fb8d5 2024-06-13 thomas.ad switch (ms) {
328 8e1fb8d5 2024-06-13 thomas.ad case MAILCAP_MIME:
329 3d89457c 2024-06-18 thomas.ad mc->mime_type = xstrdup(line);
331 8e1fb8d5 2024-06-13 thomas.ad break;
332 8e1fb8d5 2024-06-13 thomas.ad case MAILCAP_CMD:
333 3d89457c 2024-06-18 thomas.ad mc->cmd = xstrdup(line);
335 8e1fb8d5 2024-06-13 thomas.ad break;
336 52ec20ae 2024-06-14 thomas.ad case MAILCAP_FLAGS:
337 8e1fb8d5 2024-06-13 thomas.ad if (strcmp(line, "needsterminal") == 0)
338 8e1fb8d5 2024-06-13 thomas.ad mc->flags |= MAILCAP_NEEDSTERMINAL;
339 8e1fb8d5 2024-06-13 thomas.ad if (strcmp(line, "copiousoutput") == 0)
340 8e1fb8d5 2024-06-13 thomas.ad mc->flags |= MAILCAP_COPIOUSOUTPUT;
341 8e1fb8d5 2024-06-13 thomas.ad break;
345 8e1fb8d5 2024-06-13 thomas.ad if (line != NULL && *line != '\0') {
346 8e1fb8d5 2024-06-13 thomas.ad fprintf(stderr, "mailcap: trailing: %s: skipping...\n", line);
347 8e1fb8d5 2024-06-13 thomas.ad free(mc);
348 8e1fb8d5 2024-06-13 thomas.ad return (-1);
350 8e1fb8d5 2024-06-13 thomas.ad TAILQ_INSERT_TAIL(&mailcaps, mc, mailcaps);
352 8e1fb8d5 2024-06-13 thomas.ad return (0);
356 8e1fb8d5 2024-06-13 thomas.ad mailcap_expand_cmd(struct mailcap *mc, char *mt, char *file)
359 dcdea5d1 2024-06-14 op int argc = 0, ret;
361 8e1fb8d5 2024-06-13 thomas.ad if (mc->cmd == NULL)
362 8e1fb8d5 2024-06-13 thomas.ad return;
364 8e1fb8d5 2024-06-13 thomas.ad ret = str_to_argv(mc->cmd, &argc, &argv);
366 8e1fb8d5 2024-06-13 thomas.ad if (ret != 0 || argv == NULL)
367 8e1fb8d5 2024-06-13 thomas.ad return;
369 8e1fb8d5 2024-06-13 thomas.ad for (int z = 0; z < argc; z++) {
370 8e1fb8d5 2024-06-13 thomas.ad if (strcmp(argv[z], "%s") == 0) {
371 8e1fb8d5 2024-06-13 thomas.ad free(argv[z]);
372 3d89457c 2024-06-18 thomas.ad argv[z] = xstrdup(file);
375 8e1fb8d5 2024-06-13 thomas.ad if (strcmp(argv[z], "%t") == 0) {
376 8e1fb8d5 2024-06-13 thomas.ad free(argv[z]);
377 3d89457c 2024-06-18 thomas.ad argv[z] = xstrdup(mt);
380 8e1fb8d5 2024-06-13 thomas.ad argv[argc++] = NULL;
382 8e1fb8d5 2024-06-13 thomas.ad argv_free(mc->cmd_argc, mc->cmd_argv);
384 8e1fb8d5 2024-06-13 thomas.ad mc->cmd_argv = argv;
385 8e1fb8d5 2024-06-13 thomas.ad mc->cmd_argc = argc;
388 8e1fb8d5 2024-06-13 thomas.ad static struct mailcap *
389 8e1fb8d5 2024-06-13 thomas.ad mailcap_by_mimetype(const char *mt)
391 dcdea5d1 2024-06-14 op struct mailcap *mc;
393 8e1fb8d5 2024-06-13 thomas.ad TAILQ_FOREACH(mc, &mailcaps, mailcaps) {
394 8e1fb8d5 2024-06-13 thomas.ad if (fnmatch(mc->mime_type, mt, 0) == 0)
395 8e1fb8d5 2024-06-13 thomas.ad return (mc);
397 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
401 8e1fb8d5 2024-06-13 thomas.ad init_mailcap(void)
406 836a2edc 2024-06-14 op if ((f = find_mailcap_file()) != NULL) {
407 836a2edc 2024-06-14 op mailcap_parse(f);
411 3d89457c 2024-06-18 thomas.ad copy = xstrdup(DEFAULT_MAILCAP_ENTRY);
413 836a2edc 2024-06-14 op /* Our own entry won't error. */
414 836a2edc 2024-06-14 op (void)parse_mailcap_line(copy);
419 836a2edc 2024-06-14 op mailcap_parse(FILE *f)
421 8e1fb8d5 2024-06-13 thomas.ad const char delims[3] = {'\\', '\\', '\0'};
422 dcdea5d1 2024-06-14 op char *buf, *copy;
423 8e1fb8d5 2024-06-13 thomas.ad size_t line = 0;
425 8e1fb8d5 2024-06-13 thomas.ad while ((buf = fparseln(f, NULL, &line, delims, 0)) != NULL) {
426 8e1fb8d5 2024-06-13 thomas.ad memset(&sps, 0, sizeof sps);
427 8e1fb8d5 2024-06-13 thomas.ad copy = buf;
429 f141d2d1 2024-06-14 op copy = str_trim_whitespace(copy);
431 22d3f8e4 2024-06-14 op if (*copy == '#' || *copy == '\0') {
432 8e1fb8d5 2024-06-13 thomas.ad free(buf);
433 8e1fb8d5 2024-06-13 thomas.ad continue;
436 8e1fb8d5 2024-06-13 thomas.ad if (parse_mailcap_line(copy) == -1) {
437 8e1fb8d5 2024-06-13 thomas.ad fprintf(stderr, "Error with entry: <<%s>>, line: %ld\n",
438 8e1fb8d5 2024-06-13 thomas.ad copy, line);
444 8e1fb8d5 2024-06-13 thomas.ad struct mailcap *
445 8e1fb8d5 2024-06-13 thomas.ad mailcap_cmd_from_mimetype(char *mime_type, char *filename)
447 dcdea5d1 2024-06-14 op struct mailcap *mc = NULL;
449 8e1fb8d5 2024-06-13 thomas.ad if (mime_type == NULL || filename == NULL)
450 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
452 8e1fb8d5 2024-06-13 thomas.ad if ((mc = mailcap_by_mimetype(mime_type)) != NULL)
453 8e1fb8d5 2024-06-13 thomas.ad mailcap_expand_cmd(mc, mime_type, filename);
455 8e1fb8d5 2024-06-13 thomas.ad return (mc);