cmd3.c revision 1.33 1 1.31 christos /* $NetBSD: cmd3.c,v 1.33 2006/11/28 18:45:32 christos Exp $ */
2 1.5 christos
3 1.1 cgd /*
4 1.4 deraadt * Copyright (c) 1980, 1993
5 1.4 deraadt * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.21 agc * 3. Neither the name of the University nor the names of its contributors
16 1.1 cgd * may be used to endorse or promote products derived from this software
17 1.1 cgd * without specific prior written permission.
18 1.1 cgd *
19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cgd * SUCH DAMAGE.
30 1.1 cgd */
31 1.1 cgd
32 1.9 lukem #include <sys/cdefs.h>
33 1.1 cgd #ifndef lint
34 1.5 christos #if 0
35 1.6 tls static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
36 1.5 christos #else
37 1.31 christos __RCSID("$NetBSD: cmd3.c,v 1.33 2006/11/28 18:45:32 christos Exp $");
38 1.5 christos #endif
39 1.1 cgd #endif /* not lint */
40 1.1 cgd
41 1.1 cgd #include "rcv.h"
42 1.31 christos #include <util.h>
43 1.4 deraadt #include "extern.h"
44 1.31 christos #include "mime.h"
45 1.33 christos #include "thread.h"
46 1.1 cgd
47 1.1 cgd /*
48 1.1 cgd * Mail -- a mail program
49 1.1 cgd *
50 1.1 cgd * Still more user commands.
51 1.1 cgd */
52 1.33 christos
53 1.1 cgd
54 1.1 cgd /*
55 1.33 christos * Do a dictionary order comparison of the arguments from
56 1.33 christos * qsort.
57 1.1 cgd */
58 1.33 christos static int
59 1.33 christos diction(const void *a, const void *b)
60 1.1 cgd {
61 1.33 christos return strcmp(*(const char *const *)a, *(const char *const *)b);
62 1.1 cgd }
63 1.1 cgd
64 1.1 cgd /*
65 1.33 christos * Sort the passed string vector into ascending dictionary
66 1.33 christos * order.
67 1.1 cgd */
68 1.33 christos PUBLIC void
69 1.33 christos sort(const char **list)
70 1.1 cgd {
71 1.33 christos const char **ap;
72 1.1 cgd
73 1.33 christos for (ap = list; *ap != NULL; ap++)
74 1.33 christos continue;
75 1.33 christos if (ap-list < 2)
76 1.33 christos return;
77 1.33 christos qsort(list, (size_t)(ap-list), sizeof(*list), diction);
78 1.1 cgd }
79 1.1 cgd
80 1.1 cgd /*
81 1.1 cgd * Expand the shell escape by expanding unescaped !'s into the
82 1.1 cgd * last issued command where possible.
83 1.1 cgd */
84 1.33 christos static int
85 1.12 wiz bangexp(char *str)
86 1.1 cgd {
87 1.33 christos static char lastbang[128];
88 1.33 christos char bangbuf[LINESIZE];
89 1.9 lukem char *cp, *cp2;
90 1.9 lukem int n;
91 1.1 cgd int changed = 0;
92 1.1 cgd
93 1.1 cgd cp = str;
94 1.1 cgd cp2 = bangbuf;
95 1.33 christos n = sizeof(bangbuf); /* bytes left in bangbuf */
96 1.1 cgd while (*cp) {
97 1.1 cgd if (*cp == '!') {
98 1.33 christos if (n < (int)strlen(lastbang)) {
99 1.1 cgd overf:
100 1.27 christos (void)printf("Command buffer overflow\n");
101 1.33 christos return -1;
102 1.1 cgd }
103 1.1 cgd changed++;
104 1.27 christos (void)strcpy(cp2, lastbang);
105 1.1 cgd cp2 += strlen(lastbang);
106 1.1 cgd n -= strlen(lastbang);
107 1.1 cgd cp++;
108 1.1 cgd continue;
109 1.1 cgd }
110 1.1 cgd if (*cp == '\\' && cp[1] == '!') {
111 1.1 cgd if (--n <= 1)
112 1.1 cgd goto overf;
113 1.1 cgd *cp2++ = '!';
114 1.1 cgd cp += 2;
115 1.1 cgd changed++;
116 1.1 cgd }
117 1.1 cgd if (--n <= 1)
118 1.1 cgd goto overf;
119 1.1 cgd *cp2++ = *cp++;
120 1.1 cgd }
121 1.1 cgd *cp2 = 0;
122 1.1 cgd if (changed) {
123 1.27 christos (void)printf("!%s\n", bangbuf);
124 1.27 christos (void)fflush(stdout);
125 1.1 cgd }
126 1.27 christos (void)strcpy(str, bangbuf);
127 1.33 christos (void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
128 1.33 christos return 0;
129 1.33 christos }
130 1.33 christos
131 1.33 christos /*
132 1.33 christos * Process a shell escape by saving signals, ignoring signals,
133 1.33 christos * and forking a sh -c
134 1.33 christos */
135 1.33 christos PUBLIC int
136 1.33 christos shell(void *v)
137 1.33 christos {
138 1.33 christos char *str = v;
139 1.33 christos sig_t sigint = signal(SIGINT, SIG_IGN);
140 1.33 christos const char *shellcmd;
141 1.33 christos char cmd[LINESIZE];
142 1.33 christos
143 1.33 christos (void)strcpy(cmd, str);
144 1.33 christos if (bangexp(cmd) < 0)
145 1.33 christos return 1;
146 1.33 christos if ((shellcmd = value(ENAME_SHELL)) == NULL)
147 1.33 christos shellcmd = _PATH_CSHELL;
148 1.33 christos (void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL);
149 1.33 christos (void)signal(SIGINT, sigint);
150 1.33 christos (void)printf("!\n");
151 1.33 christos return 0;
152 1.33 christos }
153 1.33 christos
154 1.33 christos /*
155 1.33 christos * Fork an interactive shell.
156 1.33 christos */
157 1.33 christos /*ARGSUSED*/
158 1.33 christos PUBLIC int
159 1.33 christos dosh(void *v __unused)
160 1.33 christos {
161 1.33 christos sig_t sigint = signal(SIGINT, SIG_IGN);
162 1.33 christos const char *shellcmd;
163 1.33 christos
164 1.33 christos if ((shellcmd = value(ENAME_SHELL)) == NULL)
165 1.33 christos shellcmd = _PATH_CSHELL;
166 1.33 christos (void)run_command(shellcmd, 0, 0, 1, NULL);
167 1.33 christos (void)signal(SIGINT, sigint);
168 1.33 christos (void)putchar('\n');
169 1.33 christos return 0;
170 1.1 cgd }
171 1.1 cgd
172 1.1 cgd /*
173 1.1 cgd * Print out a nice help message from some file or another.
174 1.1 cgd */
175 1.1 cgd
176 1.27 christos /*ARGSUSED*/
177 1.33 christos PUBLIC int
178 1.31 christos help(void *v __unused)
179 1.1 cgd {
180 1.9 lukem int c;
181 1.9 lukem FILE *f;
182 1.1 cgd
183 1.1 cgd if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
184 1.17 wiz warn(_PATH_HELP);
185 1.33 christos return 1;
186 1.1 cgd }
187 1.1 cgd while ((c = getc(f)) != EOF)
188 1.27 christos (void)putchar(c);
189 1.27 christos (void)Fclose(f);
190 1.33 christos return 0;
191 1.1 cgd }
192 1.1 cgd
193 1.1 cgd /*
194 1.1 cgd * Change user's working directory.
195 1.1 cgd */
196 1.33 christos PUBLIC int
197 1.12 wiz schdir(void *v)
198 1.1 cgd {
199 1.5 christos char **arglist = v;
200 1.26 christos const char *cp;
201 1.1 cgd
202 1.14 wiz if (*arglist == NULL)
203 1.1 cgd cp = homedir;
204 1.1 cgd else
205 1.14 wiz if ((cp = expand(*arglist)) == NULL)
206 1.33 christos return 1;
207 1.1 cgd if (chdir(cp) < 0) {
208 1.17 wiz warn("%s", cp);
209 1.33 christos return 1;
210 1.1 cgd }
211 1.1 cgd return 0;
212 1.1 cgd }
213 1.1 cgd
214 1.33 christos /*
215 1.33 christos * Return the smopts field if "ReplyAsRecipient" is defined.
216 1.33 christos */
217 1.29 christos static struct name *
218 1.29 christos set_smopts(struct message *mp)
219 1.29 christos {
220 1.29 christos char *cp;
221 1.29 christos struct name *np = NULL;
222 1.33 christos char *reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
223 1.29 christos
224 1.30 christos if (reply_as_recipient &&
225 1.29 christos (cp = skin(hfield("to", mp))) != NULL &&
226 1.30 christos extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
227 1.29 christos char *p, *q;
228 1.31 christos size_t len = strlen(cp);
229 1.31 christos /*
230 1.31 christos * XXX - perhaps we always want to ignore
231 1.31 christos * "undisclosed-recipients:;" ?
232 1.31 christos */
233 1.31 christos for (p = q = reply_as_recipient; *p; p = q) {
234 1.31 christos while (*q != '\0' && *q != ',' && !isblank((unsigned char)*q))
235 1.29 christos q++;
236 1.31 christos if (p + len == q && strncasecmp(cp, p, len) == 0)
237 1.29 christos return np;
238 1.31 christos while (*q == ',' || isblank((unsigned char)*q))
239 1.29 christos q++;
240 1.29 christos }
241 1.29 christos np = extract(__UNCONST("-f"), GSMOPTS);
242 1.29 christos np = cat(np, extract(cp, GSMOPTS));
243 1.29 christos }
244 1.29 christos
245 1.29 christos return np;
246 1.29 christos }
247 1.29 christos
248 1.1 cgd /*
249 1.33 christos * Modify the subject we are replying to to begin with Re: if
250 1.33 christos * it does not already.
251 1.33 christos */
252 1.33 christos static char *
253 1.33 christos reedit(char *subj)
254 1.33 christos {
255 1.33 christos char *newsubj;
256 1.33 christos
257 1.33 christos if (subj == NULL)
258 1.33 christos return NULL;
259 1.33 christos if ((subj[0] == 'r' || subj[0] == 'R') &&
260 1.33 christos (subj[1] == 'e' || subj[1] == 'E') &&
261 1.33 christos subj[2] == ':')
262 1.33 christos return subj;
263 1.33 christos newsubj = salloc(strlen(subj) + 5);
264 1.33 christos (void)strcpy(newsubj, "Re: ");
265 1.33 christos (void)strcpy(newsubj + 4, subj);
266 1.33 christos return newsubj;
267 1.33 christos }
268 1.33 christos
269 1.33 christos /*
270 1.33 christos * Set the "In-Reply-To" and "References" header fields appropriately.
271 1.33 christos * Used in replies.
272 1.33 christos */
273 1.33 christos static void
274 1.33 christos set_ident_fields(struct header *hp, struct message *mp)
275 1.33 christos {
276 1.33 christos char *in_reply_to;
277 1.33 christos char *references;
278 1.33 christos
279 1.33 christos in_reply_to = hfield("message-id", mp);
280 1.33 christos hp->h_in_reply_to = in_reply_to;
281 1.33 christos
282 1.33 christos references = hfield("references", mp);
283 1.33 christos hp->h_references = extract(references, GMISC);
284 1.33 christos hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
285 1.33 christos }
286 1.33 christos
287 1.33 christos /*
288 1.1 cgd * Reply to a list of messages. Extract each name from the
289 1.1 cgd * message header and send them off to mail1()
290 1.1 cgd */
291 1.33 christos static int
292 1.12 wiz _respond(int *msgvec)
293 1.1 cgd {
294 1.1 cgd struct message *mp;
295 1.1 cgd char *cp, *rcv, *replyto;
296 1.1 cgd char **ap;
297 1.1 cgd struct name *np;
298 1.1 cgd struct header head;
299 1.1 cgd
300 1.33 christos /* ensure that all header fields are initially NULL */
301 1.33 christos (void)memset(&head, 0, sizeof(head));
302 1.33 christos
303 1.1 cgd if (msgvec[1] != 0) {
304 1.27 christos (void)printf("Sorry, can't reply to multiple messages at once\n");
305 1.33 christos return 1;
306 1.1 cgd }
307 1.33 christos mp = get_message(msgvec[0]);
308 1.1 cgd touch(mp);
309 1.1 cgd dot = mp;
310 1.14 wiz if ((rcv = skin(hfield("from", mp))) == NULL)
311 1.1 cgd rcv = skin(nameof(mp, 1));
312 1.14 wiz if ((replyto = skin(hfield("reply-to", mp))) != NULL)
313 1.1 cgd np = extract(replyto, GTO);
314 1.14 wiz else if ((cp = skin(hfield("to", mp))) != NULL)
315 1.1 cgd np = extract(cp, GTO);
316 1.1 cgd else
317 1.15 wiz np = NULL;
318 1.1 cgd np = elide(np);
319 1.1 cgd /*
320 1.1 cgd * Delete my name from the reply list,
321 1.1 cgd * and with it, all my alternate names.
322 1.1 cgd */
323 1.1 cgd np = delname(np, myname);
324 1.1 cgd if (altnames)
325 1.1 cgd for (ap = altnames; *ap; ap++)
326 1.1 cgd np = delname(np, *ap);
327 1.15 wiz if (np != NULL && replyto == NULL)
328 1.1 cgd np = cat(np, extract(rcv, GTO));
329 1.15 wiz else if (np == NULL) {
330 1.14 wiz if (replyto != NULL)
331 1.27 christos (void)printf("Empty reply-to field -- replying to author\n");
332 1.1 cgd np = extract(rcv, GTO);
333 1.1 cgd }
334 1.1 cgd head.h_to = np;
335 1.14 wiz if ((head.h_subject = hfield("subject", mp)) == NULL)
336 1.1 cgd head.h_subject = hfield("subj", mp);
337 1.1 cgd head.h_subject = reedit(head.h_subject);
338 1.14 wiz if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
339 1.1 cgd np = elide(extract(cp, GCC));
340 1.1 cgd np = delname(np, myname);
341 1.1 cgd if (altnames != 0)
342 1.1 cgd for (ap = altnames; *ap; ap++)
343 1.1 cgd np = delname(np, *ap);
344 1.1 cgd head.h_cc = np;
345 1.1 cgd } else
346 1.15 wiz head.h_cc = NULL;
347 1.15 wiz head.h_bcc = NULL;
348 1.29 christos head.h_smopts = set_smopts(mp);
349 1.31 christos #ifdef MIME_SUPPORT
350 1.31 christos head.h_attach = NULL;
351 1.31 christos #endif
352 1.33 christos set_ident_fields(&head, mp);
353 1.1 cgd mail1(&head, 1);
354 1.33 christos return 0;
355 1.1 cgd }
356 1.1 cgd
357 1.1 cgd /*
358 1.33 christos * Reply to a series of messages by simply mailing to the senders
359 1.33 christos * and not messing around with the To: and Cc: lists as in normal
360 1.33 christos * reply.
361 1.1 cgd */
362 1.33 christos static int
363 1.33 christos _Respond(int msgvec[])
364 1.33 christos {
365 1.33 christos struct header head;
366 1.33 christos struct message *mp;
367 1.33 christos int *ap;
368 1.33 christos char *cp;
369 1.33 christos
370 1.33 christos /* ensure that all header fields are initially NULL */
371 1.33 christos (void)memset(&head, 0, sizeof(head));
372 1.33 christos
373 1.33 christos head.h_to = NULL;
374 1.33 christos for (ap = msgvec; *ap != 0; ap++) {
375 1.33 christos mp = get_message(*ap);
376 1.33 christos touch(mp);
377 1.33 christos dot = mp;
378 1.33 christos if ((cp = skin(hfield("from", mp))) == NULL)
379 1.33 christos cp = skin(nameof(mp, 2));
380 1.33 christos head.h_to = cat(head.h_to, extract(cp, GTO));
381 1.33 christos }
382 1.33 christos if (head.h_to == NULL)
383 1.33 christos return 0;
384 1.33 christos mp = get_message(msgvec[0]);
385 1.33 christos if ((head.h_subject = hfield("subject", mp)) == NULL)
386 1.33 christos head.h_subject = hfield("subj", mp);
387 1.33 christos head.h_subject = reedit(head.h_subject);
388 1.33 christos head.h_cc = NULL;
389 1.33 christos head.h_bcc = NULL;
390 1.33 christos head.h_smopts = set_smopts(mp);
391 1.33 christos #ifdef MIME_SUPPORT
392 1.33 christos head.h_attach = NULL;
393 1.33 christos #endif
394 1.33 christos set_ident_fields(&head, mp);
395 1.33 christos mail1(&head, 1);
396 1.33 christos return 0;
397 1.33 christos }
398 1.33 christos
399 1.33 christos PUBLIC int
400 1.33 christos respond(void *v)
401 1.1 cgd {
402 1.33 christos int *msgvec = v;
403 1.33 christos if (value(ENAME_REPLYALL) == NULL)
404 1.33 christos return _respond(msgvec);
405 1.33 christos else
406 1.33 christos return _Respond(msgvec);
407 1.33 christos }
408 1.1 cgd
409 1.33 christos PUBLIC int
410 1.33 christos Respond(void *v)
411 1.33 christos {
412 1.33 christos int *msgvec = v;
413 1.33 christos if (value(ENAME_REPLYALL) == NULL)
414 1.33 christos return _Respond(msgvec);
415 1.33 christos else
416 1.33 christos return _respond(msgvec);
417 1.1 cgd }
418 1.1 cgd
419 1.1 cgd /*
420 1.1 cgd * Preserve the named messages, so that they will be sent
421 1.1 cgd * back to the system mailbox.
422 1.1 cgd */
423 1.33 christos PUBLIC int
424 1.12 wiz preserve(void *v)
425 1.1 cgd {
426 1.5 christos int *msgvec = v;
427 1.33 christos int *ip;
428 1.1 cgd
429 1.1 cgd if (edit) {
430 1.27 christos (void)printf("Cannot \"preserve\" in edit mode\n");
431 1.33 christos return 1;
432 1.1 cgd }
433 1.33 christos for (ip = msgvec; *ip != 0; ip++)
434 1.33 christos dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
435 1.33 christos
436 1.33 christos return 0;
437 1.1 cgd }
438 1.1 cgd
439 1.1 cgd /*
440 1.33 christos * Mark all given messages as unread, preserving the new status.
441 1.1 cgd */
442 1.33 christos PUBLIC int
443 1.12 wiz unread(void *v)
444 1.1 cgd {
445 1.9 lukem int *msgvec = v;
446 1.9 lukem int *ip;
447 1.1 cgd
448 1.33 christos for (ip = msgvec; *ip != 0; ip++)
449 1.33 christos dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
450 1.33 christos
451 1.33 christos return 0;
452 1.1 cgd }
453 1.1 cgd
454 1.1 cgd /*
455 1.32 christos * Mark all given messages as read.
456 1.32 christos */
457 1.33 christos PUBLIC int
458 1.32 christos markread(void *v)
459 1.32 christos {
460 1.32 christos int *msgvec = v;
461 1.32 christos int *ip;
462 1.32 christos
463 1.33 christos for (ip = msgvec; *ip != 0; ip++)
464 1.33 christos dot = set_m_flag(*ip,
465 1.33 christos ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
466 1.33 christos
467 1.33 christos return 0;
468 1.32 christos }
469 1.32 christos
470 1.32 christos /*
471 1.1 cgd * Print the size of each message.
472 1.1 cgd */
473 1.33 christos PUBLIC int
474 1.12 wiz messize(void *v)
475 1.1 cgd {
476 1.5 christos int *msgvec = v;
477 1.9 lukem struct message *mp;
478 1.9 lukem int *ip, mesg;
479 1.1 cgd
480 1.7 pk for (ip = msgvec; *ip != 0; ip++) {
481 1.1 cgd mesg = *ip;
482 1.33 christos mp = get_message(mesg);
483 1.27 christos (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
484 1.27 christos (unsigned long long)mp->m_size);
485 1.1 cgd }
486 1.33 christos return 0;
487 1.1 cgd }
488 1.1 cgd
489 1.1 cgd /*
490 1.1 cgd * Quit quickly. If we are sourcing, just pop the input level
491 1.1 cgd * by returning an error.
492 1.1 cgd */
493 1.27 christos /*ARGSUSED*/
494 1.33 christos PUBLIC int
495 1.31 christos rexit(void *v __unused)
496 1.1 cgd {
497 1.1 cgd if (sourcing)
498 1.33 christos return 1;
499 1.5 christos exit(0);
500 1.1 cgd /*NOTREACHED*/
501 1.1 cgd }
502 1.1 cgd
503 1.1 cgd /*
504 1.1 cgd * Set or display a variable value. Syntax is similar to that
505 1.1 cgd * of csh.
506 1.1 cgd */
507 1.33 christos PUBLIC int
508 1.12 wiz set(void *v)
509 1.1 cgd {
510 1.33 christos const char **arglist = v;
511 1.9 lukem struct var *vp;
512 1.26 christos const char *cp;
513 1.33 christos char varbuf[LINESIZE];
514 1.33 christos const char **ap, **p;
515 1.1 cgd int errs, h, s;
516 1.23 ross size_t l;
517 1.1 cgd
518 1.14 wiz if (*arglist == NULL) {
519 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
520 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
521 1.1 cgd s++;
522 1.26 christos ap = salloc(s * sizeof *ap);
523 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
524 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
525 1.1 cgd *p++ = vp->v_name;
526 1.14 wiz *p = NULL;
527 1.1 cgd sort(ap);
528 1.14 wiz for (p = ap; *p != NULL; p++)
529 1.27 christos (void)printf("%s\t%s\n", *p, value(*p));
530 1.33 christos return 0;
531 1.1 cgd }
532 1.1 cgd errs = 0;
533 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
534 1.1 cgd cp = *ap;
535 1.1 cgd while (*cp != '=' && *cp != '\0')
536 1.23 ross ++cp;
537 1.23 ross l = cp - *ap;
538 1.23 ross if (l >= sizeof varbuf)
539 1.33 christos l = sizeof(varbuf) - 1;
540 1.27 christos (void)strncpy(varbuf, *ap, l);
541 1.24 ross varbuf[l] = '\0';
542 1.1 cgd if (*cp == '\0')
543 1.1 cgd cp = "";
544 1.1 cgd else
545 1.1 cgd cp++;
546 1.1 cgd if (equal(varbuf, "")) {
547 1.27 christos (void)printf("Non-null variable name required\n");
548 1.1 cgd errs++;
549 1.1 cgd continue;
550 1.1 cgd }
551 1.1 cgd assign(varbuf, cp);
552 1.1 cgd }
553 1.33 christos return errs;
554 1.1 cgd }
555 1.1 cgd
556 1.1 cgd /*
557 1.1 cgd * Unset a bunch of variable values.
558 1.1 cgd */
559 1.33 christos PUBLIC int
560 1.12 wiz unset(void *v)
561 1.1 cgd {
562 1.5 christos char **arglist = v;
563 1.9 lukem struct var *vp, *vp2;
564 1.1 cgd int errs, h;
565 1.1 cgd char **ap;
566 1.1 cgd
567 1.1 cgd errs = 0;
568 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
569 1.15 wiz if ((vp2 = lookup(*ap)) == NULL) {
570 1.11 dean if (getenv(*ap)) {
571 1.27 christos (void)unsetenv(*ap);
572 1.11 dean } else if (!sourcing) {
573 1.27 christos (void)printf("\"%s\": undefined variable\n", *ap);
574 1.1 cgd errs++;
575 1.1 cgd }
576 1.1 cgd continue;
577 1.1 cgd }
578 1.1 cgd h = hash(*ap);
579 1.1 cgd if (vp2 == variables[h]) {
580 1.1 cgd variables[h] = variables[h]->v_link;
581 1.10 wsanchez v_free(vp2->v_name);
582 1.10 wsanchez v_free(vp2->v_value);
583 1.27 christos free(vp2);
584 1.1 cgd continue;
585 1.1 cgd }
586 1.1 cgd for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
587 1.33 christos continue;
588 1.1 cgd vp->v_link = vp2->v_link;
589 1.10 wsanchez v_free(vp2->v_name);
590 1.10 wsanchez v_free(vp2->v_value);
591 1.27 christos free(vp2);
592 1.1 cgd }
593 1.33 christos return errs;
594 1.1 cgd }
595 1.1 cgd
596 1.1 cgd /*
597 1.29 christos * Show a variable value.
598 1.29 christos */
599 1.33 christos PUBLIC int
600 1.29 christos show(void *v)
601 1.29 christos {
602 1.33 christos const char **arglist = v;
603 1.29 christos struct var *vp;
604 1.33 christos const char **ap, **p;
605 1.29 christos int h, s;
606 1.29 christos
607 1.29 christos if (*arglist == NULL) {
608 1.29 christos for (h = 0, s = 1; h < HSHSIZE; h++)
609 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
610 1.29 christos s++;
611 1.29 christos ap = salloc(s * sizeof *ap);
612 1.29 christos for (h = 0, p = ap; h < HSHSIZE; h++)
613 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
614 1.29 christos *p++ = vp->v_name;
615 1.29 christos *p = NULL;
616 1.29 christos sort(ap);
617 1.29 christos for (p = ap; *p != NULL; p++)
618 1.29 christos (void)printf("%s=%s\n", *p, value(*p));
619 1.33 christos return 0;
620 1.29 christos }
621 1.29 christos
622 1.29 christos for (ap = arglist; *ap != NULL; ap++) {
623 1.29 christos char *val = value(*ap);
624 1.29 christos (void)printf("%s=%s\n", *ap, val ? val : "<null>");
625 1.29 christos }
626 1.29 christos return 0;
627 1.29 christos }
628 1.29 christos
629 1.32 christos
630 1.29 christos /*
631 1.1 cgd * Put add users to a group.
632 1.1 cgd */
633 1.33 christos PUBLIC int
634 1.12 wiz group(void *v)
635 1.1 cgd {
636 1.33 christos const char **argv = v;
637 1.9 lukem struct grouphead *gh;
638 1.9 lukem struct group *gp;
639 1.9 lukem int h;
640 1.1 cgd int s;
641 1.33 christos const char *gname;
642 1.33 christos const char **ap, **p;
643 1.1 cgd
644 1.14 wiz if (*argv == NULL) {
645 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
646 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
647 1.1 cgd s++;
648 1.26 christos ap = salloc(s * sizeof *ap);
649 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
650 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
651 1.1 cgd *p++ = gh->g_name;
652 1.14 wiz *p = NULL;
653 1.1 cgd sort(ap);
654 1.14 wiz for (p = ap; *p != NULL; p++)
655 1.1 cgd printgroup(*p);
656 1.33 christos return 0;
657 1.1 cgd }
658 1.14 wiz if (argv[1] == NULL) {
659 1.1 cgd printgroup(*argv);
660 1.33 christos return 0;
661 1.1 cgd }
662 1.1 cgd gname = *argv;
663 1.1 cgd h = hash(gname);
664 1.15 wiz if ((gh = findgroup(gname)) == NULL) {
665 1.31 christos gh = (struct grouphead *) ecalloc(1, sizeof *gh);
666 1.1 cgd gh->g_name = vcopy(gname);
667 1.15 wiz gh->g_list = NULL;
668 1.1 cgd gh->g_link = groups[h];
669 1.1 cgd groups[h] = gh;
670 1.1 cgd }
671 1.1 cgd
672 1.1 cgd /*
673 1.1 cgd * Insert names from the command list into the group.
674 1.1 cgd * Who cares if there are duplicates? They get tossed
675 1.1 cgd * later anyway.
676 1.1 cgd */
677 1.1 cgd
678 1.31 christos for (ap = argv + 1; *ap != NULL; ap++) {
679 1.31 christos gp = (struct group *) ecalloc(1, sizeof *gp);
680 1.1 cgd gp->ge_name = vcopy(*ap);
681 1.1 cgd gp->ge_link = gh->g_list;
682 1.1 cgd gh->g_list = gp;
683 1.1 cgd }
684 1.28 christos return 0;
685 1.28 christos }
686 1.28 christos
687 1.28 christos /*
688 1.28 christos * Delete the named group alias. Return zero if the group was
689 1.28 christos * successfully deleted, or -1 if there was no such group.
690 1.28 christos */
691 1.28 christos static int
692 1.28 christos delgroup(const char *name)
693 1.28 christos {
694 1.28 christos struct grouphead *gh, *p;
695 1.28 christos struct group *g;
696 1.28 christos int h;
697 1.28 christos
698 1.28 christos h = hash(name);
699 1.28 christos for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
700 1.28 christos if (strcmp(gh->g_name, name) == 0) {
701 1.28 christos if (p == NULL)
702 1.28 christos groups[h] = gh->g_link;
703 1.28 christos else
704 1.28 christos p->g_link = gh->g_link;
705 1.28 christos while (gh->g_list != NULL) {
706 1.28 christos g = gh->g_list;
707 1.28 christos gh->g_list = g->ge_link;
708 1.28 christos free(g->ge_name);
709 1.28 christos free(g);
710 1.28 christos }
711 1.28 christos free(gh->g_name);
712 1.28 christos free(gh);
713 1.28 christos return 0;
714 1.28 christos }
715 1.28 christos return -1;
716 1.28 christos }
717 1.28 christos
718 1.28 christos /*
719 1.33 christos * The unalias command takes a list of alises
720 1.33 christos * and discards the remembered groups of users.
721 1.1 cgd */
722 1.33 christos PUBLIC int
723 1.33 christos unalias(void *v)
724 1.1 cgd {
725 1.9 lukem char **ap;
726 1.1 cgd
727 1.33 christos for (ap = v; *ap != NULL; ap++)
728 1.33 christos (void)delgroup(*ap);
729 1.33 christos return 0;
730 1.1 cgd }
731 1.1 cgd
732 1.1 cgd /*
733 1.1 cgd * The do nothing command for comments.
734 1.1 cgd */
735 1.1 cgd /*ARGSUSED*/
736 1.33 christos PUBLIC int
737 1.31 christos null(void *v __unused)
738 1.1 cgd {
739 1.1 cgd return 0;
740 1.1 cgd }
741 1.1 cgd
742 1.1 cgd /*
743 1.1 cgd * Change to another file. With no argument, print information about
744 1.1 cgd * the current file.
745 1.1 cgd */
746 1.33 christos PUBLIC int
747 1.12 wiz file(void *v)
748 1.1 cgd {
749 1.5 christos char **argv = v;
750 1.1 cgd
751 1.14 wiz if (argv[0] == NULL) {
752 1.27 christos (void)newfileinfo(0);
753 1.1 cgd return 0;
754 1.1 cgd }
755 1.1 cgd if (setfile(*argv) < 0)
756 1.1 cgd return 1;
757 1.1 cgd announce();
758 1.33 christos
759 1.1 cgd return 0;
760 1.1 cgd }
761 1.1 cgd
762 1.1 cgd /*
763 1.1 cgd * Expand file names like echo
764 1.1 cgd */
765 1.33 christos PUBLIC int
766 1.12 wiz echo(void *v)
767 1.1 cgd {
768 1.5 christos char **argv = v;
769 1.9 lukem char **ap;
770 1.26 christos const char *cp;
771 1.1 cgd
772 1.14 wiz for (ap = argv; *ap != NULL; ap++) {
773 1.1 cgd cp = *ap;
774 1.14 wiz if ((cp = expand(cp)) != NULL) {
775 1.1 cgd if (ap != argv)
776 1.27 christos (void)putchar(' ');
777 1.27 christos (void)printf("%s", cp);
778 1.1 cgd }
779 1.1 cgd }
780 1.27 christos (void)putchar('\n');
781 1.1 cgd return 0;
782 1.1 cgd }
783 1.1 cgd
784 1.33 christos /*
785 1.33 christos * Routines to push and pop the condition code to support nested
786 1.33 christos * if/else/endif statements.
787 1.33 christos */
788 1.33 christos static void
789 1.33 christos push_cond(int c_cond)
790 1.1 cgd {
791 1.33 christos struct cond_stack_s *csp;
792 1.33 christos csp = emalloc(sizeof(*csp));
793 1.33 christos csp->c_cond = c_cond;
794 1.33 christos csp->c_next = cond_stack;
795 1.33 christos cond_stack = csp;
796 1.1 cgd }
797 1.1 cgd
798 1.33 christos static int
799 1.33 christos pop_cond(void)
800 1.1 cgd {
801 1.33 christos int c_cond;
802 1.33 christos struct cond_stack_s *csp;
803 1.33 christos
804 1.33 christos if ((csp = cond_stack) == NULL)
805 1.33 christos return -1;
806 1.1 cgd
807 1.33 christos c_cond = csp->c_cond;
808 1.33 christos cond_stack = csp->c_next;
809 1.33 christos free(csp);
810 1.33 christos return c_cond;
811 1.1 cgd }
812 1.1 cgd
813 1.1 cgd /*
814 1.1 cgd * Conditional commands. These allow one to parameterize one's
815 1.1 cgd * .mailrc and do some things if sending, others if receiving.
816 1.1 cgd */
817 1.33 christos static int
818 1.33 christos if_push(void)
819 1.33 christos {
820 1.33 christos push_cond(cond);
821 1.33 christos cond &= ~CELSE;
822 1.33 christos if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
823 1.33 christos cond |= CIGN;
824 1.33 christos return 1;
825 1.33 christos }
826 1.33 christos return 0;
827 1.33 christos }
828 1.33 christos
829 1.33 christos PUBLIC int
830 1.12 wiz ifcmd(void *v)
831 1.1 cgd {
832 1.5 christos char **argv = v;
833 1.33 christos char *keyword = argv[0];
834 1.33 christos static const struct modetbl_s {
835 1.33 christos const char *m_name;
836 1.33 christos enum mailmode_e m_mode;
837 1.33 christos } modetbl[] = {
838 1.33 christos { "receiving", mm_receiving },
839 1.33 christos { "sending", mm_sending },
840 1.33 christos { "headersonly", mm_hdrsonly },
841 1.33 christos { NULL, 0 },
842 1.33 christos };
843 1.33 christos const struct modetbl_s *mtp;
844 1.33 christos
845 1.33 christos if (if_push())
846 1.33 christos return 0;
847 1.1 cgd
848 1.33 christos cond = CIF;
849 1.33 christos for (mtp = modetbl; mtp->m_name; mtp++)
850 1.33 christos if (strcasecmp(keyword, mtp->m_name) == 0)
851 1.33 christos break;
852 1.33 christos
853 1.33 christos if (mtp->m_name == NULL) {
854 1.33 christos cond = CNONE;
855 1.33 christos (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
856 1.33 christos return 1;
857 1.1 cgd }
858 1.33 christos if (mtp->m_mode != mailmode)
859 1.33 christos cond |= CSKIP;
860 1.33 christos
861 1.33 christos return 0;
862 1.33 christos }
863 1.33 christos
864 1.33 christos PUBLIC int
865 1.33 christos ifdefcmd(void *v)
866 1.33 christos {
867 1.33 christos char **argv = v;
868 1.33 christos
869 1.33 christos if (if_push())
870 1.33 christos return 0;
871 1.33 christos
872 1.33 christos cond = CIF;
873 1.33 christos if (value(argv[0]) == NULL)
874 1.33 christos cond |= CSKIP;
875 1.33 christos
876 1.33 christos return 0;
877 1.33 christos }
878 1.33 christos
879 1.33 christos PUBLIC int
880 1.33 christos ifndefcmd(void *v)
881 1.33 christos {
882 1.33 christos int rval;
883 1.33 christos rval = ifdefcmd(v);
884 1.33 christos cond ^= CSKIP;
885 1.33 christos return rval;
886 1.1 cgd }
887 1.1 cgd
888 1.1 cgd /*
889 1.1 cgd * Implement 'else'. This is pretty simple -- we just
890 1.1 cgd * flip over the conditional flag.
891 1.1 cgd */
892 1.27 christos /*ARGSUSED*/
893 1.33 christos PUBLIC int
894 1.31 christos elsecmd(void *v __unused)
895 1.1 cgd {
896 1.33 christos if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
897 1.33 christos (void)printf("\"else\" without matching \"if\"\n");
898 1.33 christos cond = CNONE;
899 1.33 christos return 1;
900 1.33 christos }
901 1.33 christos if ((cond & CIGN) == 0) {
902 1.33 christos cond ^= CSKIP;
903 1.33 christos cond |= CELSE;
904 1.1 cgd }
905 1.33 christos return 0;
906 1.1 cgd }
907 1.1 cgd
908 1.1 cgd /*
909 1.1 cgd * End of if statement. Just set cond back to anything.
910 1.1 cgd */
911 1.27 christos /*ARGSUSED*/
912 1.33 christos PUBLIC int
913 1.31 christos endifcmd(void *v __unused)
914 1.1 cgd {
915 1.33 christos if (cond_stack == NULL || (cond & CIF) != CIF) {
916 1.33 christos (void)printf("\"endif\" without matching \"if\"\n");
917 1.33 christos cond = CNONE;
918 1.33 christos return 1;
919 1.1 cgd }
920 1.33 christos cond = pop_cond();
921 1.33 christos return 0;
922 1.1 cgd }
923 1.1 cgd
924 1.1 cgd /*
925 1.1 cgd * Set the list of alternate names.
926 1.1 cgd */
927 1.33 christos PUBLIC int
928 1.12 wiz alternates(void *v)
929 1.1 cgd {
930 1.5 christos char **namelist = v;
931 1.9 lukem int c;
932 1.9 lukem char **ap, **ap2, *cp;
933 1.1 cgd
934 1.1 cgd c = argcount(namelist) + 1;
935 1.1 cgd if (c == 1) {
936 1.1 cgd if (altnames == 0)
937 1.33 christos return 0;
938 1.1 cgd for (ap = altnames; *ap; ap++)
939 1.27 christos (void)printf("%s ", *ap);
940 1.27 christos (void)printf("\n");
941 1.33 christos return 0;
942 1.1 cgd }
943 1.1 cgd if (altnames != 0)
944 1.27 christos free(altnames);
945 1.31 christos altnames = (char **) ecalloc((unsigned) c, sizeof (char *));
946 1.1 cgd for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
947 1.31 christos cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char));
948 1.27 christos (void)strcpy(cp, *ap);
949 1.1 cgd *ap2 = cp;
950 1.1 cgd }
951 1.1 cgd *ap2 = 0;
952 1.33 christos return 0;
953 1.1 cgd }
954