Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/cmd3.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2010 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* Still more user commands.
73
*/
74
75
#include "mailx.h"
76
77
/*
78
* Expand the shell escape by expanding unescaped !'s into the
79
* last issued command where possible.
80
*/
81
static int
82
bangexp(char* str, size_t size)
83
{
84
register char* s;
85
register char* t;
86
register char* e;
87
int changed = 0;
88
char buf[LINESIZE];
89
90
s = str;
91
t = buf;
92
e = t + sizeof(buf) - 1;
93
while (*s) {
94
if (*s == '!' && state.var.bang) {
95
if ((e - t) < strlen(state.last.bang)) {
96
overf:
97
note(0, "Command buffer overflow");
98
return -1;
99
}
100
changed++;
101
t = strcopy(t, state.last.bang);
102
s++;
103
continue;
104
}
105
if (*s == '\\' && s[1] == '!') {
106
if (t >= e)
107
goto overf;
108
*t++ = '!';
109
s += 2;
110
changed++;
111
}
112
if (t >= e)
113
goto overf;
114
*t++ = *s++;
115
}
116
*t = 0;
117
if (changed)
118
note(0, "!%s", buf);
119
strncopy(str, buf, size);
120
strncopy(state.last.bang, buf, sizeof(state.last.bang) - 1);
121
return 0;
122
}
123
124
/*
125
* Process a shell escape by saving signals, ignoring signals,
126
* and running sh -c
127
*/
128
int
129
shell(char* str)
130
{
131
sig_t saveint = signal(SIGINT, SIG_IGN);
132
char cmd[LINESIZE];
133
134
strncopy(cmd, str, sizeof(cmd));
135
if (bangexp(cmd, sizeof(cmd)) < 0)
136
return 1;
137
if (*cmd)
138
run_command(state.var.shell, 0, -1, -1, "-c", cmd, NiL);
139
else
140
run_command(state.var.shell, 0, -1, -1, NiL, NiL, NiL);
141
signal(SIGINT, saveint);
142
note(0, "!");
143
return 0;
144
}
145
146
/*
147
* Stay within the lines.
148
*/
149
static void
150
margin(FILE* fp, const char* name, register const char* s, int indent, int sep, int tab, const struct var* vp)
151
{
152
register const char** ap;
153
register int c;
154
register int n;
155
register int q;
156
const char* av[8];
157
158
if (sep) {
159
fprintf(fp, "%*s", n = indent, name);
160
for (c = 0; c < sep; c++) {
161
putc(' ', fp);
162
n++;
163
}
164
}
165
else {
166
fprintf(fp, "%s ", name);
167
n = strlen(name) + 1;
168
}
169
ap = av;
170
*ap++ = T(s);
171
if (vp && (s = vp->initialize)) {
172
*ap++ = " ";
173
if (*s) {
174
*ap++ = T("The default value is");
175
*ap++ = " \"";
176
*ap++ = s;
177
*ap++ = "\".";
178
}
179
else if (vp->set)
180
*ap++ = T("The default value is computed at runtime.");
181
else
182
*ap++ = T("On by default.");
183
}
184
*ap = 0;
185
ap = av;
186
q = 0;
187
while (s = *ap++)
188
while (c = *s++) {
189
if (c == '\n' || (isspace(c) || q && !isalnum(c)) && n >= (MARGIN - 4)) {
190
if (!isspace(c))
191
putchar(c);
192
fprintf(fp, "\n%*s", n = indent, " ");
193
for (c = 0; c < sep; c++) {
194
putc(' ', fp);
195
n++;
196
}
197
}
198
else if (c == '\t') {
199
if (tab) {
200
while (n < tab) {
201
putc(' ', fp);
202
n++;
203
}
204
}
205
else do {
206
putc(' ', fp);
207
} while (++n % 4);
208
}
209
else {
210
if (c == q)
211
q = 0;
212
else if (c == '"')
213
q = c;
214
putc(c, fp);
215
n++;
216
}
217
}
218
putc('\n', fp);
219
}
220
221
/*
222
* List command help.
223
*/
224
static void
225
helpcmd(FILE* fp, register const struct cmd* cp)
226
{
227
margin(fp, cp->c_name, cp->c_help, 4, 0, 0, NiL);
228
}
229
230
/*
231
* List escape command help.
232
*/
233
static void
234
helpesc(FILE* fp, register const struct esc* ep)
235
{
236
char buf[3];
237
238
buf[0] = *state.var.escape;
239
buf[1] = *ep->e_name;
240
buf[2] = 0;
241
margin(fp, buf, ep->e_help, 4, 0, 16, NiL);
242
}
243
244
/*
245
* List variable help.
246
*/
247
static void
248
helpvar(FILE* fp, register const struct var* vp)
249
{
250
char* help;
251
252
if (vp->flags & A)
253
{
254
sfprintf(state.path.temp, T("Equivalent to %s."), vp->help);
255
help = struse(state.path.temp);
256
}
257
else
258
help = (char*)vp->help;
259
margin(fp, vp->name, help, 14, 2, 0, vp);
260
}
261
262
/*
263
* Print out a nice help message.
264
*/
265
int
266
help(char** argv)
267
{
268
register const struct cmd* cp;
269
register const struct esc* ep;
270
register const struct var* vp;
271
register char* s;
272
register char* a;
273
register char* t;
274
register char* l;
275
char* r;
276
int all;
277
int cat;
278
FILE* fp;
279
280
if (state.more.discipline ||
281
!state.var.interactive ||
282
!state.var.crt ||
283
!(fp = pipeopen(state.var.pager, "w")))
284
fp = stdout;
285
s = *argv++;
286
a = s ? *argv : (char*)0;
287
l = "commands";
288
t = T(l);
289
r = "--------";
290
all = isall(s);
291
cat = 0;
292
if (all || !s || (streq(s, l) || streq(s, t)) && ++cat) {
293
fprintf(fp, "%s\n%s\n%s\n", r, t, r);
294
for (cp = state.cmdtab; cp < &state.cmdtab[state.cmdnum]; cp++)
295
helpcmd(fp, cp);
296
}
297
if (all || s) {
298
l = "variables";
299
t = T(l);
300
r = "---------";
301
if (all && !(cp = 0)|| (cp = (const struct cmd*)strpsearch(state.cmdtab, state.cmdnum, sizeof(struct cmd), s, NiL)) || (streq(s, l) || streq(s, t)) && ++cat) {
302
if (!cp || a && cp->c_func == (Cmd_f)set) {
303
if (!cp && !cat || !a || isall(a)) {
304
fprintf(fp, "%s\n%s\n%s\n", r, t, r);
305
for (vp = state.vartab; vp < &state.vartab[state.varnum]; vp++)
306
helpvar(fp, vp);
307
}
308
else if (vp = (const struct var*)strsearch(state.vartab, state.varnum, sizeof(struct var), stracmp, a, NiL))
309
helpvar(fp, vp);
310
else
311
note(0, "\"%s\": unknown variable", a);
312
}
313
else
314
helpcmd(fp, cp);
315
}
316
if (!cp) {
317
if (s && (*s == '~' || *s == *state.var.escape)) {
318
if (*++s)
319
a = s;
320
s = state.var.escape;
321
}
322
l = "escape";
323
t = T(l);
324
r = "---------------";
325
if (all || *s == *state.var.escape || (streq(s, t) || streq(s, l) || streq(s, "tilde")) && ++cat) {
326
if (!a) {
327
fprintf(fp, "%s\n%s %s\n%s\n", r, t, T("commands"), r);
328
for (ep = state.esctab; ep < &state.esctab[state.escnum]; ep++)
329
helpesc(fp, ep);
330
}
331
else if (ep = (const struct esc*)strsearch(state.esctab, state.escnum, sizeof(struct esc), stracmp, a, NiL))
332
helpesc(fp, ep);
333
else
334
note(0, "\"%s\": unknown escape command", a);
335
}
336
else if (!cat)
337
note(0, "\"%s\": unknown command", s);
338
}
339
}
340
fileclose(fp);
341
return 0;
342
}
343
344
/*
345
* Change user's working directory.
346
*/
347
int
348
cd(char** arglist)
349
{
350
char* cp;
351
char* tp;
352
int show = 0;
353
354
if (!*arglist)
355
cp = state.var.home;
356
else
357
if (!(cp = expand(*arglist, 1)))
358
return 1;
359
if (cp[0] == '-' && cp[1] == 0) {
360
if (!(cp = state.var.oldpwd)) {
361
note(0, "No previous working directory");
362
return 1;
363
}
364
show = 1;
365
}
366
if (chdir(cp) < 0) {
367
#if _PACKAGE_ast
368
if (state.var.cdpath && (cp[0] != '.' || cp[1] != 0 && cp[1] != '/' && (cp[1] != '.' || cp[2] != 0 && cp[2] != '/')) && pathaccess(state.var.cdpath, cp, NiL, 0, state.path.path, sizeof(state.path.path))) {
369
cp = state.path.path;
370
show = 1;
371
}
372
else
373
#endif
374
show = -1;
375
if (show < 0 || chdir(cp) < 0) {
376
note(SYSTEM, "%s", cp);
377
return 1;
378
}
379
}
380
tp = state.var.oldpwd;
381
state.var.oldpwd = state.var.pwd;
382
state.var.pwd = tp;
383
if (!getcwd(state.var.pwd, PATHSIZE))
384
strncopy(state.var.pwd, cp, PATHSIZE);
385
if (show)
386
printf("%s\n", state.var.pwd);
387
return 0;
388
}
389
390
/*
391
* Print the full path of the current working directory.
392
*/
393
int
394
pwd(void)
395
{
396
printf("%s\n", state.var.pwd);
397
return 0;
398
}
399
400
/*
401
* Create a new folder directory.
402
*/
403
404
int
405
cmdmkdir(char** arglist)
406
{
407
char* cp;
408
409
if (!(cp = expand(*arglist, 1)))
410
return 1;
411
if (*cp == '@')
412
imap_mkdir(cp);
413
else if (mkdir(cp, MAILMODE|S_IXUSR)) {
414
note(SYSTEM, "%s", cp);
415
return 1;
416
}
417
return 0;
418
}
419
420
/*
421
* Rename a folder or folder directory.
422
*/
423
424
int
425
cmdrename(char** arglist)
426
{
427
char* f;
428
char* t;
429
430
if (!(f = expand(arglist[0], 1)) || !(t = expand(arglist[1], 1)))
431
return 1;
432
if (*f == '@')
433
imap_rename(f, t);
434
else if (rename(f, t)) {
435
note(SYSTEM, "%s: cannot rename to %s", f, t);
436
return 1;
437
}
438
return 0;
439
}
440
441
/*
442
* Remove an empty folder directory.
443
*/
444
445
int
446
cmdrmdir(char** arglist)
447
{
448
char* cp;
449
450
if (!(cp = expand(*arglist, 1)))
451
return 1;
452
if (*cp == '@')
453
imap_rmdir(cp);
454
else if (rmdir(cp)) {
455
note(SYSTEM, "%s", cp);
456
return 1;
457
}
458
return 0;
459
}
460
461
/*
462
* Modify the subject we are replying to to begin with Re: if
463
* it does not already.
464
*/
465
static char*
466
reedit(register char* subj)
467
{
468
char* newsubj;
469
470
if (!subj)
471
return 0;
472
#if _PACKAGE_ast
473
if (isalpha(subj[0]) && isalpha(subj[1]) && subj[2] == ':' && subj[3] == ' ' && subj[4])
474
#else
475
if ((subj[0] == 'r' || subj[0] == 'R') &&
476
(subj[1] == 'e' || subj[1] == 'E') &&
477
subj[2] == ':')
478
#endif
479
return subj;
480
newsubj = salloc(strlen(subj) + 5);
481
strcpy(newsubj, "Re: ");
482
strcpy(newsubj + 4, subj);
483
return newsubj;
484
}
485
486
/*
487
* Low level for the reply variants.
488
*/
489
static int
490
reply1(struct msg* msgvec, unsigned long flags, int all)
491
{
492
struct msg* mp;
493
char* cp;
494
char* rp;
495
struct msg* ip;
496
struct header head;
497
498
memset(&head, 0, sizeof(head));
499
if (all) {
500
if (msgvec->m_index && (msgvec + 1)->m_index) {
501
note(0, "Sorry, can't reply to multiple messages at once");
502
return 1;
503
}
504
mp = state.msg.list + msgvec->m_index - 1;
505
touchmsg(mp);
506
state.msg.dot = mp;
507
rp = grab(mp, GREPLY, NiL);
508
if (cp = grab(mp, GTO, NiL))
509
extract(&head, GTO, cp);
510
if (cp = grab(mp, GCC, NiL))
511
extract(&head, GCC, cp);
512
extract(&head, GTO|GFIRST|GMETOO, rp);
513
}
514
else {
515
for (ip = msgvec; ip->m_index; ip++) {
516
mp = state.msg.list + ip->m_index - 1;
517
touchmsg(mp);
518
state.msg.dot = mp;
519
extract(&head, GTO, grab(mp, GREPLY, NiL));
520
}
521
if (!head.h_names)
522
return 0;
523
mp = state.msg.list + msgvec->m_index - 1;
524
}
525
if (head.h_subject = grab(mp, GSUB, NiL))
526
head.h_flags |= GSUB;
527
head.h_subject = reedit(head.h_subject);
528
if (flags & (FOLLOWUP|INTERPOLATE)) {
529
if (head.h_messageid = grab(mp, GMESSAGEID, NiL))
530
head.h_flags |= GMESSAGEID;
531
if (head.h_references = grab(mp, GREFERENCES, NiL))
532
head.h_flags |= GREFERENCES;
533
}
534
sendmail(&head, flags|HEADERS);
535
return 0;
536
}
537
538
int
539
followup(struct msg* msgvec)
540
{
541
return reply1(msgvec, FOLLOWUP, !state.var.flipr);
542
}
543
544
int
545
Followup(struct msg* msgvec)
546
{
547
return reply1(msgvec, FOLLOWUP, !!state.var.flipr);
548
}
549
550
int
551
join(struct msg* msgvec)
552
{
553
return reply1(msgvec, INTERPOLATE, !state.var.flipr);
554
}
555
556
int
557
Join(struct msg* msgvec)
558
{
559
return reply1(msgvec, INTERPOLATE, !!state.var.flipr);
560
}
561
562
int
563
reply(struct msg* msgvec)
564
{
565
return reply1(msgvec, 0, !state.var.flipr);
566
}
567
568
int
569
Reply(struct msg* msgvec)
570
{
571
return reply1(msgvec, 0, !!state.var.flipr);
572
}
573
574
int
575
replyall(struct msg* msgvec)
576
{
577
return reply1(msgvec, 0, 1);
578
}
579
580
int
581
replysender(struct msg* msgvec)
582
{
583
return reply1(msgvec, 0, 0);
584
}
585
586
/*
587
* Print the size of each message.
588
*/
589
int
590
size(struct msg* msgvec)
591
{
592
register struct msg* mp;
593
register struct msg* ip;
594
595
for (ip = msgvec; ip->m_index; ip++) {
596
mp = state.msg.list + ip->m_index - 1;
597
note(0, "%d: %ld/%ld", mp->m_index, (long)mp->m_lines, (long)mp->m_size);
598
}
599
return 0;
600
}
601
602
/*
603
* Quit quickly. If we are sourcing, just pop the input level
604
* by returning an error.
605
*/
606
int
607
cmdexit(int e)
608
{
609
if (!state.sourcing) {
610
if (state.msg.imap.state)
611
imap_exit(e);
612
exit(e);
613
}
614
return 1;
615
}
616
617
/*
618
* Set or display a variable value. Syntax is similar to that
619
* of csh.
620
*/
621
int
622
set(register char** argv)
623
{
624
register char* cp;
625
register char* np;
626
register char* ep;
627
int all = 0;
628
int errs = 0;
629
630
if (!*argv || (all = isall(*argv) && !*(argv + 1)))
631
errs += varlist(all);
632
else while (cp = *argv++) {
633
np = cp;
634
for (;;) {
635
if (*cp == '=') {
636
ep = cp;
637
*cp++ = 0;
638
break;
639
}
640
if (!*cp++) {
641
ep = 0;
642
cp = state.on;
643
break;
644
}
645
}
646
if (!*np) {
647
note(0, "Non-null variable name required");
648
errs++;
649
}
650
else
651
errs += varset(np, cp);
652
if (ep)
653
*ep = '=';
654
}
655
return errs;
656
}
657
658
/*
659
* Unset a bunch of variable values.
660
*/
661
int
662
unset(register char** argv)
663
{
664
register char* s;
665
register int all = -1;
666
register int errs = 0;
667
668
if (!*argv || (all = isall(*argv) && !*(argv + 1)))
669
errs += varlist(all);
670
else while (s = *argv++)
671
errs += varset(s, NiL);
672
return errs;
673
}
674
675
/*
676
* List an alias.
677
*/
678
static int
679
listalias(Dt_t* dt, void* object, void* context)
680
{
681
register struct list* mp;
682
683
printf("%-16s", ((struct name*)object)->name);
684
for (mp = (struct list*)((struct name*)object)->value; mp; mp = mp->next)
685
printf(" %s", mp->name);
686
putchar('\n');
687
return 0;
688
}
689
690
/*
691
* Low level for alias/alternates.
692
*/
693
static int
694
alias1(register char** argv, unsigned long flags)
695
{
696
register struct name* ap;
697
register struct list* mp;
698
register char* s;
699
register char* t;
700
register char* v;
701
char* name;
702
FILE* fp;
703
char buf[LINESIZE];
704
705
if (!(name = *argv++))
706
dictwalk(&state.aliases, listalias, NiL);
707
else if (!*argv) {
708
if (!(ap = dictsearch(&state.aliases, name, LOOKUP|IGNORECASE)))
709
note(0, "\"%s\": unknown alias", name);
710
else
711
listalias(NiL, ap, NiL);
712
}
713
else if (streq(name, "<")) {
714
/*
715
* sendmail alias file parse
716
*/
717
while (name = *argv++) {
718
if (fp = fileopen(name, "EXr")) {
719
while (s = fgets(buf, sizeof(buf), fp)) {
720
for (; isspace(*s); s++);
721
if (*s != '#') {
722
for (t = s; *t && *t != ':' && !isspace(*t); t++);
723
if (*t) {
724
*t++ = 0;
725
while (*t == ':' || isspace(*t))
726
t++;
727
ap = dictsearch(&state.aliases, s, INSERT|IGNORECASE);
728
ap->flags |= flags;
729
do {
730
for (v = t; *v && *v != ',' && !isspace(*v); v++);
731
if (*v) {
732
*v++ = 0;
733
while (*v == ',' || isspace(*v))
734
v++;
735
}
736
if (!(mp = newof(0, struct list, 1, strlen(t) + 1)))
737
note(PANIC, "Out of space");
738
strcpy(mp->name, t);
739
mp->next = (struct list*)ap->value;
740
ap->value = (void*)mp;
741
} while (*(t = v));
742
}
743
}
744
}
745
fileclose(fp);
746
}
747
}
748
}
749
else {
750
/*
751
* Insert names from the command list into the alias group.
752
*/
753
ap = dictsearch(&state.aliases, name, INSERT|IGNORECASE);
754
ap->flags |= flags;
755
while (name = *argv++) {
756
if (!(mp = newof(0, struct list, 1, strlen(name) + 1)))
757
note(PANIC, "Out of space");
758
strcpy(mp->name, name);
759
mp->next = (struct list*)ap->value;
760
ap->value = (void*)mp;
761
}
762
}
763
return 0;
764
}
765
766
/*
767
* Case sensitive alias.
768
*/
769
int
770
alias(register char** argv)
771
{
772
return alias1(argv, GALIAS);
773
}
774
775
/*
776
* Unset a bunch of aliases.
777
*/
778
int
779
unalias(register char** argv)
780
{
781
register char* s;
782
783
while (s = *argv++)
784
if (!(dictsearch(&state.aliases, s, DELETE)))
785
note(0, "\"%s\": unknown alias", s);
786
return 0;
787
}
788
789
/*
790
* List an alternate.
791
*/
792
static int
793
listalternate(Dt_t* dt, void* object, void* context)
794
{
795
if (((struct name*)object)->flags & GALTERNATE)
796
printf("%s\n", ((struct name*)object)->name);
797
return 0;
798
}
799
800
/*
801
* Set the list of alternate names.
802
*/
803
int
804
alternates(register char** argv)
805
{
806
register char* s;
807
char* av[3];
808
809
if (!*argv)
810
dictwalk(&state.aliases, listalternate, NiL);
811
else {
812
av[1] = state.var.user;
813
av[2] = 0;
814
while (s = *argv++) {
815
av[0] = s;
816
alias1(av, GALTERNATE);
817
}
818
}
819
return 0;
820
}
821
822
/*
823
* The do nothing command for comments.
824
*/
825
826
/*ARGSUSED*/
827
int
828
null(int e)
829
{
830
return 0;
831
}
832
833
/*
834
* Change to another folder. With no argument, print information about
835
* the current folder.
836
*/
837
int
838
folder(register char** argv)
839
{
840
if (!argv[0]) {
841
folderinfo(0);
842
return 0;
843
}
844
if (setfolder(*argv) < 0)
845
return 1;
846
announce();
847
return 0;
848
}
849
850
/*
851
* Expand file names like echo
852
*/
853
int
854
echo(register char** argv)
855
{
856
register char* s;
857
register int sep;
858
859
sep = 0;
860
while (s = *argv++) {
861
if (s = expand(s, 0)) {
862
if (sep++)
863
putchar(' ');
864
printf("%s", s);
865
}
866
}
867
putchar('\n');
868
return 0;
869
}
870
871
/*
872
* Conditional commands. These allow one to parameterize one's
873
* .mailrc and do some things if sending, others if receiving.
874
*/
875
int
876
cmdif(char** argv)
877
{
878
register char* s;
879
register char* t;
880
register char* x;
881
int n;
882
883
if (state.cond) {
884
note(0, "Invalid nested \"%s\"", state.cmd->c_name);
885
return 1;
886
}
887
s = argv[0];
888
t = (x = argv[1]) ? argv[2] : (char*)0;
889
if (n = streq(s, "!")) {
890
s = x;
891
x = t;
892
t = 0;
893
}
894
else if (n = *s == '!')
895
s++;
896
if (*s && (!*(s + 1) || *(s + 1) == '?') && !x && *(x = s + 2)) {
897
switch (*s) {
898
case 'd':
899
if (!x || t)
900
goto bad;
901
n ^= isdir(expand(x, 1));
902
goto ok;
903
case 'f':
904
if (!x || t)
905
goto bad;
906
n ^= isreg(expand(x, 1));
907
goto ok;
908
case 'r':
909
case 'R':
910
if (x)
911
goto bad;
912
state.cond = n ? SEND : RECEIVE;
913
return 0;
914
case 's':
915
case 'S':
916
if (x)
917
goto bad;
918
state.cond = n ? RECEIVE : SEND;
919
return 0;
920
}
921
}
922
if (!x)
923
n ^= varget(s) != 0;
924
else if (!t || n)
925
goto bad;
926
else {
927
if (n = *x == '!')
928
x++;
929
switch (*x) {
930
case '=':
931
if (*++x == '=')
932
x++;
933
n ^= streq(s, t);
934
break;
935
default:
936
n = -1;
937
break;
938
}
939
if (n < 0 || *x) {
940
note(0, "\"%s\": unknown %s condition operator", argv[1], state.cmd->c_name);
941
return 1;
942
}
943
}
944
ok:
945
state.cond = n ? state.mode : -state.mode;
946
return 0;
947
bad:
948
sfprintf(state.path.temp, "%s", s);
949
if (x) {
950
sfprintf(state.path.temp, " %s", x);
951
if (t)
952
sfprintf(state.path.temp, " %s", t);
953
}
954
note(0, "\"%s\": unknown %s condition", struse(state.path.temp), state.cmd->c_name);
955
return 1;
956
}
957
958
/*
959
* Implement 'else'. This is pretty simple -- we just
960
* flip over the conditional flag.
961
*/
962
int
963
cmdelse(void)
964
{
965
if (!state.cond) {
966
note(0, "\"%s\" without matching \"if\"", state.cmd->c_name);
967
return 1;
968
}
969
state.cond = -state.cond;
970
return 0;
971
}
972
973
/*
974
* End of if statement. Just set cond back to anything.
975
*/
976
int
977
cmdendif(void)
978
{
979
980
if (!state.cond) {
981
note(0, "\"%s\" without matching \"if\"", state.cmd->c_name);
982
return 1;
983
}
984
state.cond = 0;
985
return 0;
986
}
987
988