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