Blame


1 8e1fb8d5 2024-06-13 thomas.ad /*
2 8e1fb8d5 2024-06-13 thomas.ad * Copyright (c) 2024 Thomas Adam <thomas@xteddy.org>
3 8e1fb8d5 2024-06-13 thomas.ad *
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.
7 8e1fb8d5 2024-06-13 thomas.ad *
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.
15 8e1fb8d5 2024-06-13 thomas.ad */
16 8e1fb8d5 2024-06-13 thomas.ad
17 8e1fb8d5 2024-06-13 thomas.ad /*
18 8e1fb8d5 2024-06-13 thomas.ad * Handles reading mailmap files.
19 8e1fb8d5 2024-06-13 thomas.ad */
20 8e1fb8d5 2024-06-13 thomas.ad
21 8e1fb8d5 2024-06-13 thomas.ad #include "config.h"
22 8e1fb8d5 2024-06-13 thomas.ad
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>
30 8e1fb8d5 2024-06-13 thomas.ad
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"
34 8e1fb8d5 2024-06-13 thomas.ad
35 d37f7580 2024-06-18 op #define DEFAULT_MAILCAP_ENTRY "*/*; "DEFAULT_OPENER" %s"
36 8e1fb8d5 2024-06-13 thomas.ad
37 8e1fb8d5 2024-06-13 thomas.ad #define str_unappend(ch) if (sps.off > 0 && (ch) != EOF) { sps.off--; }
38 8e1fb8d5 2024-06-13 thomas.ad
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;
42 dcdea5d1 2024-06-14 op int esc;
43 8e1fb8d5 2024-06-13 thomas.ad };
44 8e1fb8d5 2024-06-13 thomas.ad static struct str_parse_state sps;
45 8e1fb8d5 2024-06-13 thomas.ad
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 **);
52 8e1fb8d5 2024-06-13 thomas.ad
53 8e1fb8d5 2024-06-13 thomas.ad struct mailcaplist mailcaps = TAILQ_HEAD_INITIALIZER(mailcaps);
54 8e1fb8d5 2024-06-13 thomas.ad
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 *);
60 8e1fb8d5 2024-06-13 thomas.ad
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",
67 8e1fb8d5 2024-06-13 thomas.ad NULL,
68 8e1fb8d5 2024-06-13 thomas.ad };
69 8e1fb8d5 2024-06-13 thomas.ad
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,
74 8e1fb8d5 2024-06-13 thomas.ad };
75 8e1fb8d5 2024-06-13 thomas.ad
76 f141d2d1 2024-06-14 op static char *
77 8e1fb8d5 2024-06-13 thomas.ad str_trim_whitespace(char *s)
78 8e1fb8d5 2024-06-13 thomas.ad {
79 8e1fb8d5 2024-06-13 thomas.ad char *t;
80 8e1fb8d5 2024-06-13 thomas.ad
81 8e1fb8d5 2024-06-13 thomas.ad s += strspn(s, " \t\n");
82 8e1fb8d5 2024-06-13 thomas.ad t = s + strlen(s) - 1;
83 8e1fb8d5 2024-06-13 thomas.ad
84 8e1fb8d5 2024-06-13 thomas.ad while (t >= s && isspace((unsigned char)*t))
85 8e1fb8d5 2024-06-13 thomas.ad *t-- = '\0';
86 f141d2d1 2024-06-14 op return s;
87 8e1fb8d5 2024-06-13 thomas.ad }
88 8e1fb8d5 2024-06-13 thomas.ad
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)
91 8e1fb8d5 2024-06-13 thomas.ad {
92 8e1fb8d5 2024-06-13 thomas.ad size_t al = 1;
93 8e1fb8d5 2024-06-13 thomas.ad
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");
96 8e1fb8d5 2024-06-13 thomas.ad
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;
100 8e1fb8d5 2024-06-13 thomas.ad }
101 8e1fb8d5 2024-06-13 thomas.ad
102 8e1fb8d5 2024-06-13 thomas.ad static int
103 8e1fb8d5 2024-06-13 thomas.ad str_getc(void)
104 8e1fb8d5 2024-06-13 thomas.ad {
105 dcdea5d1 2024-06-14 op int ch;
106 8e1fb8d5 2024-06-13 thomas.ad
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 ('\\');
110 8e1fb8d5 2024-06-13 thomas.ad }
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++];
115 8e1fb8d5 2024-06-13 thomas.ad
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;
119 8e1fb8d5 2024-06-13 thomas.ad }
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;
123 8e1fb8d5 2024-06-13 thomas.ad }
124 8e1fb8d5 2024-06-13 thomas.ad
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 ('\\');
129 8e1fb8d5 2024-06-13 thomas.ad }
130 8e1fb8d5 2024-06-13 thomas.ad return (ch);
131 8e1fb8d5 2024-06-13 thomas.ad }
132 8e1fb8d5 2024-06-13 thomas.ad }
133 8e1fb8d5 2024-06-13 thomas.ad
134 8e1fb8d5 2024-06-13 thomas.ad static char *
135 8e1fb8d5 2024-06-13 thomas.ad str_tokenise(void)
136 8e1fb8d5 2024-06-13 thomas.ad {
137 dcdea5d1 2024-06-14 op int ch;
138 8e1fb8d5 2024-06-13 thomas.ad char *buf;
139 8e1fb8d5 2024-06-13 thomas.ad size_t len;
140 dcdea5d1 2024-06-14 op enum {
141 dcdea5d1 2024-06-14 op APPEND,
142 dcdea5d1 2024-06-14 op DOUBLE_QUOTES,
143 dcdea5d1 2024-06-14 op SINGLE_QUOTES,
144 dcdea5d1 2024-06-14 op } state = APPEND;
145 8e1fb8d5 2024-06-13 thomas.ad
146 8e1fb8d5 2024-06-13 thomas.ad len = 0;
147 8e1fb8d5 2024-06-13 thomas.ad buf = calloc(1, sizeof *buf);
148 8e1fb8d5 2024-06-13 thomas.ad
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;
154 8e1fb8d5 2024-06-13 thomas.ad
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:
157 8e1fb8d5 2024-06-13 thomas.ad *
158 8e1fb8d5 2024-06-13 thomas.ad * foo "" bar
159 8e1fb8d5 2024-06-13 thomas.ad *
160 8e1fb8d5 2024-06-13 thomas.ad * don't lose that.
161 8e1fb8d5 2024-06-13 thomas.ad */
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;
165 8e1fb8d5 2024-06-13 thomas.ad }
166 8e1fb8d5 2024-06-13 thomas.ad
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;
171 8e1fb8d5 2024-06-13 thomas.ad }
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;
177 8e1fb8d5 2024-06-13 thomas.ad }
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;
181 8e1fb8d5 2024-06-13 thomas.ad }
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;
187 8e1fb8d5 2024-06-13 thomas.ad }
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;
191 8e1fb8d5 2024-06-13 thomas.ad }
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;
197 8e1fb8d5 2024-06-13 thomas.ad
198 8e1fb8d5 2024-06-13 thomas.ad }
199 8e1fb8d5 2024-06-13 thomas.ad }
200 8e1fb8d5 2024-06-13 thomas.ad str_unappend(ch);
201 8e1fb8d5 2024-06-13 thomas.ad buf[len] = '\0';
202 8e1fb8d5 2024-06-13 thomas.ad
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);
209 8e1fb8d5 2024-06-13 thomas.ad }
210 8e1fb8d5 2024-06-13 thomas.ad
211 8e1fb8d5 2024-06-13 thomas.ad return (buf);
212 8e1fb8d5 2024-06-13 thomas.ad }
213 8e1fb8d5 2024-06-13 thomas.ad
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)
216 8e1fb8d5 2024-06-13 thomas.ad {
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;
221 8e1fb8d5 2024-06-13 thomas.ad
222 8e1fb8d5 2024-06-13 thomas.ad if (str == NULL)
223 8e1fb8d5 2024-06-13 thomas.ad return -1;
224 8e1fb8d5 2024-06-13 thomas.ad
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);
229 8e1fb8d5 2024-06-13 thomas.ad
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. */
236 8e1fb8d5 2024-06-13 thomas.ad }
237 8e1fb8d5 2024-06-13 thomas.ad
238 8e1fb8d5 2024-06-13 thomas.ad ch = str_getc();
239 8e1fb8d5 2024-06-13 thomas.ad
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;
244 8e1fb8d5 2024-06-13 thomas.ad
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.
249 8e1fb8d5 2024-06-13 thomas.ad */
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;
254 8e1fb8d5 2024-06-13 thomas.ad }
255 8e1fb8d5 2024-06-13 thomas.ad
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);
259 8e1fb8d5 2024-06-13 thomas.ad }
260 8e1fb8d5 2024-06-13 thomas.ad out:
261 8e1fb8d5 2024-06-13 thomas.ad *ret_argv = argv;
262 8e1fb8d5 2024-06-13 thomas.ad *ret_argc = argc;
263 8e1fb8d5 2024-06-13 thomas.ad
264 8e1fb8d5 2024-06-13 thomas.ad return 0;
265 8e1fb8d5 2024-06-13 thomas.ad }
266 8e1fb8d5 2024-06-13 thomas.ad
267 8e1fb8d5 2024-06-13 thomas.ad void
268 8e1fb8d5 2024-06-13 thomas.ad argv_free(int argc, char **argv)
269 8e1fb8d5 2024-06-13 thomas.ad {
270 dcdea5d1 2024-06-14 op int i;
271 8e1fb8d5 2024-06-13 thomas.ad
272 8e1fb8d5 2024-06-13 thomas.ad if (argc == 0)
273 8e1fb8d5 2024-06-13 thomas.ad return;
274 8e1fb8d5 2024-06-13 thomas.ad
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);
278 8e1fb8d5 2024-06-13 thomas.ad }
279 8e1fb8d5 2024-06-13 thomas.ad
280 8e1fb8d5 2024-06-13 thomas.ad static FILE *
281 8e1fb8d5 2024-06-13 thomas.ad find_mailcap_file(void)
282 8e1fb8d5 2024-06-13 thomas.ad {
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;
287 8e1fb8d5 2024-06-13 thomas.ad
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);
296 8e1fb8d5 2024-06-13 thomas.ad
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);
301 8e1fb8d5 2024-06-13 thomas.ad }
302 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
303 8e1fb8d5 2024-06-13 thomas.ad }
304 8e1fb8d5 2024-06-13 thomas.ad
305 8e1fb8d5 2024-06-13 thomas.ad static struct mailcap *
306 8e1fb8d5 2024-06-13 thomas.ad mailcap_new(void)
307 8e1fb8d5 2024-06-13 thomas.ad {
308 8e1fb8d5 2024-06-13 thomas.ad struct mailcap *mc = NULL;
309 8e1fb8d5 2024-06-13 thomas.ad
310 3d89457c 2024-06-18 thomas.ad mc = xcalloc(1, sizeof *mc);
311 8e1fb8d5 2024-06-13 thomas.ad
312 8e1fb8d5 2024-06-13 thomas.ad return (mc);
313 8e1fb8d5 2024-06-13 thomas.ad }
314 8e1fb8d5 2024-06-13 thomas.ad
315 8e1fb8d5 2024-06-13 thomas.ad static int
316 8e1fb8d5 2024-06-13 thomas.ad parse_mailcap_line(char *input)
317 8e1fb8d5 2024-06-13 thomas.ad {
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;
321 8e1fb8d5 2024-06-13 thomas.ad
322 8e1fb8d5 2024-06-13 thomas.ad mc = mailcap_new();
323 8e1fb8d5 2024-06-13 thomas.ad
324 52ec20ae 2024-06-14 thomas.ad while ((line = strsep(&input, ";")) != NULL) {
325 f141d2d1 2024-06-14 op line = str_trim_whitespace(line);
326 8e1fb8d5 2024-06-13 thomas.ad
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);
330 52ec20ae 2024-06-14 thomas.ad ms++;
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);
334 52ec20ae 2024-06-14 thomas.ad ms++;
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;
342 8e1fb8d5 2024-06-13 thomas.ad }
343 8e1fb8d5 2024-06-13 thomas.ad }
344 8e1fb8d5 2024-06-13 thomas.ad
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);
349 8e1fb8d5 2024-06-13 thomas.ad }
350 8e1fb8d5 2024-06-13 thomas.ad TAILQ_INSERT_TAIL(&mailcaps, mc, mailcaps);
351 8e1fb8d5 2024-06-13 thomas.ad
352 8e1fb8d5 2024-06-13 thomas.ad return (0);
353 8e1fb8d5 2024-06-13 thomas.ad }
354 8e1fb8d5 2024-06-13 thomas.ad
355 8e1fb8d5 2024-06-13 thomas.ad void
356 8e1fb8d5 2024-06-13 thomas.ad mailcap_expand_cmd(struct mailcap *mc, char *mt, char *file)
357 8e1fb8d5 2024-06-13 thomas.ad {
358 dcdea5d1 2024-06-14 op char **argv;
359 dcdea5d1 2024-06-14 op int argc = 0, ret;
360 8e1fb8d5 2024-06-13 thomas.ad
361 8e1fb8d5 2024-06-13 thomas.ad if (mc->cmd == NULL)
362 8e1fb8d5 2024-06-13 thomas.ad return;
363 8e1fb8d5 2024-06-13 thomas.ad
364 8e1fb8d5 2024-06-13 thomas.ad ret = str_to_argv(mc->cmd, &argc, &argv);
365 8e1fb8d5 2024-06-13 thomas.ad
366 8e1fb8d5 2024-06-13 thomas.ad if (ret != 0 || argv == NULL)
367 8e1fb8d5 2024-06-13 thomas.ad return;
368 8e1fb8d5 2024-06-13 thomas.ad
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);
373 8e1fb8d5 2024-06-13 thomas.ad }
374 8e1fb8d5 2024-06-13 thomas.ad
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);
378 8e1fb8d5 2024-06-13 thomas.ad }
379 8e1fb8d5 2024-06-13 thomas.ad }
380 8e1fb8d5 2024-06-13 thomas.ad argv[argc++] = NULL;
381 8e1fb8d5 2024-06-13 thomas.ad
382 8e1fb8d5 2024-06-13 thomas.ad argv_free(mc->cmd_argc, mc->cmd_argv);
383 8e1fb8d5 2024-06-13 thomas.ad
384 8e1fb8d5 2024-06-13 thomas.ad mc->cmd_argv = argv;
385 8e1fb8d5 2024-06-13 thomas.ad mc->cmd_argc = argc;
386 8e1fb8d5 2024-06-13 thomas.ad }
387 8e1fb8d5 2024-06-13 thomas.ad
388 8e1fb8d5 2024-06-13 thomas.ad static struct mailcap *
389 8e1fb8d5 2024-06-13 thomas.ad mailcap_by_mimetype(const char *mt)
390 8e1fb8d5 2024-06-13 thomas.ad {
391 dcdea5d1 2024-06-14 op struct mailcap *mc;
392 8e1fb8d5 2024-06-13 thomas.ad
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);
396 8e1fb8d5 2024-06-13 thomas.ad }
397 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
398 8e1fb8d5 2024-06-13 thomas.ad }
399 8e1fb8d5 2024-06-13 thomas.ad
400 8e1fb8d5 2024-06-13 thomas.ad void
401 8e1fb8d5 2024-06-13 thomas.ad init_mailcap(void)
402 8e1fb8d5 2024-06-13 thomas.ad {
403 836a2edc 2024-06-14 op FILE *f;
404 836a2edc 2024-06-14 op char *copy;
405 836a2edc 2024-06-14 op
406 836a2edc 2024-06-14 op if ((f = find_mailcap_file()) != NULL) {
407 836a2edc 2024-06-14 op mailcap_parse(f);
408 836a2edc 2024-06-14 op fclose(f);
409 836a2edc 2024-06-14 op }
410 836a2edc 2024-06-14 op
411 3d89457c 2024-06-18 thomas.ad copy = xstrdup(DEFAULT_MAILCAP_ENTRY);
412 836a2edc 2024-06-14 op
413 836a2edc 2024-06-14 op /* Our own entry won't error. */
414 836a2edc 2024-06-14 op (void)parse_mailcap_line(copy);
415 836a2edc 2024-06-14 op free(copy);
416 836a2edc 2024-06-14 op }
417 836a2edc 2024-06-14 op
418 836a2edc 2024-06-14 op void
419 836a2edc 2024-06-14 op mailcap_parse(FILE *f)
420 836a2edc 2024-06-14 op {
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;
424 8e1fb8d5 2024-06-13 thomas.ad
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;
428 8e1fb8d5 2024-06-13 thomas.ad
429 f141d2d1 2024-06-14 op copy = str_trim_whitespace(copy);
430 8e1fb8d5 2024-06-13 thomas.ad
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;
434 8e1fb8d5 2024-06-13 thomas.ad }
435 8e1fb8d5 2024-06-13 thomas.ad
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);
439 8e1fb8d5 2024-06-13 thomas.ad }
440 f141d2d1 2024-06-14 op free(buf);
441 8e1fb8d5 2024-06-13 thomas.ad }
442 8e1fb8d5 2024-06-13 thomas.ad }
443 8e1fb8d5 2024-06-13 thomas.ad
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)
446 8e1fb8d5 2024-06-13 thomas.ad {
447 dcdea5d1 2024-06-14 op struct mailcap *mc = NULL;
448 8e1fb8d5 2024-06-13 thomas.ad
449 8e1fb8d5 2024-06-13 thomas.ad if (mime_type == NULL || filename == NULL)
450 8e1fb8d5 2024-06-13 thomas.ad return (NULL);
451 8e1fb8d5 2024-06-13 thomas.ad
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);
454 8e1fb8d5 2024-06-13 thomas.ad
455 8e1fb8d5 2024-06-13 thomas.ad return (mc);
456 8e1fb8d5 2024-06-13 thomas.ad }