Annotation of src/usr.bin/mail/cmd3.c, Revision 1.3
1.3 ! millert 1: /* $OpenBSD: cmd3.c,v 1.2 1996/06/11 12:53:34 deraadt Exp $ */
1.2 deraadt 2: /* $NetBSD: cmd3.c,v 1.5 1996/06/08 19:48:14 christos Exp $ */
3:
1.1 deraadt 4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.2 deraadt 38: #if 0
1.1 deraadt 39: static char sccsid[] = "@(#)cmd3.c 8.1 (Berkeley) 6/6/93";
1.2 deraadt 40: #else
1.3 ! millert 41: static char rcsid[] = "$OpenBSD: cmd3.c,v 1.2 1996/06/11 12:53:34 deraadt Exp $";
1.2 deraadt 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "rcv.h"
46: #include "extern.h"
47:
48: /*
49: * Mail -- a mail program
50: *
51: * Still more user commands.
52: */
1.2 deraadt 53: static int diction __P((const void *, const void *));
1.1 deraadt 54:
55: /*
56: * Process a shell escape by saving signals, ignoring signals,
57: * and forking a sh -c
58: */
59: int
1.2 deraadt 60: shell(v)
61: void *v;
1.1 deraadt 62: {
1.2 deraadt 63: char *str = v;
1.1 deraadt 64: sig_t sigint = signal(SIGINT, SIG_IGN);
65: char *shell;
66: char cmd[BUFSIZ];
67:
68: (void) strcpy(cmd, str);
69: if (bangexp(cmd) < 0)
70: return 1;
71: if ((shell = value("SHELL")) == NOSTR)
72: shell = _PATH_CSHELL;
73: (void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
74: (void) signal(SIGINT, sigint);
75: printf("!\n");
76: return 0;
77: }
78:
79: /*
80: * Fork an interactive shell.
81: */
82: /*ARGSUSED*/
83: int
1.2 deraadt 84: dosh(v)
85: void *v;
1.1 deraadt 86: {
87: sig_t sigint = signal(SIGINT, SIG_IGN);
88: char *shell;
89:
90: if ((shell = value("SHELL")) == NOSTR)
91: shell = _PATH_CSHELL;
92: (void) run_command(shell, 0, -1, -1, NOSTR, NOSTR, NOSTR);
93: (void) signal(SIGINT, sigint);
94: putchar('\n');
95: return 0;
96: }
97:
98: /*
99: * Expand the shell escape by expanding unescaped !'s into the
100: * last issued command where possible.
101: */
102:
103: char lastbang[128];
104:
105: int
106: bangexp(str)
107: char *str;
108: {
109: char bangbuf[BUFSIZ];
110: register char *cp, *cp2;
111: register int n;
112: int changed = 0;
113:
114: cp = str;
115: cp2 = bangbuf;
116: n = BUFSIZ;
117: while (*cp) {
118: if (*cp == '!') {
119: if (n < strlen(lastbang)) {
120: overf:
121: printf("Command buffer overflow\n");
122: return(-1);
123: }
124: changed++;
125: strcpy(cp2, lastbang);
126: cp2 += strlen(lastbang);
127: n -= strlen(lastbang);
128: cp++;
129: continue;
130: }
131: if (*cp == '\\' && cp[1] == '!') {
132: if (--n <= 1)
133: goto overf;
134: *cp2++ = '!';
135: cp += 2;
136: changed++;
137: }
138: if (--n <= 1)
139: goto overf;
140: *cp2++ = *cp++;
141: }
142: *cp2 = 0;
143: if (changed) {
144: printf("!%s\n", bangbuf);
145: fflush(stdout);
146: }
147: strcpy(str, bangbuf);
148: strncpy(lastbang, bangbuf, 128);
149: lastbang[127] = 0;
150: return(0);
151: }
152:
153: /*
154: * Print out a nice help message from some file or another.
155: */
156:
157: int
1.2 deraadt 158: help(v)
159: void *v;
1.1 deraadt 160: {
161: register c;
162: register FILE *f;
163:
164: if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
165: perror(_PATH_HELP);
166: return(1);
167: }
168: while ((c = getc(f)) != EOF)
169: putchar(c);
170: Fclose(f);
171: return(0);
172: }
173:
174: /*
175: * Change user's working directory.
176: */
177: int
1.2 deraadt 178: schdir(v)
179: void *v;
1.1 deraadt 180: {
1.2 deraadt 181: char **arglist = v;
1.1 deraadt 182: char *cp;
183:
184: if (*arglist == NOSTR)
185: cp = homedir;
186: else
187: if ((cp = expand(*arglist)) == NOSTR)
188: return(1);
189: if (chdir(cp) < 0) {
190: perror(cp);
191: return(1);
192: }
193: return 0;
194: }
195:
196: int
1.2 deraadt 197: respond(v)
198: void *v;
1.1 deraadt 199: {
1.2 deraadt 200: int *msgvec = v;
1.1 deraadt 201: if (value("Replyall") == NOSTR)
202: return (_respond(msgvec));
203: else
204: return (_Respond(msgvec));
205: }
206:
207: /*
208: * Reply to a list of messages. Extract each name from the
209: * message header and send them off to mail1()
210: */
211: int
212: _respond(msgvec)
213: int *msgvec;
214: {
215: struct message *mp;
216: char *cp, *rcv, *replyto;
217: char **ap;
218: struct name *np;
219: struct header head;
220:
221: if (msgvec[1] != 0) {
222: printf("Sorry, can't reply to multiple messages at once\n");
223: return(1);
224: }
225: mp = &message[msgvec[0] - 1];
226: touch(mp);
227: dot = mp;
228: if ((rcv = skin(hfield("from", mp))) == NOSTR)
229: rcv = skin(nameof(mp, 1));
230: if ((replyto = skin(hfield("reply-to", mp))) != NOSTR)
231: np = extract(replyto, GTO);
232: else if ((cp = skin(hfield("to", mp))) != NOSTR)
233: np = extract(cp, GTO);
234: else
235: np = NIL;
236: np = elide(np);
237: /*
238: * Delete my name from the reply list,
239: * and with it, all my alternate names.
240: */
241: np = delname(np, myname);
242: if (altnames)
243: for (ap = altnames; *ap; ap++)
244: np = delname(np, *ap);
245: if (np != NIL && replyto == NOSTR)
246: np = cat(np, extract(rcv, GTO));
247: else if (np == NIL) {
248: if (replyto != NOSTR)
249: printf("Empty reply-to field -- replying to author\n");
250: np = extract(rcv, GTO);
251: }
252: head.h_to = np;
253: if ((head.h_subject = hfield("subject", mp)) == NOSTR)
254: head.h_subject = hfield("subj", mp);
255: head.h_subject = reedit(head.h_subject);
256: if (replyto == NOSTR && (cp = skin(hfield("cc", mp))) != NOSTR) {
257: np = elide(extract(cp, GCC));
258: np = delname(np, myname);
259: if (altnames != 0)
260: for (ap = altnames; *ap; ap++)
261: np = delname(np, *ap);
262: head.h_cc = np;
263: } else
264: head.h_cc = NIL;
265: head.h_bcc = NIL;
266: head.h_smopts = NIL;
267: mail1(&head, 1);
268: return(0);
269: }
270:
271: /*
272: * Modify the subject we are replying to to begin with Re: if
273: * it does not already.
274: */
275: char *
276: reedit(subj)
277: register char *subj;
278: {
279: char *newsubj;
280:
281: if (subj == NOSTR)
282: return NOSTR;
283: if ((subj[0] == 'r' || subj[0] == 'R') &&
284: (subj[1] == 'e' || subj[1] == 'E') &&
285: subj[2] == ':')
286: return subj;
287: newsubj = salloc(strlen(subj) + 5);
288: strcpy(newsubj, "Re: ");
289: strcpy(newsubj + 4, subj);
290: return newsubj;
291: }
292:
293: /*
294: * Preserve the named messages, so that they will be sent
295: * back to the system mailbox.
296: */
297: int
1.2 deraadt 298: preserve(v)
299: void *v;
1.1 deraadt 300: {
1.2 deraadt 301: int *msgvec = v;
1.1 deraadt 302: register struct message *mp;
303: register int *ip, mesg;
304:
305: if (edit) {
306: printf("Cannot \"preserve\" in edit mode\n");
307: return(1);
308: }
309: for (ip = msgvec; *ip != NULL; ip++) {
310: mesg = *ip;
311: mp = &message[mesg-1];
312: mp->m_flag |= MPRESERVE;
313: mp->m_flag &= ~MBOX;
314: dot = mp;
315: }
316: return(0);
317: }
318:
319: /*
320: * Mark all given messages as unread.
321: */
322: int
1.2 deraadt 323: unread(v)
324: void *v;
1.1 deraadt 325: {
1.2 deraadt 326: int *msgvec = v;
1.1 deraadt 327: register int *ip;
328:
329: for (ip = msgvec; *ip != NULL; ip++) {
330: dot = &message[*ip-1];
331: dot->m_flag &= ~(MREAD|MTOUCH);
332: dot->m_flag |= MSTATUS;
333: }
334: return(0);
335: }
336:
337: /*
338: * Print the size of each message.
339: */
340: int
1.2 deraadt 341: messize(v)
342: void *v;
1.1 deraadt 343: {
1.2 deraadt 344: int *msgvec = v;
1.1 deraadt 345: register struct message *mp;
346: register int *ip, mesg;
347:
348: for (ip = msgvec; *ip != NULL; ip++) {
349: mesg = *ip;
350: mp = &message[mesg-1];
1.3 ! millert 351: printf("%d: %d/%d\n", mesg, mp->m_lines, mp->m_size);
1.1 deraadt 352: }
353: return(0);
354: }
355:
356: /*
357: * Quit quickly. If we are sourcing, just pop the input level
358: * by returning an error.
359: */
360: int
1.2 deraadt 361: rexit(v)
362: void *v;
1.1 deraadt 363: {
364: if (sourcing)
365: return(1);
1.2 deraadt 366: exit(0);
1.1 deraadt 367: /*NOTREACHED*/
368: }
369:
370: /*
371: * Set or display a variable value. Syntax is similar to that
372: * of csh.
373: */
374: int
1.2 deraadt 375: set(v)
376: void *v;
1.1 deraadt 377: {
1.2 deraadt 378: char **arglist = v;
1.1 deraadt 379: register struct var *vp;
380: register char *cp, *cp2;
381: char varbuf[BUFSIZ], **ap, **p;
382: int errs, h, s;
383:
384: if (*arglist == NOSTR) {
385: for (h = 0, s = 1; h < HSHSIZE; h++)
386: for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
387: s++;
388: ap = (char **) salloc(s * sizeof *ap);
389: for (h = 0, p = ap; h < HSHSIZE; h++)
390: for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
391: *p++ = vp->v_name;
392: *p = NOSTR;
393: sort(ap);
394: for (p = ap; *p != NOSTR; p++)
395: printf("%s\t%s\n", *p, value(*p));
396: return(0);
397: }
398: errs = 0;
399: for (ap = arglist; *ap != NOSTR; ap++) {
400: cp = *ap;
401: cp2 = varbuf;
402: while (*cp != '=' && *cp != '\0')
403: *cp2++ = *cp++;
404: *cp2 = '\0';
405: if (*cp == '\0')
406: cp = "";
407: else
408: cp++;
409: if (equal(varbuf, "")) {
410: printf("Non-null variable name required\n");
411: errs++;
412: continue;
413: }
414: assign(varbuf, cp);
415: }
416: return(errs);
417: }
418:
419: /*
420: * Unset a bunch of variable values.
421: */
422: int
1.2 deraadt 423: unset(v)
424: void *v;
1.1 deraadt 425: {
1.2 deraadt 426: char **arglist = v;
1.1 deraadt 427: register struct var *vp, *vp2;
428: int errs, h;
429: char **ap;
430:
431: errs = 0;
432: for (ap = arglist; *ap != NOSTR; ap++) {
433: if ((vp2 = lookup(*ap)) == NOVAR) {
434: if (!sourcing) {
435: printf("\"%s\": undefined variable\n", *ap);
436: errs++;
437: }
438: continue;
439: }
440: h = hash(*ap);
441: if (vp2 == variables[h]) {
442: variables[h] = variables[h]->v_link;
443: vfree(vp2->v_name);
444: vfree(vp2->v_value);
445: free((char *)vp2);
446: continue;
447: }
448: for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
449: ;
450: vp->v_link = vp2->v_link;
451: vfree(vp2->v_name);
452: vfree(vp2->v_value);
453: free((char *) vp2);
454: }
455: return(errs);
456: }
457:
458: /*
459: * Put add users to a group.
460: */
461: int
1.2 deraadt 462: group(v)
463: void *v;
1.1 deraadt 464: {
1.2 deraadt 465: char **argv = v;
1.1 deraadt 466: register struct grouphead *gh;
467: register struct group *gp;
468: register int h;
469: int s;
470: char **ap, *gname, **p;
471:
472: if (*argv == NOSTR) {
473: for (h = 0, s = 1; h < HSHSIZE; h++)
474: for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
475: s++;
476: ap = (char **) salloc(s * sizeof *ap);
477: for (h = 0, p = ap; h < HSHSIZE; h++)
478: for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
479: *p++ = gh->g_name;
480: *p = NOSTR;
481: sort(ap);
482: for (p = ap; *p != NOSTR; p++)
483: printgroup(*p);
484: return(0);
485: }
486: if (argv[1] == NOSTR) {
487: printgroup(*argv);
488: return(0);
489: }
490: gname = *argv;
491: h = hash(gname);
492: if ((gh = findgroup(gname)) == NOGRP) {
493: gh = (struct grouphead *) calloc(sizeof *gh, 1);
494: gh->g_name = vcopy(gname);
495: gh->g_list = NOGE;
496: gh->g_link = groups[h];
497: groups[h] = gh;
498: }
499:
500: /*
501: * Insert names from the command list into the group.
502: * Who cares if there are duplicates? They get tossed
503: * later anyway.
504: */
505:
506: for (ap = argv+1; *ap != NOSTR; ap++) {
507: gp = (struct group *) calloc(sizeof *gp, 1);
508: gp->ge_name = vcopy(*ap);
509: gp->ge_link = gh->g_list;
510: gh->g_list = gp;
511: }
512: return(0);
513: }
514:
515: /*
516: * Sort the passed string vecotor into ascending dictionary
517: * order.
518: */
519: void
520: sort(list)
521: char **list;
522: {
523: register char **ap;
524:
525: for (ap = list; *ap != NOSTR; ap++)
526: ;
527: if (ap-list < 2)
528: return;
529: qsort(list, ap-list, sizeof(*list), diction);
530: }
531:
532: /*
533: * Do a dictionary order comparison of the arguments from
534: * qsort.
535: */
1.2 deraadt 536: static int
1.1 deraadt 537: diction(a, b)
538: const void *a, *b;
539: {
540: return(strcmp(*(char **)a, *(char **)b));
541: }
542:
543: /*
544: * The do nothing command for comments.
545: */
546:
547: /*ARGSUSED*/
548: int
1.2 deraadt 549: null(v)
550: void *v;
1.1 deraadt 551: {
552: return 0;
553: }
554:
555: /*
556: * Change to another file. With no argument, print information about
557: * the current file.
558: */
559: int
1.2 deraadt 560: file(v)
561: void *v;
1.1 deraadt 562: {
1.2 deraadt 563: char **argv = v;
1.1 deraadt 564:
565: if (argv[0] == NOSTR) {
566: newfileinfo();
567: return 0;
568: }
569: if (setfile(*argv) < 0)
570: return 1;
571: announce();
572: return 0;
573: }
574:
575: /*
576: * Expand file names like echo
577: */
578: int
1.2 deraadt 579: echo(v)
580: void *v;
1.1 deraadt 581: {
1.2 deraadt 582: char **argv = v;
1.1 deraadt 583: register char **ap;
584: register char *cp;
585:
586: for (ap = argv; *ap != NOSTR; ap++) {
587: cp = *ap;
588: if ((cp = expand(cp)) != NOSTR) {
589: if (ap != argv)
590: putchar(' ');
591: printf("%s", cp);
592: }
593: }
594: putchar('\n');
595: return 0;
596: }
597:
598: int
1.2 deraadt 599: Respond(v)
600: void *v;
1.1 deraadt 601: {
1.2 deraadt 602: int *msgvec = v;
1.1 deraadt 603: if (value("Replyall") == NOSTR)
604: return (_Respond(msgvec));
605: else
606: return (_respond(msgvec));
607: }
608:
609: /*
610: * Reply to a series of messages by simply mailing to the senders
611: * and not messing around with the To: and Cc: lists as in normal
612: * reply.
613: */
614: int
615: _Respond(msgvec)
616: int msgvec[];
617: {
618: struct header head;
619: struct message *mp;
620: register int *ap;
621: register char *cp;
622:
623: head.h_to = NIL;
624: for (ap = msgvec; *ap != 0; ap++) {
625: mp = &message[*ap - 1];
626: touch(mp);
627: dot = mp;
628: if ((cp = skin(hfield("from", mp))) == NOSTR)
629: cp = skin(nameof(mp, 2));
630: head.h_to = cat(head.h_to, extract(cp, GTO));
631: }
632: if (head.h_to == NIL)
633: return 0;
634: mp = &message[msgvec[0] - 1];
635: if ((head.h_subject = hfield("subject", mp)) == NOSTR)
636: head.h_subject = hfield("subj", mp);
637: head.h_subject = reedit(head.h_subject);
638: head.h_cc = NIL;
639: head.h_bcc = NIL;
640: head.h_smopts = NIL;
641: mail1(&head, 1);
642: return 0;
643: }
644:
645: /*
646: * Conditional commands. These allow one to parameterize one's
647: * .mailrc and do some things if sending, others if receiving.
648: */
649: int
1.2 deraadt 650: ifcmd(v)
651: void *v;
1.1 deraadt 652: {
1.2 deraadt 653: char **argv = v;
1.1 deraadt 654: register char *cp;
655:
656: if (cond != CANY) {
657: printf("Illegal nested \"if\"\n");
658: return(1);
659: }
660: cond = CANY;
661: cp = argv[0];
662: switch (*cp) {
663: case 'r': case 'R':
664: cond = CRCV;
665: break;
666:
667: case 's': case 'S':
668: cond = CSEND;
669: break;
670:
671: default:
672: printf("Unrecognized if-keyword: \"%s\"\n", cp);
673: return(1);
674: }
675: return(0);
676: }
677:
678: /*
679: * Implement 'else'. This is pretty simple -- we just
680: * flip over the conditional flag.
681: */
682: int
1.2 deraadt 683: elsecmd(v)
684: void *v;
1.1 deraadt 685: {
686:
687: switch (cond) {
688: case CANY:
689: printf("\"Else\" without matching \"if\"\n");
690: return(1);
691:
692: case CSEND:
693: cond = CRCV;
694: break;
695:
696: case CRCV:
697: cond = CSEND;
698: break;
699:
700: default:
701: printf("Mail's idea of conditions is screwed up\n");
702: cond = CANY;
703: break;
704: }
705: return(0);
706: }
707:
708: /*
709: * End of if statement. Just set cond back to anything.
710: */
711: int
1.2 deraadt 712: endifcmd(v)
713: void *v;
1.1 deraadt 714: {
715:
716: if (cond == CANY) {
717: printf("\"Endif\" without matching \"if\"\n");
718: return(1);
719: }
720: cond = CANY;
721: return(0);
722: }
723:
724: /*
725: * Set the list of alternate names.
726: */
727: int
1.2 deraadt 728: alternates(v)
729: void *v;
1.1 deraadt 730: {
1.2 deraadt 731: char **namelist = v;
1.1 deraadt 732: register int c;
733: register char **ap, **ap2, *cp;
734:
735: c = argcount(namelist) + 1;
736: if (c == 1) {
737: if (altnames == 0)
738: return(0);
739: for (ap = altnames; *ap; ap++)
740: printf("%s ", *ap);
741: printf("\n");
742: return(0);
743: }
744: if (altnames != 0)
745: free((char *) altnames);
746: altnames = (char **) calloc((unsigned) c, sizeof (char *));
747: for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
748: cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
749: strcpy(cp, *ap);
750: *ap2 = cp;
751: }
752: *ap2 = 0;
753: return(0);
754: }