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