cmd4.c revision 1.8 1 /* $NetBSD: cmd4.c,v 1.8 2025/09/18 21:26:44 rillig Exp $ */
2
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Anon Ymous.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33 #include <sys/cdefs.h>
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
37 #else
38 __RCSID("$NetBSD: cmd4.c,v 1.8 2025/09/18 21:26:44 rillig Exp $");
39 #endif
40 #endif /* not lint */
41
42 #include "rcv.h"
43 #include <util.h>
44 #include "extern.h"
45
46 /*
47 * Mail -- a mail program
48 *
49 * Still more user commands.
50 * XXX - should this be renamed smopts.c?
51 */
52
53 #if 0 /* XXX - debugging stuff - to be removed */
54 void showname(struct name *);
55 void
56 showname(struct name *np)
57 {
58
59 for (/*EMPTY*/; np; np = np->n_flink)
60 (void)printf("np: %p np->n_type: %d np->n_name: '%s' (%p)\n",
61 np, np->n_type, np->n_name, np->n_name);
62 }
63
64 __unused
65 static void
66 showsmopts(struct smopts_s *sp)
67 {
68
69 (void)printf("%s (%p)\n", sp->s_name, sp);
70 showname(sp->s_smopts);
71 }
72 #endif /* XXX - debugging stuff - to be removed */
73
74
75 static int
76 hashcase(const char *key)
77 {
78 char *lckey;
79
80 lckey = salloc(strlen(key) + 1);
81 istrcpy(lckey, key);
82 return hash(lckey);
83 }
84
85 static struct smopts_s *
86 findsmopts_core(const char *name)
87 {
88 struct smopts_s *sh;
89
90 for (sh = smoptstbl[hashcase(name)]; sh; sh = sh->s_link)
91 if (strcasecmp(sh->s_name, name) == 0)
92 return sh;
93 return NULL;
94 }
95
96 /*
97 * The exported smopts lookup routine.
98 */
99 PUBLIC struct smopts_s *
100 findsmopts(const char *name, int top_only)
101 {
102 const char *cp;
103 struct smopts_s *sh;
104
105 if ((sh = findsmopts_core(name)) != NULL)
106 return sh;
107
108 if (top_only)
109 return NULL;
110
111 for (cp = strchr(name, '@'); cp; cp = strchr(cp + 1, '.'))
112 if ((sh = findsmopts_core(cp)) != NULL)
113 return sh;
114
115 return findsmopts_core(".");
116 }
117
118 static void
119 printsmopts(const char *name)
120 {
121 struct smopts_s *sp;
122
123 if ((sp = findsmopts(name, 1)) == NULL) {
124 (void)printf("%s:\n", name);
125 return;
126 }
127 (void)printf("%s:\t%s\n", sp->s_name, detract(sp->s_smopts, GSMOPTS));
128 }
129
130 static void
131 printsmoptstbl(void)
132 {
133 struct smopts_s *sp;
134 const char **argv;
135 const char **ap;
136 int h;
137 int cnt;
138
139 cnt = 1;
140 for (h = 0; h < (int)__arraycount(smoptstbl); h++)
141 for (sp = smoptstbl[h]; sp && sp->s_name != NULL; sp = sp->s_link)
142 cnt++;
143
144 argv = salloc(cnt * sizeof(*argv));
145 ap = argv;
146 for (h = 0; h < (int)__arraycount(smoptstbl); h++)
147 for (sp = smoptstbl[h]; sp && sp->s_name != NULL; sp = sp->s_link)
148 *ap++ = sp->s_name;
149 *ap = NULL;
150 sort(argv);
151 for (ap = argv; *ap != NULL; ap++)
152 printsmopts(*ap);
153 }
154
155 static struct name *
156 name_expand(char *sname, int ntype)
157 {
158 struct grouphead *gh;
159 struct name *np;
160
161 if ((gh = findgroup(sname)) != NULL) {
162 np = gexpand(NULL, gh, 0, ntype);
163 }
164 else {
165 np = csalloc(1, sizeof(*np));
166 np->n_name = sname;
167 np->n_type = ntype;
168 }
169 return np;
170 }
171
172 static struct name *
173 ncalloc(char *str, int ntype)
174 {
175 struct name *np;
176
177 np = ecalloc(1, sizeof(*np));
178 np->n_type = ntype;
179 np->n_name = vcopy(str);
180 return np;
181 }
182
183 static void
184 smopts_core(const char *sname, char **argv)
185 {
186 struct smopts_s *sp;
187 struct name *np;
188 struct name *t;
189 int h;
190 char **ap;
191
192 if ((sp = findsmopts(sname, 1)) != NULL) {
193 char *cp;
194 cp = detract(sp->s_smopts, GSMOPTS);
195 (void)printf("%s already defined as: %s\n", sname, cp);
196 return;
197 }
198 h = hashcase(sname);
199 sp = ecalloc(1, sizeof(*sp));
200 sp->s_name = vcopy(sname);
201 if (smoptstbl[h])
202 sp->s_link = smoptstbl[h];
203 smoptstbl[h] = sp;
204
205 np = NULL;
206 for (ap = argv + 1; *ap != NULL; ap++) {
207 t = ncalloc(*ap, GSMOPTS);
208 if (sp->s_smopts == NULL)
209 sp->s_smopts = t;
210 else
211 np->n_flink = t;
212 t->n_blink = np;
213 np = t;
214 }
215 }
216
217 /*
218 * Takes a list of entries, expands them, and adds the results to the
219 * smopts table.
220 */
221 PUBLIC int
222 smoptscmd(void *v)
223 {
224 struct name *np;
225 char **argv;
226
227 argv = v;
228 if (*argv == NULL) {
229 printsmoptstbl();
230 return 0;
231 }
232 np = name_expand(argv[0], GTO);
233
234 if (argv[1] == NULL) {
235 for (/*EMPTY*/; np; np = np->n_flink)
236 printsmopts(np->n_name);
237 return 0;
238 }
239 for (/*EMPTY*/; np; np = np->n_flink)
240 smopts_core(np->n_name, argv);
241
242 return 0;
243 }
244
245 static void
246 free_name(struct name *np)
247 {
248 struct name *next_np;
249
250 for (/*EMPTY*/; np; np = next_np) {
251 next_np = np->n_flink;
252 free(np);
253 }
254 }
255
256 static void
257 delsmopts(char *name)
258 {
259 struct smopts_s *sp, *next;
260 struct smopts_s **last_link;
261
262 last_link = &smoptstbl[hashcase(name)];
263 for (sp = *last_link; sp; sp = next) {
264 next = sp->s_link;
265 if (strcasecmp(sp->s_name, name) == 0) {
266 *last_link = sp->s_link;
267 free_name(sp->s_smopts);
268 free(sp);
269 }
270 }
271 }
272
273 /*
274 * Takes a list of entries and removes them from the smoptstbl.
275 */
276 PUBLIC int
277 unsmoptscmd(void *v)
278 {
279 struct name *np;
280 char **ap;
281
282 for (ap = v; *ap != NULL; ap++)
283 for (np = name_expand(*ap, GTO); np; np = np->n_flink)
284 delsmopts(np->n_name);
285 return 0;
286 }
287
288 static struct name *
289 alloc_Header(char *str)
290 {
291 struct name *np;
292
293 /*
294 * Don't use salloc() routines here as these strings must persist.
295 */
296 np = ecalloc(1, sizeof(*np));
297 np->n_name = estrdup(str);
298 np->n_type = GMISC;
299 return np;
300 }
301
302 static int
303 free_Header(char *str)
304 {
305 struct name *np;
306 struct name *next_np;
307 size_t len;
308
309 len = strlen(str);
310 for (np = extra_headers; np != NULL; np = next_np) {
311 next_np = np->n_flink;
312 if (strncasecmp(np->n_name, str, len) == 0) {
313 if (np == extra_headers) {
314 extra_headers = np->n_flink;
315 if (extra_headers)
316 extra_headers->n_blink = NULL;
317 }
318 else {
319 struct name *bp;
320 struct name *fp;
321
322 bp = np->n_blink;
323 fp = np->n_flink;
324 if (bp)
325 bp->n_flink = fp;
326 if (fp)
327 fp->n_blink = bp;
328 }
329 if (np->n_name)
330 free(np->n_name);
331 free(np);
332 }
333 }
334 return 0;
335 }
336
337 /*
338 * Takes a string and includes it in the header.
339 */
340 PUBLIC int
341 Header(void *v)
342 {
343 struct name *np;
344 char *str;
345 char *p;
346
347 str = v;
348 if (str == NULL)
349 return 0;
350
351 (void)strip_WSP(str); /* strip trailing whitespace */
352
353 if (str[0] == '\0') { /* Show the extra headers */
354 for (np = extra_headers; np != NULL; np = np->n_flink)
355 (void)printf("%s\n", np->n_name);
356 return 0;
357 }
358
359 /*
360 * Check for a valid header line: find the end of its name.
361 */
362 for (p = str; *p != '\0' && *p != ':' && !is_WSP(*p); p++)
363 continue;
364
365 if (p[0] == ':' && p[1] == '\0') /* free headers of this type */
366 return free_Header(str);
367
368 /*
369 * Check for a valid header name.
370 */
371 if (*p != ':' || !is_WSP(p[1])) {
372 (void)printf("invalid header string: `%s'\n", str);
373 return 0;
374 }
375
376 np = alloc_Header(str);
377 if (extra_headers == NULL)
378 extra_headers = np;
379 else {
380 struct name *tp;
381
382 for (tp = extra_headers; tp->n_flink; tp = tp->n_flink)
383 continue;
384 tp->n_flink = np;
385 np->n_blink = tp;
386 }
387 return 0;
388 }
389