cmd3.c revision 1.37 1 1.37 christos /* $NetBSD: cmd3.c,v 1.37 2007/10/29 23:20:37 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.37 christos __RCSID("$NetBSD: cmd3.c,v 1.37 2007/10/29 23:20:37 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.34 christos cathelp(_PATH_HELP);
181 1.33 christos return 0;
182 1.1 cgd }
183 1.1 cgd
184 1.1 cgd /*
185 1.1 cgd * Change user's working directory.
186 1.1 cgd */
187 1.33 christos PUBLIC int
188 1.12 wiz schdir(void *v)
189 1.1 cgd {
190 1.5 christos char **arglist = v;
191 1.26 christos const char *cp;
192 1.1 cgd
193 1.14 wiz if (*arglist == NULL)
194 1.1 cgd cp = homedir;
195 1.1 cgd else
196 1.14 wiz if ((cp = expand(*arglist)) == NULL)
197 1.33 christos return 1;
198 1.1 cgd if (chdir(cp) < 0) {
199 1.17 wiz warn("%s", cp);
200 1.33 christos return 1;
201 1.1 cgd }
202 1.1 cgd return 0;
203 1.1 cgd }
204 1.1 cgd
205 1.33 christos /*
206 1.33 christos * Return the smopts field if "ReplyAsRecipient" is defined.
207 1.33 christos */
208 1.29 christos static struct name *
209 1.29 christos set_smopts(struct message *mp)
210 1.29 christos {
211 1.29 christos char *cp;
212 1.29 christos struct name *np = NULL;
213 1.33 christos char *reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
214 1.29 christos
215 1.30 christos if (reply_as_recipient &&
216 1.29 christos (cp = skin(hfield("to", mp))) != NULL &&
217 1.30 christos extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
218 1.29 christos char *p, *q;
219 1.31 christos size_t len = strlen(cp);
220 1.31 christos /*
221 1.31 christos * XXX - perhaps we always want to ignore
222 1.31 christos * "undisclosed-recipients:;" ?
223 1.31 christos */
224 1.31 christos for (p = q = reply_as_recipient; *p; p = q) {
225 1.35 christos while (*q != '\0' && *q != ',' && !is_WSP(*q))
226 1.29 christos q++;
227 1.31 christos if (p + len == q && strncasecmp(cp, p, len) == 0)
228 1.29 christos return np;
229 1.35 christos while (*q == ',' || is_WSP(*q))
230 1.29 christos q++;
231 1.29 christos }
232 1.29 christos np = extract(__UNCONST("-f"), GSMOPTS);
233 1.29 christos np = cat(np, extract(cp, GSMOPTS));
234 1.29 christos }
235 1.29 christos
236 1.29 christos return np;
237 1.29 christos }
238 1.29 christos
239 1.1 cgd /*
240 1.33 christos * Modify the subject we are replying to to begin with Re: if
241 1.33 christos * it does not already.
242 1.33 christos */
243 1.33 christos static char *
244 1.33 christos reedit(char *subj)
245 1.33 christos {
246 1.33 christos char *newsubj;
247 1.33 christos
248 1.33 christos if (subj == NULL)
249 1.33 christos return NULL;
250 1.33 christos if ((subj[0] == 'r' || subj[0] == 'R') &&
251 1.33 christos (subj[1] == 'e' || subj[1] == 'E') &&
252 1.33 christos subj[2] == ':')
253 1.33 christos return subj;
254 1.33 christos newsubj = salloc(strlen(subj) + 5);
255 1.33 christos (void)strcpy(newsubj, "Re: ");
256 1.33 christos (void)strcpy(newsubj + 4, subj);
257 1.33 christos return newsubj;
258 1.33 christos }
259 1.33 christos
260 1.33 christos /*
261 1.33 christos * Set the "In-Reply-To" and "References" header fields appropriately.
262 1.33 christos * Used in replies.
263 1.33 christos */
264 1.33 christos static void
265 1.33 christos set_ident_fields(struct header *hp, struct message *mp)
266 1.33 christos {
267 1.33 christos char *in_reply_to;
268 1.33 christos char *references;
269 1.33 christos
270 1.33 christos in_reply_to = hfield("message-id", mp);
271 1.33 christos hp->h_in_reply_to = in_reply_to;
272 1.33 christos
273 1.33 christos references = hfield("references", mp);
274 1.33 christos hp->h_references = extract(references, GMISC);
275 1.33 christos hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
276 1.33 christos }
277 1.33 christos
278 1.33 christos /*
279 1.1 cgd * Reply to a list of messages. Extract each name from the
280 1.1 cgd * message header and send them off to mail1()
281 1.1 cgd */
282 1.33 christos static int
283 1.36 christos respond_core(int *msgvec)
284 1.1 cgd {
285 1.1 cgd struct message *mp;
286 1.1 cgd char *cp, *rcv, *replyto;
287 1.1 cgd char **ap;
288 1.1 cgd struct name *np;
289 1.1 cgd struct header head;
290 1.1 cgd
291 1.33 christos /* ensure that all header fields are initially NULL */
292 1.33 christos (void)memset(&head, 0, sizeof(head));
293 1.33 christos
294 1.1 cgd if (msgvec[1] != 0) {
295 1.27 christos (void)printf("Sorry, can't reply to multiple messages at once\n");
296 1.33 christos return 1;
297 1.1 cgd }
298 1.33 christos mp = get_message(msgvec[0]);
299 1.1 cgd touch(mp);
300 1.1 cgd dot = mp;
301 1.14 wiz if ((rcv = skin(hfield("from", mp))) == NULL)
302 1.1 cgd rcv = skin(nameof(mp, 1));
303 1.14 wiz if ((replyto = skin(hfield("reply-to", mp))) != NULL)
304 1.1 cgd np = extract(replyto, GTO);
305 1.14 wiz else if ((cp = skin(hfield("to", mp))) != NULL)
306 1.1 cgd np = extract(cp, GTO);
307 1.1 cgd else
308 1.15 wiz np = NULL;
309 1.1 cgd np = elide(np);
310 1.1 cgd /*
311 1.1 cgd * Delete my name from the reply list,
312 1.1 cgd * and with it, all my alternate names.
313 1.1 cgd */
314 1.1 cgd np = delname(np, myname);
315 1.1 cgd if (altnames)
316 1.1 cgd for (ap = altnames; *ap; ap++)
317 1.1 cgd np = delname(np, *ap);
318 1.15 wiz if (np != NULL && replyto == NULL)
319 1.1 cgd np = cat(np, extract(rcv, GTO));
320 1.15 wiz else if (np == NULL) {
321 1.14 wiz if (replyto != NULL)
322 1.27 christos (void)printf("Empty reply-to field -- replying to author\n");
323 1.1 cgd np = extract(rcv, GTO);
324 1.1 cgd }
325 1.1 cgd head.h_to = np;
326 1.14 wiz if ((head.h_subject = hfield("subject", mp)) == NULL)
327 1.1 cgd head.h_subject = hfield("subj", mp);
328 1.1 cgd head.h_subject = reedit(head.h_subject);
329 1.14 wiz if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
330 1.1 cgd np = elide(extract(cp, GCC));
331 1.1 cgd np = delname(np, myname);
332 1.1 cgd if (altnames != 0)
333 1.1 cgd for (ap = altnames; *ap; ap++)
334 1.1 cgd np = delname(np, *ap);
335 1.1 cgd head.h_cc = np;
336 1.1 cgd } else
337 1.15 wiz head.h_cc = NULL;
338 1.15 wiz head.h_bcc = NULL;
339 1.29 christos head.h_smopts = set_smopts(mp);
340 1.31 christos #ifdef MIME_SUPPORT
341 1.31 christos head.h_attach = NULL;
342 1.31 christos #endif
343 1.33 christos set_ident_fields(&head, mp);
344 1.1 cgd mail1(&head, 1);
345 1.33 christos return 0;
346 1.1 cgd }
347 1.1 cgd
348 1.1 cgd /*
349 1.33 christos * Reply to a series of messages by simply mailing to the senders
350 1.33 christos * and not messing around with the To: and Cc: lists as in normal
351 1.33 christos * reply.
352 1.1 cgd */
353 1.33 christos static int
354 1.36 christos Respond_core(int msgvec[])
355 1.33 christos {
356 1.33 christos struct header head;
357 1.33 christos struct message *mp;
358 1.33 christos int *ap;
359 1.33 christos char *cp;
360 1.33 christos
361 1.33 christos /* ensure that all header fields are initially NULL */
362 1.33 christos (void)memset(&head, 0, sizeof(head));
363 1.33 christos
364 1.33 christos head.h_to = NULL;
365 1.33 christos for (ap = msgvec; *ap != 0; ap++) {
366 1.33 christos mp = get_message(*ap);
367 1.33 christos touch(mp);
368 1.33 christos dot = mp;
369 1.33 christos if ((cp = skin(hfield("from", mp))) == NULL)
370 1.33 christos cp = skin(nameof(mp, 2));
371 1.33 christos head.h_to = cat(head.h_to, extract(cp, GTO));
372 1.33 christos }
373 1.33 christos if (head.h_to == NULL)
374 1.33 christos return 0;
375 1.33 christos mp = get_message(msgvec[0]);
376 1.33 christos if ((head.h_subject = hfield("subject", mp)) == NULL)
377 1.33 christos head.h_subject = hfield("subj", mp);
378 1.33 christos head.h_subject = reedit(head.h_subject);
379 1.33 christos head.h_cc = NULL;
380 1.33 christos head.h_bcc = NULL;
381 1.33 christos head.h_smopts = set_smopts(mp);
382 1.33 christos #ifdef MIME_SUPPORT
383 1.33 christos head.h_attach = NULL;
384 1.33 christos #endif
385 1.33 christos set_ident_fields(&head, mp);
386 1.33 christos mail1(&head, 1);
387 1.33 christos return 0;
388 1.33 christos }
389 1.33 christos
390 1.33 christos PUBLIC int
391 1.33 christos respond(void *v)
392 1.1 cgd {
393 1.33 christos int *msgvec = v;
394 1.33 christos if (value(ENAME_REPLYALL) == NULL)
395 1.36 christos return respond_core(msgvec);
396 1.33 christos else
397 1.36 christos return Respond_core(msgvec);
398 1.33 christos }
399 1.1 cgd
400 1.33 christos PUBLIC int
401 1.33 christos Respond(void *v)
402 1.33 christos {
403 1.33 christos int *msgvec = v;
404 1.33 christos if (value(ENAME_REPLYALL) == NULL)
405 1.36 christos return Respond_core(msgvec);
406 1.33 christos else
407 1.36 christos return respond_core(msgvec);
408 1.36 christos }
409 1.36 christos
410 1.36 christos PUBLIC int
411 1.36 christos forward(void *v)
412 1.36 christos {
413 1.36 christos int *msgvec = v;
414 1.36 christos int *ip;
415 1.36 christos for ( ip = msgvec; *ip; ip++)
416 1.36 christos (void)printf("%d ", *ip);
417 1.36 christos (void)printf("forward not supported yet\n");
418 1.36 christos return 0;
419 1.36 christos }
420 1.36 christos
421 1.36 christos static int
422 1.36 christos bounce_one(int msgno, const char **smargs, struct name *h_to)
423 1.36 christos {
424 1.36 christos char mailtempname[PATHSIZE];
425 1.36 christos struct message *mp;
426 1.36 christos int fd;
427 1.36 christos FILE *obuf;
428 1.36 christos int rval;
429 1.36 christos
430 1.36 christos rval = 0;
431 1.36 christos
432 1.36 christos obuf = NULL;
433 1.36 christos (void)snprintf(mailtempname, sizeof(mailtempname),
434 1.36 christos "%s/mail.RsXXXXXXXXXX", tmpdir);
435 1.36 christos if ((fd = mkstemp(mailtempname)) == -1 ||
436 1.36 christos (obuf = Fdopen(fd, "w+")) == NULL) {
437 1.36 christos if (fd != -1)
438 1.36 christos (void)close(fd);
439 1.36 christos warn("%s", mailtempname);
440 1.36 christos rval = 1;
441 1.36 christos goto done;
442 1.36 christos }
443 1.36 christos (void)rm(mailtempname);
444 1.36 christos
445 1.36 christos mp = get_message(msgno);
446 1.36 christos
447 1.36 christos if (mp == NULL) {
448 1.36 christos (void)printf("no such message %d\n", msgno);
449 1.36 christos rval = 1;
450 1.36 christos goto done;
451 1.36 christos }
452 1.36 christos else {
453 1.36 christos char *cp;
454 1.36 christos char **ap;
455 1.36 christos struct name *np;
456 1.36 christos struct header hdr;
457 1.36 christos
458 1.36 christos /*
459 1.36 christos * Construct and output a new "To:" field:
460 1.36 christos * Remove our address from anything in the old "To:" field
461 1.36 christos * and append that list to the bounce address(es).
462 1.36 christos */
463 1.36 christos np = NULL;
464 1.36 christos if ((cp = skin(hfield("to", mp))) != NULL)
465 1.36 christos np = extract(cp, GTO);
466 1.36 christos np = delname(np, myname);
467 1.36 christos if (altnames)
468 1.36 christos for (ap = altnames; *ap; ap++)
469 1.36 christos np = delname(np, *ap);
470 1.36 christos np = cat(h_to, np);
471 1.36 christos (void)memset(&hdr, 0, sizeof(hdr));
472 1.36 christos hdr.h_to = elide(np);
473 1.36 christos (void)puthead(&hdr, obuf, GTO | GCOMMA);
474 1.36 christos }
475 1.36 christos if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
476 1.36 christos (void)printf("bounce failed for message %d\n", msgno);
477 1.36 christos rval = 1;
478 1.36 christos goto done;
479 1.36 christos }
480 1.36 christos rewind(obuf); /* XXX - here or inside mail2() */
481 1.36 christos mail2(obuf, smargs);
482 1.36 christos done:
483 1.36 christos if (obuf)
484 1.36 christos (void)Fclose(obuf);
485 1.36 christos return rval;
486 1.36 christos }
487 1.36 christos
488 1.36 christos PUBLIC int
489 1.36 christos bounce(void *v)
490 1.36 christos {
491 1.36 christos int *msgvec = v;
492 1.36 christos int *ip;
493 1.36 christos const char **smargs;
494 1.36 christos struct header hdr;
495 1.36 christos int rval;
496 1.36 christos
497 1.36 christos if (bouncetab[0].i_count == 0) {
498 1.36 christos /* setup the bounce tab */
499 1.36 christos add_ignore("Status", bouncetab);
500 1.36 christos add_ignore("Delivered-To", bouncetab);
501 1.36 christos add_ignore("To", bouncetab);
502 1.36 christos add_ignore("X-Original-To", bouncetab);
503 1.36 christos }
504 1.36 christos (void)memset(&hdr, 0, sizeof(hdr));
505 1.36 christos if ((rval = grabh(&hdr, GTO)) != 0)
506 1.36 christos return rval;
507 1.36 christos
508 1.36 christos if (hdr.h_to == NULL)
509 1.36 christos return 1;
510 1.36 christos
511 1.36 christos smargs = unpack(hdr.h_to);
512 1.36 christos for ( ip = msgvec; *ip; ip++) {
513 1.36 christos int e;
514 1.36 christos if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
515 1.36 christos return e;
516 1.36 christos }
517 1.36 christos return 0;
518 1.1 cgd }
519 1.1 cgd
520 1.1 cgd /*
521 1.1 cgd * Preserve the named messages, so that they will be sent
522 1.1 cgd * back to the system mailbox.
523 1.1 cgd */
524 1.33 christos PUBLIC int
525 1.12 wiz preserve(void *v)
526 1.1 cgd {
527 1.5 christos int *msgvec = v;
528 1.33 christos int *ip;
529 1.1 cgd
530 1.1 cgd if (edit) {
531 1.27 christos (void)printf("Cannot \"preserve\" in edit mode\n");
532 1.33 christos return 1;
533 1.1 cgd }
534 1.33 christos for (ip = msgvec; *ip != 0; ip++)
535 1.33 christos dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
536 1.33 christos
537 1.33 christos return 0;
538 1.1 cgd }
539 1.1 cgd
540 1.1 cgd /*
541 1.33 christos * Mark all given messages as unread, preserving the new status.
542 1.1 cgd */
543 1.33 christos PUBLIC int
544 1.12 wiz unread(void *v)
545 1.1 cgd {
546 1.9 lukem int *msgvec = v;
547 1.9 lukem int *ip;
548 1.1 cgd
549 1.33 christos for (ip = msgvec; *ip != 0; ip++)
550 1.33 christos dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
551 1.33 christos
552 1.33 christos return 0;
553 1.1 cgd }
554 1.1 cgd
555 1.1 cgd /*
556 1.32 christos * Mark all given messages as read.
557 1.32 christos */
558 1.33 christos PUBLIC int
559 1.32 christos markread(void *v)
560 1.32 christos {
561 1.32 christos int *msgvec = v;
562 1.32 christos int *ip;
563 1.32 christos
564 1.33 christos for (ip = msgvec; *ip != 0; ip++)
565 1.33 christos dot = set_m_flag(*ip,
566 1.33 christos ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
567 1.33 christos
568 1.33 christos return 0;
569 1.32 christos }
570 1.32 christos
571 1.32 christos /*
572 1.1 cgd * Print the size of each message.
573 1.1 cgd */
574 1.33 christos PUBLIC int
575 1.12 wiz messize(void *v)
576 1.1 cgd {
577 1.5 christos int *msgvec = v;
578 1.9 lukem struct message *mp;
579 1.9 lukem int *ip, mesg;
580 1.1 cgd
581 1.7 pk for (ip = msgvec; *ip != 0; ip++) {
582 1.1 cgd mesg = *ip;
583 1.33 christos mp = get_message(mesg);
584 1.27 christos (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
585 1.27 christos (unsigned long long)mp->m_size);
586 1.1 cgd }
587 1.33 christos return 0;
588 1.1 cgd }
589 1.1 cgd
590 1.1 cgd /*
591 1.1 cgd * Quit quickly. If we are sourcing, just pop the input level
592 1.1 cgd * by returning an error.
593 1.1 cgd */
594 1.27 christos /*ARGSUSED*/
595 1.33 christos PUBLIC int
596 1.31 christos rexit(void *v __unused)
597 1.1 cgd {
598 1.1 cgd if (sourcing)
599 1.33 christos return 1;
600 1.5 christos exit(0);
601 1.1 cgd /*NOTREACHED*/
602 1.1 cgd }
603 1.1 cgd
604 1.1 cgd /*
605 1.1 cgd * Set or display a variable value. Syntax is similar to that
606 1.1 cgd * of csh.
607 1.1 cgd */
608 1.33 christos PUBLIC int
609 1.12 wiz set(void *v)
610 1.1 cgd {
611 1.33 christos const char **arglist = v;
612 1.9 lukem struct var *vp;
613 1.26 christos const char *cp;
614 1.33 christos char varbuf[LINESIZE];
615 1.33 christos const char **ap, **p;
616 1.1 cgd int errs, h, s;
617 1.23 ross size_t l;
618 1.1 cgd
619 1.14 wiz if (*arglist == NULL) {
620 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
621 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
622 1.1 cgd s++;
623 1.37 christos ap = salloc(s * sizeof(*ap));
624 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
625 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
626 1.1 cgd *p++ = vp->v_name;
627 1.14 wiz *p = NULL;
628 1.1 cgd sort(ap);
629 1.14 wiz for (p = ap; *p != NULL; p++)
630 1.27 christos (void)printf("%s\t%s\n", *p, value(*p));
631 1.33 christos return 0;
632 1.1 cgd }
633 1.1 cgd errs = 0;
634 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
635 1.1 cgd cp = *ap;
636 1.1 cgd while (*cp != '=' && *cp != '\0')
637 1.23 ross ++cp;
638 1.23 ross l = cp - *ap;
639 1.37 christos if (l >= sizeof(varbuf))
640 1.33 christos l = sizeof(varbuf) - 1;
641 1.27 christos (void)strncpy(varbuf, *ap, l);
642 1.24 ross varbuf[l] = '\0';
643 1.1 cgd if (*cp == '\0')
644 1.1 cgd cp = "";
645 1.1 cgd else
646 1.1 cgd cp++;
647 1.1 cgd if (equal(varbuf, "")) {
648 1.27 christos (void)printf("Non-null variable name required\n");
649 1.1 cgd errs++;
650 1.1 cgd continue;
651 1.1 cgd }
652 1.1 cgd assign(varbuf, cp);
653 1.1 cgd }
654 1.33 christos return errs;
655 1.1 cgd }
656 1.1 cgd
657 1.1 cgd /*
658 1.1 cgd * Unset a bunch of variable values.
659 1.1 cgd */
660 1.33 christos PUBLIC int
661 1.12 wiz unset(void *v)
662 1.1 cgd {
663 1.5 christos char **arglist = v;
664 1.9 lukem struct var *vp, *vp2;
665 1.1 cgd int errs, h;
666 1.1 cgd char **ap;
667 1.1 cgd
668 1.1 cgd errs = 0;
669 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
670 1.15 wiz if ((vp2 = lookup(*ap)) == NULL) {
671 1.11 dean if (getenv(*ap)) {
672 1.27 christos (void)unsetenv(*ap);
673 1.11 dean } else if (!sourcing) {
674 1.27 christos (void)printf("\"%s\": undefined variable\n", *ap);
675 1.1 cgd errs++;
676 1.1 cgd }
677 1.1 cgd continue;
678 1.1 cgd }
679 1.1 cgd h = hash(*ap);
680 1.1 cgd if (vp2 == variables[h]) {
681 1.1 cgd variables[h] = variables[h]->v_link;
682 1.10 wsanchez v_free(vp2->v_name);
683 1.10 wsanchez v_free(vp2->v_value);
684 1.27 christos free(vp2);
685 1.1 cgd continue;
686 1.1 cgd }
687 1.1 cgd for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
688 1.33 christos continue;
689 1.1 cgd vp->v_link = vp2->v_link;
690 1.10 wsanchez v_free(vp2->v_name);
691 1.10 wsanchez v_free(vp2->v_value);
692 1.27 christos free(vp2);
693 1.1 cgd }
694 1.33 christos return errs;
695 1.1 cgd }
696 1.1 cgd
697 1.1 cgd /*
698 1.29 christos * Show a variable value.
699 1.29 christos */
700 1.33 christos PUBLIC int
701 1.29 christos show(void *v)
702 1.29 christos {
703 1.33 christos const char **arglist = v;
704 1.29 christos struct var *vp;
705 1.33 christos const char **ap, **p;
706 1.29 christos int h, s;
707 1.29 christos
708 1.29 christos if (*arglist == NULL) {
709 1.29 christos for (h = 0, s = 1; h < HSHSIZE; h++)
710 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
711 1.29 christos s++;
712 1.37 christos ap = salloc(s * sizeof(*ap));
713 1.29 christos for (h = 0, p = ap; h < HSHSIZE; h++)
714 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
715 1.29 christos *p++ = vp->v_name;
716 1.29 christos *p = NULL;
717 1.29 christos sort(ap);
718 1.29 christos for (p = ap; *p != NULL; p++)
719 1.29 christos (void)printf("%s=%s\n", *p, value(*p));
720 1.33 christos return 0;
721 1.29 christos }
722 1.29 christos
723 1.29 christos for (ap = arglist; *ap != NULL; ap++) {
724 1.29 christos char *val = value(*ap);
725 1.29 christos (void)printf("%s=%s\n", *ap, val ? val : "<null>");
726 1.29 christos }
727 1.29 christos return 0;
728 1.29 christos }
729 1.29 christos
730 1.32 christos
731 1.29 christos /*
732 1.1 cgd * Put add users to a group.
733 1.1 cgd */
734 1.33 christos PUBLIC int
735 1.12 wiz group(void *v)
736 1.1 cgd {
737 1.33 christos const char **argv = v;
738 1.9 lukem struct grouphead *gh;
739 1.9 lukem struct group *gp;
740 1.9 lukem int h;
741 1.1 cgd int s;
742 1.33 christos const char *gname;
743 1.33 christos const char **ap, **p;
744 1.1 cgd
745 1.14 wiz if (*argv == NULL) {
746 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
747 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
748 1.1 cgd s++;
749 1.37 christos ap = salloc(s * sizeof(*ap));
750 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
751 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
752 1.1 cgd *p++ = gh->g_name;
753 1.14 wiz *p = NULL;
754 1.1 cgd sort(ap);
755 1.14 wiz for (p = ap; *p != NULL; p++)
756 1.1 cgd printgroup(*p);
757 1.33 christos return 0;
758 1.1 cgd }
759 1.14 wiz if (argv[1] == NULL) {
760 1.1 cgd printgroup(*argv);
761 1.33 christos return 0;
762 1.1 cgd }
763 1.1 cgd gname = *argv;
764 1.1 cgd h = hash(gname);
765 1.15 wiz if ((gh = findgroup(gname)) == NULL) {
766 1.37 christos gh = ecalloc(1, sizeof(*gh));
767 1.1 cgd gh->g_name = vcopy(gname);
768 1.15 wiz gh->g_list = NULL;
769 1.1 cgd gh->g_link = groups[h];
770 1.1 cgd groups[h] = gh;
771 1.1 cgd }
772 1.1 cgd
773 1.1 cgd /*
774 1.1 cgd * Insert names from the command list into the group.
775 1.1 cgd * Who cares if there are duplicates? They get tossed
776 1.1 cgd * later anyway.
777 1.1 cgd */
778 1.1 cgd
779 1.31 christos for (ap = argv + 1; *ap != NULL; ap++) {
780 1.37 christos gp = ecalloc(1, sizeof(*gp));
781 1.1 cgd gp->ge_name = vcopy(*ap);
782 1.1 cgd gp->ge_link = gh->g_list;
783 1.1 cgd gh->g_list = gp;
784 1.1 cgd }
785 1.28 christos return 0;
786 1.28 christos }
787 1.28 christos
788 1.28 christos /*
789 1.28 christos * Delete the named group alias. Return zero if the group was
790 1.28 christos * successfully deleted, or -1 if there was no such group.
791 1.28 christos */
792 1.28 christos static int
793 1.28 christos delgroup(const char *name)
794 1.28 christos {
795 1.28 christos struct grouphead *gh, *p;
796 1.28 christos struct group *g;
797 1.28 christos int h;
798 1.28 christos
799 1.28 christos h = hash(name);
800 1.28 christos for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
801 1.28 christos if (strcmp(gh->g_name, name) == 0) {
802 1.28 christos if (p == NULL)
803 1.28 christos groups[h] = gh->g_link;
804 1.28 christos else
805 1.28 christos p->g_link = gh->g_link;
806 1.28 christos while (gh->g_list != NULL) {
807 1.28 christos g = gh->g_list;
808 1.28 christos gh->g_list = g->ge_link;
809 1.28 christos free(g->ge_name);
810 1.28 christos free(g);
811 1.28 christos }
812 1.28 christos free(gh->g_name);
813 1.28 christos free(gh);
814 1.28 christos return 0;
815 1.28 christos }
816 1.28 christos return -1;
817 1.28 christos }
818 1.28 christos
819 1.28 christos /*
820 1.33 christos * The unalias command takes a list of alises
821 1.33 christos * and discards the remembered groups of users.
822 1.1 cgd */
823 1.33 christos PUBLIC int
824 1.33 christos unalias(void *v)
825 1.1 cgd {
826 1.9 lukem char **ap;
827 1.1 cgd
828 1.33 christos for (ap = v; *ap != NULL; ap++)
829 1.33 christos (void)delgroup(*ap);
830 1.33 christos return 0;
831 1.1 cgd }
832 1.1 cgd
833 1.1 cgd /*
834 1.1 cgd * The do nothing command for comments.
835 1.1 cgd */
836 1.1 cgd /*ARGSUSED*/
837 1.33 christos PUBLIC int
838 1.31 christos null(void *v __unused)
839 1.1 cgd {
840 1.1 cgd return 0;
841 1.1 cgd }
842 1.1 cgd
843 1.1 cgd /*
844 1.1 cgd * Change to another file. With no argument, print information about
845 1.1 cgd * the current file.
846 1.1 cgd */
847 1.33 christos PUBLIC int
848 1.12 wiz file(void *v)
849 1.1 cgd {
850 1.5 christos char **argv = v;
851 1.1 cgd
852 1.14 wiz if (argv[0] == NULL) {
853 1.27 christos (void)newfileinfo(0);
854 1.1 cgd return 0;
855 1.1 cgd }
856 1.1 cgd if (setfile(*argv) < 0)
857 1.1 cgd return 1;
858 1.1 cgd announce();
859 1.33 christos
860 1.1 cgd return 0;
861 1.1 cgd }
862 1.1 cgd
863 1.1 cgd /*
864 1.1 cgd * Expand file names like echo
865 1.1 cgd */
866 1.33 christos PUBLIC int
867 1.12 wiz echo(void *v)
868 1.1 cgd {
869 1.5 christos char **argv = v;
870 1.9 lukem char **ap;
871 1.26 christos const char *cp;
872 1.1 cgd
873 1.14 wiz for (ap = argv; *ap != NULL; ap++) {
874 1.1 cgd cp = *ap;
875 1.14 wiz if ((cp = expand(cp)) != NULL) {
876 1.1 cgd if (ap != argv)
877 1.27 christos (void)putchar(' ');
878 1.27 christos (void)printf("%s", cp);
879 1.1 cgd }
880 1.1 cgd }
881 1.27 christos (void)putchar('\n');
882 1.1 cgd return 0;
883 1.1 cgd }
884 1.1 cgd
885 1.33 christos /*
886 1.33 christos * Routines to push and pop the condition code to support nested
887 1.33 christos * if/else/endif statements.
888 1.33 christos */
889 1.33 christos static void
890 1.33 christos push_cond(int c_cond)
891 1.1 cgd {
892 1.33 christos struct cond_stack_s *csp;
893 1.33 christos csp = emalloc(sizeof(*csp));
894 1.33 christos csp->c_cond = c_cond;
895 1.33 christos csp->c_next = cond_stack;
896 1.33 christos cond_stack = csp;
897 1.1 cgd }
898 1.1 cgd
899 1.33 christos static int
900 1.33 christos pop_cond(void)
901 1.1 cgd {
902 1.33 christos int c_cond;
903 1.33 christos struct cond_stack_s *csp;
904 1.33 christos
905 1.33 christos if ((csp = cond_stack) == NULL)
906 1.33 christos return -1;
907 1.1 cgd
908 1.33 christos c_cond = csp->c_cond;
909 1.33 christos cond_stack = csp->c_next;
910 1.33 christos free(csp);
911 1.35 christos return c_cond;
912 1.1 cgd }
913 1.1 cgd
914 1.1 cgd /*
915 1.1 cgd * Conditional commands. These allow one to parameterize one's
916 1.1 cgd * .mailrc and do some things if sending, others if receiving.
917 1.1 cgd */
918 1.33 christos static int
919 1.33 christos if_push(void)
920 1.33 christos {
921 1.33 christos push_cond(cond);
922 1.33 christos cond &= ~CELSE;
923 1.33 christos if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
924 1.33 christos cond |= CIGN;
925 1.33 christos return 1;
926 1.33 christos }
927 1.33 christos return 0;
928 1.33 christos }
929 1.33 christos
930 1.33 christos PUBLIC int
931 1.12 wiz ifcmd(void *v)
932 1.1 cgd {
933 1.5 christos char **argv = v;
934 1.33 christos char *keyword = argv[0];
935 1.33 christos static const struct modetbl_s {
936 1.33 christos const char *m_name;
937 1.33 christos enum mailmode_e m_mode;
938 1.33 christos } modetbl[] = {
939 1.33 christos { "receiving", mm_receiving },
940 1.33 christos { "sending", mm_sending },
941 1.33 christos { "headersonly", mm_hdrsonly },
942 1.33 christos { NULL, 0 },
943 1.33 christos };
944 1.33 christos const struct modetbl_s *mtp;
945 1.33 christos
946 1.33 christos if (if_push())
947 1.33 christos return 0;
948 1.1 cgd
949 1.33 christos cond = CIF;
950 1.33 christos for (mtp = modetbl; mtp->m_name; mtp++)
951 1.33 christos if (strcasecmp(keyword, mtp->m_name) == 0)
952 1.33 christos break;
953 1.33 christos
954 1.33 christos if (mtp->m_name == NULL) {
955 1.33 christos cond = CNONE;
956 1.33 christos (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
957 1.33 christos return 1;
958 1.1 cgd }
959 1.33 christos if (mtp->m_mode != mailmode)
960 1.33 christos cond |= CSKIP;
961 1.33 christos
962 1.33 christos return 0;
963 1.33 christos }
964 1.33 christos
965 1.33 christos PUBLIC int
966 1.33 christos ifdefcmd(void *v)
967 1.33 christos {
968 1.33 christos char **argv = v;
969 1.33 christos
970 1.33 christos if (if_push())
971 1.33 christos return 0;
972 1.33 christos
973 1.33 christos cond = CIF;
974 1.33 christos if (value(argv[0]) == NULL)
975 1.33 christos cond |= CSKIP;
976 1.33 christos
977 1.33 christos return 0;
978 1.33 christos }
979 1.33 christos
980 1.33 christos PUBLIC int
981 1.33 christos ifndefcmd(void *v)
982 1.33 christos {
983 1.33 christos int rval;
984 1.33 christos rval = ifdefcmd(v);
985 1.33 christos cond ^= CSKIP;
986 1.33 christos return rval;
987 1.1 cgd }
988 1.1 cgd
989 1.1 cgd /*
990 1.1 cgd * Implement 'else'. This is pretty simple -- we just
991 1.1 cgd * flip over the conditional flag.
992 1.1 cgd */
993 1.27 christos /*ARGSUSED*/
994 1.33 christos PUBLIC int
995 1.31 christos elsecmd(void *v __unused)
996 1.1 cgd {
997 1.33 christos if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
998 1.33 christos (void)printf("\"else\" without matching \"if\"\n");
999 1.33 christos cond = CNONE;
1000 1.33 christos return 1;
1001 1.33 christos }
1002 1.33 christos if ((cond & CIGN) == 0) {
1003 1.33 christos cond ^= CSKIP;
1004 1.33 christos cond |= CELSE;
1005 1.1 cgd }
1006 1.33 christos return 0;
1007 1.1 cgd }
1008 1.1 cgd
1009 1.1 cgd /*
1010 1.1 cgd * End of if statement. Just set cond back to anything.
1011 1.1 cgd */
1012 1.27 christos /*ARGSUSED*/
1013 1.33 christos PUBLIC int
1014 1.31 christos endifcmd(void *v __unused)
1015 1.1 cgd {
1016 1.33 christos if (cond_stack == NULL || (cond & CIF) != CIF) {
1017 1.33 christos (void)printf("\"endif\" without matching \"if\"\n");
1018 1.33 christos cond = CNONE;
1019 1.33 christos return 1;
1020 1.1 cgd }
1021 1.33 christos cond = pop_cond();
1022 1.33 christos return 0;
1023 1.1 cgd }
1024 1.1 cgd
1025 1.1 cgd /*
1026 1.1 cgd * Set the list of alternate names.
1027 1.1 cgd */
1028 1.33 christos PUBLIC int
1029 1.12 wiz alternates(void *v)
1030 1.1 cgd {
1031 1.5 christos char **namelist = v;
1032 1.36 christos size_t c;
1033 1.9 lukem char **ap, **ap2, *cp;
1034 1.1 cgd
1035 1.1 cgd c = argcount(namelist) + 1;
1036 1.1 cgd if (c == 1) {
1037 1.1 cgd if (altnames == 0)
1038 1.33 christos return 0;
1039 1.1 cgd for (ap = altnames; *ap; ap++)
1040 1.27 christos (void)printf("%s ", *ap);
1041 1.27 christos (void)printf("\n");
1042 1.33 christos return 0;
1043 1.1 cgd }
1044 1.1 cgd if (altnames != 0)
1045 1.27 christos free(altnames);
1046 1.36 christos altnames = ecalloc(c, sizeof(char *));
1047 1.1 cgd for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1048 1.36 christos cp = ecalloc(strlen(*ap) + 1, sizeof(char));
1049 1.27 christos (void)strcpy(cp, *ap);
1050 1.1 cgd *ap2 = cp;
1051 1.1 cgd }
1052 1.1 cgd *ap2 = 0;
1053 1.33 christos return 0;
1054 1.1 cgd }
1055