cmd3.c revision 1.34 1 1.34 christos /* $NetBSD: cmd3.c,v 1.34 2007/06/05 17:50:22 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.34 christos __RCSID("$NetBSD: cmd3.c,v 1.34 2007/06/05 17:50:22 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.31 christos while (*q != '\0' && *q != ',' && !isblank((unsigned char)*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.31 christos while (*q == ',' || isblank((unsigned char)*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.12 wiz _respond(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.33 christos _Respond(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.33 christos return _respond(msgvec);
396 1.33 christos else
397 1.33 christos return _Respond(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.33 christos return _Respond(msgvec);
406 1.33 christos else
407 1.33 christos return _respond(msgvec);
408 1.1 cgd }
409 1.1 cgd
410 1.1 cgd /*
411 1.1 cgd * Preserve the named messages, so that they will be sent
412 1.1 cgd * back to the system mailbox.
413 1.1 cgd */
414 1.33 christos PUBLIC int
415 1.12 wiz preserve(void *v)
416 1.1 cgd {
417 1.5 christos int *msgvec = v;
418 1.33 christos int *ip;
419 1.1 cgd
420 1.1 cgd if (edit) {
421 1.27 christos (void)printf("Cannot \"preserve\" in edit mode\n");
422 1.33 christos return 1;
423 1.1 cgd }
424 1.33 christos for (ip = msgvec; *ip != 0; ip++)
425 1.33 christos dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
426 1.33 christos
427 1.33 christos return 0;
428 1.1 cgd }
429 1.1 cgd
430 1.1 cgd /*
431 1.33 christos * Mark all given messages as unread, preserving the new status.
432 1.1 cgd */
433 1.33 christos PUBLIC int
434 1.12 wiz unread(void *v)
435 1.1 cgd {
436 1.9 lukem int *msgvec = v;
437 1.9 lukem int *ip;
438 1.1 cgd
439 1.33 christos for (ip = msgvec; *ip != 0; ip++)
440 1.33 christos dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
441 1.33 christos
442 1.33 christos return 0;
443 1.1 cgd }
444 1.1 cgd
445 1.1 cgd /*
446 1.32 christos * Mark all given messages as read.
447 1.32 christos */
448 1.33 christos PUBLIC int
449 1.32 christos markread(void *v)
450 1.32 christos {
451 1.32 christos int *msgvec = v;
452 1.32 christos int *ip;
453 1.32 christos
454 1.33 christos for (ip = msgvec; *ip != 0; ip++)
455 1.33 christos dot = set_m_flag(*ip,
456 1.33 christos ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
457 1.33 christos
458 1.33 christos return 0;
459 1.32 christos }
460 1.32 christos
461 1.32 christos /*
462 1.1 cgd * Print the size of each message.
463 1.1 cgd */
464 1.33 christos PUBLIC int
465 1.12 wiz messize(void *v)
466 1.1 cgd {
467 1.5 christos int *msgvec = v;
468 1.9 lukem struct message *mp;
469 1.9 lukem int *ip, mesg;
470 1.1 cgd
471 1.7 pk for (ip = msgvec; *ip != 0; ip++) {
472 1.1 cgd mesg = *ip;
473 1.33 christos mp = get_message(mesg);
474 1.27 christos (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
475 1.27 christos (unsigned long long)mp->m_size);
476 1.1 cgd }
477 1.33 christos return 0;
478 1.1 cgd }
479 1.1 cgd
480 1.1 cgd /*
481 1.1 cgd * Quit quickly. If we are sourcing, just pop the input level
482 1.1 cgd * by returning an error.
483 1.1 cgd */
484 1.27 christos /*ARGSUSED*/
485 1.33 christos PUBLIC int
486 1.31 christos rexit(void *v __unused)
487 1.1 cgd {
488 1.1 cgd if (sourcing)
489 1.33 christos return 1;
490 1.5 christos exit(0);
491 1.1 cgd /*NOTREACHED*/
492 1.1 cgd }
493 1.1 cgd
494 1.1 cgd /*
495 1.1 cgd * Set or display a variable value. Syntax is similar to that
496 1.1 cgd * of csh.
497 1.1 cgd */
498 1.33 christos PUBLIC int
499 1.12 wiz set(void *v)
500 1.1 cgd {
501 1.33 christos const char **arglist = v;
502 1.9 lukem struct var *vp;
503 1.26 christos const char *cp;
504 1.33 christos char varbuf[LINESIZE];
505 1.33 christos const char **ap, **p;
506 1.1 cgd int errs, h, s;
507 1.23 ross size_t l;
508 1.1 cgd
509 1.14 wiz if (*arglist == NULL) {
510 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
511 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
512 1.1 cgd s++;
513 1.26 christos ap = salloc(s * sizeof *ap);
514 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
515 1.15 wiz for (vp = variables[h]; vp != NULL; vp = vp->v_link)
516 1.1 cgd *p++ = vp->v_name;
517 1.14 wiz *p = NULL;
518 1.1 cgd sort(ap);
519 1.14 wiz for (p = ap; *p != NULL; p++)
520 1.27 christos (void)printf("%s\t%s\n", *p, value(*p));
521 1.33 christos return 0;
522 1.1 cgd }
523 1.1 cgd errs = 0;
524 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
525 1.1 cgd cp = *ap;
526 1.1 cgd while (*cp != '=' && *cp != '\0')
527 1.23 ross ++cp;
528 1.23 ross l = cp - *ap;
529 1.23 ross if (l >= sizeof varbuf)
530 1.33 christos l = sizeof(varbuf) - 1;
531 1.27 christos (void)strncpy(varbuf, *ap, l);
532 1.24 ross varbuf[l] = '\0';
533 1.1 cgd if (*cp == '\0')
534 1.1 cgd cp = "";
535 1.1 cgd else
536 1.1 cgd cp++;
537 1.1 cgd if (equal(varbuf, "")) {
538 1.27 christos (void)printf("Non-null variable name required\n");
539 1.1 cgd errs++;
540 1.1 cgd continue;
541 1.1 cgd }
542 1.1 cgd assign(varbuf, cp);
543 1.1 cgd }
544 1.33 christos return errs;
545 1.1 cgd }
546 1.1 cgd
547 1.1 cgd /*
548 1.1 cgd * Unset a bunch of variable values.
549 1.1 cgd */
550 1.33 christos PUBLIC int
551 1.12 wiz unset(void *v)
552 1.1 cgd {
553 1.5 christos char **arglist = v;
554 1.9 lukem struct var *vp, *vp2;
555 1.1 cgd int errs, h;
556 1.1 cgd char **ap;
557 1.1 cgd
558 1.1 cgd errs = 0;
559 1.14 wiz for (ap = arglist; *ap != NULL; ap++) {
560 1.15 wiz if ((vp2 = lookup(*ap)) == NULL) {
561 1.11 dean if (getenv(*ap)) {
562 1.27 christos (void)unsetenv(*ap);
563 1.11 dean } else if (!sourcing) {
564 1.27 christos (void)printf("\"%s\": undefined variable\n", *ap);
565 1.1 cgd errs++;
566 1.1 cgd }
567 1.1 cgd continue;
568 1.1 cgd }
569 1.1 cgd h = hash(*ap);
570 1.1 cgd if (vp2 == variables[h]) {
571 1.1 cgd variables[h] = variables[h]->v_link;
572 1.10 wsanchez v_free(vp2->v_name);
573 1.10 wsanchez v_free(vp2->v_value);
574 1.27 christos free(vp2);
575 1.1 cgd continue;
576 1.1 cgd }
577 1.1 cgd for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
578 1.33 christos continue;
579 1.1 cgd vp->v_link = vp2->v_link;
580 1.10 wsanchez v_free(vp2->v_name);
581 1.10 wsanchez v_free(vp2->v_value);
582 1.27 christos free(vp2);
583 1.1 cgd }
584 1.33 christos return errs;
585 1.1 cgd }
586 1.1 cgd
587 1.1 cgd /*
588 1.29 christos * Show a variable value.
589 1.29 christos */
590 1.33 christos PUBLIC int
591 1.29 christos show(void *v)
592 1.29 christos {
593 1.33 christos const char **arglist = v;
594 1.29 christos struct var *vp;
595 1.33 christos const char **ap, **p;
596 1.29 christos int h, s;
597 1.29 christos
598 1.29 christos if (*arglist == NULL) {
599 1.29 christos for (h = 0, s = 1; h < HSHSIZE; h++)
600 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
601 1.29 christos s++;
602 1.29 christos ap = salloc(s * sizeof *ap);
603 1.29 christos for (h = 0, p = ap; h < HSHSIZE; h++)
604 1.29 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link)
605 1.29 christos *p++ = vp->v_name;
606 1.29 christos *p = NULL;
607 1.29 christos sort(ap);
608 1.29 christos for (p = ap; *p != NULL; p++)
609 1.29 christos (void)printf("%s=%s\n", *p, value(*p));
610 1.33 christos return 0;
611 1.29 christos }
612 1.29 christos
613 1.29 christos for (ap = arglist; *ap != NULL; ap++) {
614 1.29 christos char *val = value(*ap);
615 1.29 christos (void)printf("%s=%s\n", *ap, val ? val : "<null>");
616 1.29 christos }
617 1.29 christos return 0;
618 1.29 christos }
619 1.29 christos
620 1.32 christos
621 1.29 christos /*
622 1.1 cgd * Put add users to a group.
623 1.1 cgd */
624 1.33 christos PUBLIC int
625 1.12 wiz group(void *v)
626 1.1 cgd {
627 1.33 christos const char **argv = v;
628 1.9 lukem struct grouphead *gh;
629 1.9 lukem struct group *gp;
630 1.9 lukem int h;
631 1.1 cgd int s;
632 1.33 christos const char *gname;
633 1.33 christos const char **ap, **p;
634 1.1 cgd
635 1.14 wiz if (*argv == NULL) {
636 1.1 cgd for (h = 0, s = 1; h < HSHSIZE; h++)
637 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
638 1.1 cgd s++;
639 1.26 christos ap = salloc(s * sizeof *ap);
640 1.1 cgd for (h = 0, p = ap; h < HSHSIZE; h++)
641 1.15 wiz for (gh = groups[h]; gh != NULL; gh = gh->g_link)
642 1.1 cgd *p++ = gh->g_name;
643 1.14 wiz *p = NULL;
644 1.1 cgd sort(ap);
645 1.14 wiz for (p = ap; *p != NULL; p++)
646 1.1 cgd printgroup(*p);
647 1.33 christos return 0;
648 1.1 cgd }
649 1.14 wiz if (argv[1] == NULL) {
650 1.1 cgd printgroup(*argv);
651 1.33 christos return 0;
652 1.1 cgd }
653 1.1 cgd gname = *argv;
654 1.1 cgd h = hash(gname);
655 1.15 wiz if ((gh = findgroup(gname)) == NULL) {
656 1.31 christos gh = (struct grouphead *) ecalloc(1, sizeof *gh);
657 1.1 cgd gh->g_name = vcopy(gname);
658 1.15 wiz gh->g_list = NULL;
659 1.1 cgd gh->g_link = groups[h];
660 1.1 cgd groups[h] = gh;
661 1.1 cgd }
662 1.1 cgd
663 1.1 cgd /*
664 1.1 cgd * Insert names from the command list into the group.
665 1.1 cgd * Who cares if there are duplicates? They get tossed
666 1.1 cgd * later anyway.
667 1.1 cgd */
668 1.1 cgd
669 1.31 christos for (ap = argv + 1; *ap != NULL; ap++) {
670 1.31 christos gp = (struct group *) ecalloc(1, sizeof *gp);
671 1.1 cgd gp->ge_name = vcopy(*ap);
672 1.1 cgd gp->ge_link = gh->g_list;
673 1.1 cgd gh->g_list = gp;
674 1.1 cgd }
675 1.28 christos return 0;
676 1.28 christos }
677 1.28 christos
678 1.28 christos /*
679 1.28 christos * Delete the named group alias. Return zero if the group was
680 1.28 christos * successfully deleted, or -1 if there was no such group.
681 1.28 christos */
682 1.28 christos static int
683 1.28 christos delgroup(const char *name)
684 1.28 christos {
685 1.28 christos struct grouphead *gh, *p;
686 1.28 christos struct group *g;
687 1.28 christos int h;
688 1.28 christos
689 1.28 christos h = hash(name);
690 1.28 christos for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
691 1.28 christos if (strcmp(gh->g_name, name) == 0) {
692 1.28 christos if (p == NULL)
693 1.28 christos groups[h] = gh->g_link;
694 1.28 christos else
695 1.28 christos p->g_link = gh->g_link;
696 1.28 christos while (gh->g_list != NULL) {
697 1.28 christos g = gh->g_list;
698 1.28 christos gh->g_list = g->ge_link;
699 1.28 christos free(g->ge_name);
700 1.28 christos free(g);
701 1.28 christos }
702 1.28 christos free(gh->g_name);
703 1.28 christos free(gh);
704 1.28 christos return 0;
705 1.28 christos }
706 1.28 christos return -1;
707 1.28 christos }
708 1.28 christos
709 1.28 christos /*
710 1.33 christos * The unalias command takes a list of alises
711 1.33 christos * and discards the remembered groups of users.
712 1.1 cgd */
713 1.33 christos PUBLIC int
714 1.33 christos unalias(void *v)
715 1.1 cgd {
716 1.9 lukem char **ap;
717 1.1 cgd
718 1.33 christos for (ap = v; *ap != NULL; ap++)
719 1.33 christos (void)delgroup(*ap);
720 1.33 christos return 0;
721 1.1 cgd }
722 1.1 cgd
723 1.1 cgd /*
724 1.1 cgd * The do nothing command for comments.
725 1.1 cgd */
726 1.1 cgd /*ARGSUSED*/
727 1.33 christos PUBLIC int
728 1.31 christos null(void *v __unused)
729 1.1 cgd {
730 1.1 cgd return 0;
731 1.1 cgd }
732 1.1 cgd
733 1.1 cgd /*
734 1.1 cgd * Change to another file. With no argument, print information about
735 1.1 cgd * the current file.
736 1.1 cgd */
737 1.33 christos PUBLIC int
738 1.12 wiz file(void *v)
739 1.1 cgd {
740 1.5 christos char **argv = v;
741 1.1 cgd
742 1.14 wiz if (argv[0] == NULL) {
743 1.27 christos (void)newfileinfo(0);
744 1.1 cgd return 0;
745 1.1 cgd }
746 1.1 cgd if (setfile(*argv) < 0)
747 1.1 cgd return 1;
748 1.1 cgd announce();
749 1.33 christos
750 1.1 cgd return 0;
751 1.1 cgd }
752 1.1 cgd
753 1.1 cgd /*
754 1.1 cgd * Expand file names like echo
755 1.1 cgd */
756 1.33 christos PUBLIC int
757 1.12 wiz echo(void *v)
758 1.1 cgd {
759 1.5 christos char **argv = v;
760 1.9 lukem char **ap;
761 1.26 christos const char *cp;
762 1.1 cgd
763 1.14 wiz for (ap = argv; *ap != NULL; ap++) {
764 1.1 cgd cp = *ap;
765 1.14 wiz if ((cp = expand(cp)) != NULL) {
766 1.1 cgd if (ap != argv)
767 1.27 christos (void)putchar(' ');
768 1.27 christos (void)printf("%s", cp);
769 1.1 cgd }
770 1.1 cgd }
771 1.27 christos (void)putchar('\n');
772 1.1 cgd return 0;
773 1.1 cgd }
774 1.1 cgd
775 1.33 christos /*
776 1.33 christos * Routines to push and pop the condition code to support nested
777 1.33 christos * if/else/endif statements.
778 1.33 christos */
779 1.33 christos static void
780 1.33 christos push_cond(int c_cond)
781 1.1 cgd {
782 1.33 christos struct cond_stack_s *csp;
783 1.33 christos csp = emalloc(sizeof(*csp));
784 1.33 christos csp->c_cond = c_cond;
785 1.33 christos csp->c_next = cond_stack;
786 1.33 christos cond_stack = csp;
787 1.1 cgd }
788 1.1 cgd
789 1.33 christos static int
790 1.33 christos pop_cond(void)
791 1.1 cgd {
792 1.33 christos int c_cond;
793 1.33 christos struct cond_stack_s *csp;
794 1.33 christos
795 1.33 christos if ((csp = cond_stack) == NULL)
796 1.33 christos return -1;
797 1.1 cgd
798 1.33 christos c_cond = csp->c_cond;
799 1.33 christos cond_stack = csp->c_next;
800 1.33 christos free(csp);
801 1.33 christos return c_cond;
802 1.1 cgd }
803 1.1 cgd
804 1.1 cgd /*
805 1.1 cgd * Conditional commands. These allow one to parameterize one's
806 1.1 cgd * .mailrc and do some things if sending, others if receiving.
807 1.1 cgd */
808 1.33 christos static int
809 1.33 christos if_push(void)
810 1.33 christos {
811 1.33 christos push_cond(cond);
812 1.33 christos cond &= ~CELSE;
813 1.33 christos if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
814 1.33 christos cond |= CIGN;
815 1.33 christos return 1;
816 1.33 christos }
817 1.33 christos return 0;
818 1.33 christos }
819 1.33 christos
820 1.33 christos PUBLIC int
821 1.12 wiz ifcmd(void *v)
822 1.1 cgd {
823 1.5 christos char **argv = v;
824 1.33 christos char *keyword = argv[0];
825 1.33 christos static const struct modetbl_s {
826 1.33 christos const char *m_name;
827 1.33 christos enum mailmode_e m_mode;
828 1.33 christos } modetbl[] = {
829 1.33 christos { "receiving", mm_receiving },
830 1.33 christos { "sending", mm_sending },
831 1.33 christos { "headersonly", mm_hdrsonly },
832 1.33 christos { NULL, 0 },
833 1.33 christos };
834 1.33 christos const struct modetbl_s *mtp;
835 1.33 christos
836 1.33 christos if (if_push())
837 1.33 christos return 0;
838 1.1 cgd
839 1.33 christos cond = CIF;
840 1.33 christos for (mtp = modetbl; mtp->m_name; mtp++)
841 1.33 christos if (strcasecmp(keyword, mtp->m_name) == 0)
842 1.33 christos break;
843 1.33 christos
844 1.33 christos if (mtp->m_name == NULL) {
845 1.33 christos cond = CNONE;
846 1.33 christos (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
847 1.33 christos return 1;
848 1.1 cgd }
849 1.33 christos if (mtp->m_mode != mailmode)
850 1.33 christos cond |= CSKIP;
851 1.33 christos
852 1.33 christos return 0;
853 1.33 christos }
854 1.33 christos
855 1.33 christos PUBLIC int
856 1.33 christos ifdefcmd(void *v)
857 1.33 christos {
858 1.33 christos char **argv = v;
859 1.33 christos
860 1.33 christos if (if_push())
861 1.33 christos return 0;
862 1.33 christos
863 1.33 christos cond = CIF;
864 1.33 christos if (value(argv[0]) == NULL)
865 1.33 christos cond |= CSKIP;
866 1.33 christos
867 1.33 christos return 0;
868 1.33 christos }
869 1.33 christos
870 1.33 christos PUBLIC int
871 1.33 christos ifndefcmd(void *v)
872 1.33 christos {
873 1.33 christos int rval;
874 1.33 christos rval = ifdefcmd(v);
875 1.33 christos cond ^= CSKIP;
876 1.33 christos return rval;
877 1.1 cgd }
878 1.1 cgd
879 1.1 cgd /*
880 1.1 cgd * Implement 'else'. This is pretty simple -- we just
881 1.1 cgd * flip over the conditional flag.
882 1.1 cgd */
883 1.27 christos /*ARGSUSED*/
884 1.33 christos PUBLIC int
885 1.31 christos elsecmd(void *v __unused)
886 1.1 cgd {
887 1.33 christos if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
888 1.33 christos (void)printf("\"else\" without matching \"if\"\n");
889 1.33 christos cond = CNONE;
890 1.33 christos return 1;
891 1.33 christos }
892 1.33 christos if ((cond & CIGN) == 0) {
893 1.33 christos cond ^= CSKIP;
894 1.33 christos cond |= CELSE;
895 1.1 cgd }
896 1.33 christos return 0;
897 1.1 cgd }
898 1.1 cgd
899 1.1 cgd /*
900 1.1 cgd * End of if statement. Just set cond back to anything.
901 1.1 cgd */
902 1.27 christos /*ARGSUSED*/
903 1.33 christos PUBLIC int
904 1.31 christos endifcmd(void *v __unused)
905 1.1 cgd {
906 1.33 christos if (cond_stack == NULL || (cond & CIF) != CIF) {
907 1.33 christos (void)printf("\"endif\" without matching \"if\"\n");
908 1.33 christos cond = CNONE;
909 1.33 christos return 1;
910 1.1 cgd }
911 1.33 christos cond = pop_cond();
912 1.33 christos return 0;
913 1.1 cgd }
914 1.1 cgd
915 1.1 cgd /*
916 1.1 cgd * Set the list of alternate names.
917 1.1 cgd */
918 1.33 christos PUBLIC int
919 1.12 wiz alternates(void *v)
920 1.1 cgd {
921 1.5 christos char **namelist = v;
922 1.9 lukem int c;
923 1.9 lukem char **ap, **ap2, *cp;
924 1.1 cgd
925 1.1 cgd c = argcount(namelist) + 1;
926 1.1 cgd if (c == 1) {
927 1.1 cgd if (altnames == 0)
928 1.33 christos return 0;
929 1.1 cgd for (ap = altnames; *ap; ap++)
930 1.27 christos (void)printf("%s ", *ap);
931 1.27 christos (void)printf("\n");
932 1.33 christos return 0;
933 1.1 cgd }
934 1.1 cgd if (altnames != 0)
935 1.27 christos free(altnames);
936 1.31 christos altnames = (char **) ecalloc((unsigned) c, sizeof (char *));
937 1.1 cgd for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
938 1.31 christos cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char));
939 1.27 christos (void)strcpy(cp, *ap);
940 1.1 cgd *ap2 = cp;
941 1.1 cgd }
942 1.1 cgd *ap2 = 0;
943 1.33 christos return 0;
944 1.1 cgd }
945