]> diplodocus.org Git - nmh/blob - uip/aliasbr.c
seq_setprev.c: Move interface to own file.
[nmh] / uip / aliasbr.c
1 /* aliasbr.c -- new aliasing mechanism
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/vfgets.h"
10 #include "sbr/getcpy.h"
11 #include "h/aliasbr.h"
12 #include "h/addrsbr.h"
13 #include "h/utils.h"
14 #include <pwd.h>
15
16 static int akvis;
17 static char *akerrst;
18
19 struct aka *akahead = NULL;
20 struct aka *akatail = NULL;
21
22 /*
23 * prototypes
24 */
25 static char *akval (struct aka *, char *);
26 static bool aleq (char *, char *) PURE;
27 static char *scanp (char *) PURE;
28 static char *getp (char *);
29 static char *seekp (char *, char *, char **);
30 static int addfile (struct aka *, char *);
31 static char *getalias (char *);
32 static void add_aka (struct aka *, char *);
33 static struct aka *akalloc (char *);
34
35
36 /* Do mh alias substitution on 's' and return the results. */
37 char *
38 akvalue (char *s)
39 {
40 char *v;
41
42 if (akahead == NULL)
43 alias (AliasFile);
44
45 akvis = -1;
46 v = akval (akahead, s);
47 if (akvis == -1)
48 akvis = 0;
49 return v;
50 }
51
52
53 int
54 akvisible (void)
55 {
56 return akvis;
57 }
58
59
60 char *
61 akresult (struct aka *ak)
62 {
63 char *cp = NULL, *dp, *pp;
64 struct adr *ad;
65
66 for (ad = ak->ak_addr; ad; ad = ad->ad_next) {
67 pp = ad->ad_local ? akval (ak->ak_next, ad->ad_text)
68 : getcpy (ad->ad_text);
69
70 if (cp) {
71 dp = cp;
72 cp = concat (cp, ",", pp, NULL);
73 free (dp);
74 free (pp);
75 }
76 else
77 cp = pp;
78 }
79
80 if (akvis == -1)
81 akvis = ak->ak_visible;
82 return cp;
83 }
84
85
86 static char *
87 akval (struct aka *ak, char *s)
88 {
89 if (!s)
90 return s; /* XXX */
91
92 /* It'd be tempting to check for a trailing semicolon and remove
93 it. But that would break the EXMH alias parser on what would
94 then be valid expressions:
95 http://lists.gnu.org/archive/html/nmh-workers/2012-10/msg00039.html
96 */
97
98 for (; ak; ak = ak->ak_next) {
99 if (aleq (s, ak->ak_name)) {
100 return akresult (ak);
101 }
102
103 if (strchr (s, ':')) {
104 /* The first address in a blind list will contain the
105 alias name, so try to match, but just with just the
106 address (not including the list name). If there's a
107 match, then replace the alias part with its
108 expansion. */
109
110 char *name = getname (s);
111 char *cp = NULL;
112
113 if (name) {
114 /* s is of the form "Blind list: address". If address
115 is an alias, expand it. */
116 struct mailname *mp = getm (name, NULL, 0, NULL, 0);
117
118 if (mp && mp->m_ingrp) {
119 char *gname = mh_xstrdup(FENDNULL(mp->m_gname));
120
121 /* FIXME: gname must be true; add() never returns NULL.
122 * Is some other test required? */
123 if (gname && aleq (name, ak->ak_name)) {
124 /* Will leak cp. */
125 cp = concat (gname, akresult (ak), NULL);
126 free (gname);
127 }
128 }
129
130 mnfree (mp);
131 }
132
133 /* Need to flush getname after use. */
134 while (getname ("")) continue;
135
136 if (cp) {
137 return cp;
138 }
139 }
140 }
141
142 return mh_xstrdup(s);
143 }
144
145
146 static bool
147 aleq (char *string, char *aliasent)
148 {
149 char c;
150
151 while ((c = *string++)) {
152 if (*aliasent == '*')
153 return true;
154 if (tolower((unsigned char)c) != tolower((unsigned char)*aliasent))
155 return false;
156 aliasent++;
157 }
158
159 return *aliasent == 0 || *aliasent == '*';
160 }
161
162
163 int
164 alias (char *file)
165 {
166 int i;
167 char *bp, *cp, *pp;
168 char lc, *ap;
169 struct aka *ak = NULL;
170 FILE *fp;
171
172 if (*file != '/'
173 && !has_prefix(file, "./") && !has_prefix(file, "../"))
174 file = etcpath (file);
175 if ((fp = fopen (file, "r")) == NULL) {
176 akerrst = file;
177 return AK_NOFILE;
178 }
179
180 while (vfgets (fp, &ap) == OK) {
181 bp = ap;
182 switch (*(pp = scanp (bp))) {
183 case '<': /* recurse a level */
184 if (!*(cp = getp (pp + 1))) {
185 akerrst = "'<' without alias-file";
186 fclose (fp);
187 return AK_ERROR;
188 }
189 if ((i = alias (cp)) != AK_OK) {
190 fclose (fp);
191 return i;
192 }
193 continue;
194 case ':': /* comment */
195 case ';':
196 case '#':
197 case 0:
198 continue;
199 }
200
201 akerrst = bp;
202 if (!*(cp = seekp (pp, &lc, &ap))) {
203 fclose (fp);
204 return AK_ERROR;
205 }
206 if (!(ak = akalloc (cp))) {
207 fclose (fp);
208 return AK_LIMIT;
209 }
210 switch (lc) {
211 case ':':
212 ak->ak_visible = false;
213 break;
214
215 case ';':
216 ak->ak_visible = true;
217 break;
218
219 default:
220 fclose (fp);
221 return AK_ERROR;
222 }
223
224 switch (*(pp = scanp (ap))) {
225 case 0: /* EOL */
226 fclose (fp);
227 return AK_ERROR;
228
229 case '<': /* read values from file */
230 if (!*(cp = getp (pp + 1))) {
231 fclose (fp);
232 return AK_ERROR;
233 }
234 if (!addfile (ak, cp)) {
235 fclose (fp);
236 return AK_NOFILE;
237 }
238 break;
239
240 default: /* list */
241 while ((cp = getalias (pp)))
242 add_aka (ak, cp);
243 break;
244 }
245 }
246
247 fclose (fp);
248 return AK_OK;
249 }
250
251
252 char *
253 akerror (int i)
254 {
255 static char buffer[BUFSIZ];
256
257 switch (i) {
258 case AK_NOFILE:
259 snprintf (buffer, sizeof(buffer), "unable to read '%s'", akerrst);
260 break;
261
262 case AK_ERROR:
263 snprintf (buffer, sizeof(buffer), "error in line '%s'", akerrst);
264 break;
265
266 case AK_LIMIT:
267 snprintf (buffer, sizeof(buffer), "out of memory while on '%s'", akerrst);
268 break;
269
270 default:
271 snprintf (buffer, sizeof(buffer), "unknown error (%d)", i);
272 break;
273 }
274
275 return buffer;
276 }
277
278
279 static char *
280 scanp (char *p)
281 {
282 while (isspace ((unsigned char) *p))
283 p++;
284 return p;
285 }
286
287
288 static char *
289 getp (char *p)
290 {
291 char *cp = scanp (p);
292
293 p = cp;
294 while (!isspace ((unsigned char) *cp) && *cp)
295 cp++;
296 *cp = 0;
297
298 return p;
299 }
300
301
302 static char *
303 seekp (char *p, char *c, char **a)
304 {
305 char *cp;
306
307 p = cp = scanp (p);
308 while (!isspace ((unsigned char) *cp) && *cp && *cp != ':' && *cp != ';')
309 cp++;
310 *c = *cp;
311 *cp++ = 0;
312 *a = cp;
313
314 return p;
315 }
316
317
318 static int
319 addfile (struct aka *ak, char *file)
320 {
321 char *cp;
322 char buffer[BUFSIZ];
323 FILE *fp;
324
325 if (!(fp = fopen (etcpath (file), "r"))) {
326 akerrst = file;
327 return 0;
328 }
329
330 while (fgets (buffer, sizeof buffer, fp))
331 while ((cp = getalias (buffer)))
332 add_aka (ak, cp);
333
334 fclose (fp);
335 return 1;
336 }
337
338
339 static char *
340 getalias (char *addrs)
341 {
342 char *pp, *qp;
343 static char *cp = NULL;
344
345 if (cp == NULL)
346 cp = addrs;
347 else if (*cp == 0)
348 return cp = NULL;
349
350 /* Remove leading any space from the address. */
351 for (pp = cp; isspace ((unsigned char) *pp); pp++)
352 continue;
353 if (*pp == 0)
354 return cp = NULL;
355 /* Find the end of the address. */
356 for (qp = pp; *qp != 0 && *qp != ','; qp++)
357 continue;
358 /* Set cp to point to the remainder of the addresses. */
359 if (*qp == ',')
360 *qp++ = 0;
361 for (cp = qp, qp--; qp > pp; qp--)
362 if (*qp != 0) {
363 if (isspace ((unsigned char) *qp))
364 *qp = 0;
365 else
366 break;
367 }
368
369 return pp;
370 }
371
372
373 static void
374 add_aka (struct aka *ak, char *pp)
375 {
376 struct adr *ad, *ld;
377
378 for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
379 if (!strcmp (pp, ad->ad_text))
380 return;
381
382 NEW(ad);
383 ad->ad_text = mh_xstrdup(pp);
384 ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL;
385 ad->ad_next = NULL;
386 if (ak->ak_addr)
387 ld->ad_next = ad;
388 else
389 ak->ak_addr = ad;
390 }
391
392
393 static struct aka *
394 akalloc (char *id)
395 {
396 struct aka *p;
397
398 NEW(p);
399 p->ak_name = getcpy (id);
400 p->ak_visible = false;
401 p->ak_addr = NULL;
402 p->ak_next = NULL;
403 if (akatail != NULL)
404 akatail->ak_next = p;
405 if (akahead == NULL)
406 akahead = p;
407 akatail = p;
408
409 return p;
410 }