Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/misc.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2006 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
* Auxiliary functions.
73
*
74
* So why isn't this stuff still in the file aux.c?
75
* What file base name is special on what system.
76
* I mean really special.
77
* Any wagers on whether aux is in the POSIX conformance test suite?
78
*/
79
80
#include "mailx.h"
81
82
#include <stdarg.h>
83
84
/*
85
* Note message.
86
*/
87
void
88
note(register int flags, const char* fmt, ...)
89
{
90
register FILE* fp;
91
va_list ap;
92
93
va_start(ap, fmt);
94
if ((flags & DEBUG) && !state.var.debug)
95
return;
96
if (flags & (ERROR|PANIC)) {
97
fp = stderr;
98
fflush(stdout);
99
}
100
else
101
fp = stdout;
102
if (state.var.coprocess)
103
fprintf(fp, "%s ", state.var.coprocess);
104
if (flags & IDENTIFY)
105
fprintf(fp, "mail: ");
106
if (flags & PANIC)
107
fprintf(fp, T("panic: "));
108
else if (flags & WARNING)
109
fprintf(fp, T("warning: "));
110
else if (flags & DEBUG)
111
fprintf(fp, T("debug: "));
112
vfprintf(fp, T(fmt), ap);
113
va_end(ap);
114
if (flags & SYSTEM)
115
fprintf(fp, ": %s", strerror(errno));
116
if (!(flags & PROMPT))
117
fprintf(fp, "\n");
118
fflush(fp);
119
if (flags & PANIC)
120
abort();
121
if (flags & (FATAL|PANIC))
122
exit(1);
123
}
124
125
/*
126
* Return a pointer to a dynamic copy of the argument.
127
*/
128
char*
129
savestr(char* str)
130
{
131
char* p;
132
int size = strlen(str) + 1;
133
134
if ((p = salloc(size)))
135
memcpy(p, str, size);
136
return p;
137
}
138
139
/*
140
* Touch the named message by setting its MTOUCH flag.
141
* Touched messages have the effect of not being sent
142
* back to the system mailbox on exit.
143
*/
144
void
145
touchmsg(register struct msg* mp)
146
{
147
if (mp->m_flag & MREAD)
148
msgflags(mp, MTOUCH, 0);
149
else
150
msgflags(mp, MREAD|MSTATUS, 0);
151
}
152
153
/*
154
* Test to see if the passed file name is a directory.
155
*/
156
int
157
isdir(char* name)
158
{
159
struct stat st;
160
161
if (!name || stat(name, &st) < 0)
162
return 0;
163
return S_ISDIR(st.st_mode);
164
}
165
166
/*
167
* Test to see if the passed file name is a regular file.
168
*/
169
int
170
isreg(char* name)
171
{
172
struct stat st;
173
174
if (!name || stat(name, &st) < 0)
175
return 0;
176
return S_ISREG(st.st_mode);
177
}
178
179
/*
180
* The following code deals with input stacking to do source
181
* commands. All but the current file pointer are saved on
182
* the stack.
183
*/
184
185
/*
186
* Pushdown current input file and switch to a new one.
187
* Set the global flag "sourcing" so that others will realize
188
* that they are no longer reading from a tty (in all probability).
189
*/
190
int
191
source(char** arglist)
192
{
193
FILE* fp;
194
195
if (!(fp = fileopen(*arglist, "EXr")))
196
return 1;
197
if (state.source.sp >= NOFILE - 1) {
198
note(0, "Too much \"sourcing\" going on");
199
fileclose(fp);
200
return 1;
201
}
202
state.source.stack[state.source.sp].input = state.input;
203
state.source.stack[state.source.sp].cond = state.cond;
204
state.source.stack[state.source.sp].loading = state.loading;
205
state.source.sp++;
206
state.loading = 0;
207
state.cond = 0;
208
state.input = fp;
209
state.sourcing++;
210
return 0;
211
}
212
213
/*
214
* Pop the current input back to the previous level.
215
* Update the "sourcing" flag as appropriate.
216
*/
217
int
218
unstack(void)
219
{
220
if (state.source.sp <= 0) {
221
note(0, "\"Source\" stack over-pop");
222
state.sourcing = 0;
223
return 1;
224
}
225
fileclose(state.input);
226
if (state.cond)
227
note(0, "Unmatched \"if\"");
228
state.source.sp--;
229
state.cond = state.source.stack[state.source.sp].cond;
230
state.loading = state.source.stack[state.source.sp].loading;
231
state.input = state.source.stack[state.source.sp].input;
232
if (state.source.sp == 0)
233
state.sourcing = state.loading;
234
return 0;
235
}
236
237
/*
238
* Touch the indicated file.
239
* This is nifty for the shell.
240
*/
241
void
242
alter(char* name)
243
{
244
touch(name, (time_t)0, (time_t)(-1), 0);
245
}
246
247
/*
248
* Examine the passed line buffer and
249
* return true if it is all blanks and tabs.
250
*/
251
int
252
blankline(char* linebuf)
253
{
254
register char* cp;
255
256
for (cp = linebuf; *cp; cp++)
257
if (*cp != ' ' && *cp != '\t')
258
return 0;
259
return 1;
260
}
261
262
/*
263
* Start of a "comment".
264
* Ignore it.
265
*/
266
static char*
267
skip_comment(register char* cp)
268
{
269
register int nesting = 1;
270
271
for (; nesting > 0 && *cp; cp++) {
272
switch (*cp) {
273
case '\\':
274
if (cp[1])
275
cp++;
276
break;
277
case '(':
278
nesting++;
279
break;
280
case ')':
281
nesting--;
282
break;
283
}
284
}
285
return cp;
286
}
287
288
/*
289
* shorten host if it is part of the local domain
290
*/
291
292
char*
293
localize(char* host)
294
{
295
register char* lp;
296
register char* le;
297
register char* hx;
298
299
hx = strchr(host, '.');
300
lp = state.var.local;
301
for (;;) {
302
if (le = strchr(lp, ','))
303
*le = 0;
304
if (!strcasecmp(lp, host)) {
305
if (le)
306
*le = ',';
307
return 0;
308
}
309
if (hx && !strcasecmp(lp, hx + 1)) {
310
*hx = 0;
311
if (le)
312
*le = ',';
313
return host;
314
}
315
if (!(lp = le))
316
break;
317
*lp++ = ',';
318
}
319
return host;
320
}
321
322
/*
323
* apply GCOMPARE, GDISPLAY, state.var.allnet, state.var.local
324
*/
325
326
char*
327
normalize(char* addr, unsigned long type, char* buf, size_t size)
328
{
329
register char* p;
330
register char* e;
331
register int n;
332
char* uucp;
333
char* arpa;
334
char* user;
335
char* inet;
336
int hadarpa;
337
int hadinet;
338
char temp[LINESIZE];
339
char norm[LINESIZE];
340
341
while (isspace(*addr))
342
addr++;
343
if (!(type & GFROM) && (p = strrchr(addr, ':')))
344
addr = p + 1;
345
if ((type & GCOMPARE) && state.var.allnet) {
346
if (p = strrchr(addr, '!'))
347
addr = p + 1;
348
if ((p = strchr(addr, '%')) || (p = strchr(addr, '@'))) {
349
if (buf) {
350
if ((n = p - addr) > size)
351
n = size;
352
addr = (char*)memcpy(buf, addr, n);
353
p = addr + n;
354
}
355
*p = 0;
356
return addr;
357
}
358
}
359
else {
360
strncopy(user = temp, addr, sizeof(temp));
361
uucp = arpa = inet = 0;
362
hadarpa = hadinet = 0;
363
if (p = strrchr(user, '!')) {
364
uucp = user;
365
*p++ = 0;
366
user = p;
367
if (p = strrchr(uucp, '!'))
368
p++;
369
if (p && (type & GDISPLAY)) {
370
uucp = p;
371
p = 0;
372
}
373
if (p && state.var.local)
374
uucp = localize(p);
375
}
376
if (p = strchr(user, '@')) {
377
hadinet = 1;
378
*p++ = 0;
379
inet = state.var.local ? localize(p) : p;
380
}
381
if (p = strchr(user, '%')) {
382
hadarpa = 1;
383
*p++ = 0;
384
if (!(type & (GCOMPARE|GDISPLAY)))
385
arpa = state.var.local ? localize(p) : p;
386
}
387
if (uucp &&
388
(hadinet || (inet && streq(uucp, inet)) ||
389
(hadarpa || arpa && streq(uucp, arpa))))
390
uucp = 0;
391
if (arpa && (hadinet || inet && streq(arpa, inet)))
392
arpa = 0;
393
if (type & GDISPLAY) {
394
if (inet)
395
uucp = 0;
396
else if (uucp) {
397
inet = uucp;
398
uucp = 0;
399
}
400
}
401
p = norm;
402
e = p + sizeof(norm);
403
if (uucp) {
404
p = strncopy(p, uucp, e - p);
405
p = strncopy(p, "!", e - p);
406
}
407
p = strncopy(p, user, e - p);
408
if (arpa) {
409
p = strncopy(p, "%", e - p);
410
p = strncopy(p, arpa, e - p);
411
}
412
if (inet) {
413
p = strncopy(p, "@", e - p);
414
p = strncopy(p, inet, e - p);
415
}
416
if (!streq(addr, norm))
417
return savestr(norm);
418
}
419
return buf ? (char*)0 : (type & GSTACK) ? savestr(addr) : addr;
420
}
421
422
/*
423
* Skin an arpa net address according to the RFC 822 interpretation
424
* of "host-phrase."
425
*/
426
char*
427
skin(char* name, unsigned long type)
428
{
429
register int c;
430
register char* cp;
431
register char* cp2;
432
char* bufend;
433
int gotlt;
434
int lastsp;
435
char buf[LINESIZE];
436
437
if (!name)
438
return 0;
439
if (type & (GMESSAGEID|GREFERENCES))
440
return savestr(name);
441
if (!strchr(name, '(') && !strchr(name, '<') && !strchr(name, ' '))
442
return normalize(name, type, NiL, 0);
443
gotlt = 0;
444
lastsp = 0;
445
bufend = buf;
446
for (cp = name, cp2 = bufend; c = *cp++; ) {
447
switch (c) {
448
case '(':
449
cp = skip_comment(cp);
450
lastsp = 0;
451
break;
452
453
case '"':
454
/*
455
* Start of a "quoted-string".
456
* Copy it in its entirety.
457
*/
458
while (c = *cp) {
459
cp++;
460
if (c == '"')
461
break;
462
if (c != '\\')
463
*cp2++ = c;
464
else if (c = *cp) {
465
*cp2++ = c;
466
cp++;
467
}
468
}
469
lastsp = 0;
470
break;
471
472
case ' ':
473
if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
474
cp += 3, *cp2++ = '@';
475
else if (cp[0] == '@' && cp[1] == ' ')
476
cp += 2, *cp2++ = '@';
477
else
478
lastsp = 1;
479
break;
480
481
case '<':
482
cp2 = bufend;
483
gotlt++;
484
lastsp = 0;
485
break;
486
487
case '>':
488
if (gotlt) {
489
gotlt = 0;
490
while ((c = *cp) && c != ',') {
491
cp++;
492
if (c == '(')
493
cp = skip_comment(cp);
494
else if (c == '"')
495
while (c = *cp) {
496
cp++;
497
if (c == '"')
498
break;
499
if (c == '\\' && *cp)
500
cp++;
501
}
502
}
503
lastsp = 0;
504
break;
505
}
506
/* Fall into . . . */
507
508
default:
509
if (lastsp) {
510
lastsp = 0;
511
*cp2++ = ' ';
512
}
513
*cp2++ = c;
514
if (c == ',' && !gotlt) {
515
*cp2++ = ' ';
516
for (; *cp == ' '; cp++)
517
;
518
lastsp = 0;
519
bufend = cp2;
520
}
521
}
522
}
523
*cp2 = 0;
524
return normalize(buf, type|GSTACK, NiL, 0);
525
}
526
527
/*
528
* Are any of the characters in the two strings the same?
529
*/
530
int
531
anyof(register char* s1, register char* s2)
532
{
533
534
while (*s1)
535
if (strchr(s2, *s1++))
536
return 1;
537
return 0;
538
}
539
540
/*
541
* Convert c to lower case
542
*/
543
int
544
lower(register int c)
545
{
546
return isupper(c) ? tolower(c) : c;
547
}
548
549
/*
550
* Convert c to upper case
551
*/
552
int
553
upper(register int c)
554
{
555
return islower(c) ? toupper(c) : c;
556
}
557
558
/*
559
* Convert s to lower case
560
*/
561
char*
562
strlower(register char* s)
563
{
564
register char* b = s;
565
register int c;
566
567
while (c = *s)
568
*s++ = isupper(c) ? tolower(c) : c;
569
return b;
570
}
571
572
/*
573
* 0 terminate tmp string stream, rewind, and return beginning of string
574
*/
575
char*
576
struse(Sfio_t* sp)
577
{
578
char* s;
579
580
if (!(s = sfstruse(sp)))
581
note(FATAL, "out of space");
582
return s;
583
}
584
585
/*
586
* See if the given header field is supposed to be ignored.
587
*/
588
int
589
ignored(Dt_t** ignore, const char* field)
590
{
591
struct name* tp;
592
593
if (ignore == &state.ignoreall)
594
return 1;
595
tp = dictsearch(ignore, field, LOOKUP);
596
if (*ignore && (dictflags(ignore) & RETAIN))
597
return !tp || !(tp->flags & RETAIN);
598
return tp && (tp->flags & IGNORE);
599
}
600
601
/*
602
* Allocate size more bytes of space and return the address of the
603
* first byte to the caller. An even number of bytes are always
604
* allocated so that the space will always be on a word boundary.
605
* The string spaces are of exponentially increasing size, to satisfy
606
* the occasional user with enormous string size requests.
607
*
608
* Strings handed out here are reclaimed at the top of the command
609
* loop each time, so they need not be freed.
610
*/
611
612
char*
613
salloc(register int size)
614
{
615
register char* t;
616
register struct strings* sp;
617
int index;
618
619
if (state.onstack <= 0) {
620
if (!(t = newof(0, char, size, 0)))
621
note(PANIC, "Out of space");
622
return t;
623
}
624
size += 7;
625
size &= ~7;
626
index = 0;
627
for (sp = &state.stringdope[0]; sp < &state.stringdope[elementsof(state.stringdope)]; sp++) {
628
if (!sp->s_topfree && (STRINGSIZE << index) >= size)
629
break;
630
if (sp->s_nleft >= size)
631
break;
632
index++;
633
}
634
if (sp >= &state.stringdope[elementsof(state.stringdope)])
635
note(PANIC, "String too large");
636
if (!sp->s_topfree) {
637
index = sp - &state.stringdope[0];
638
sp->s_topfree = (char*)malloc(STRINGSIZE << index);
639
if (!sp->s_topfree)
640
note(PANIC, "No room for dynamic string space %d", index);
641
sp->s_nextfree = sp->s_topfree;
642
sp->s_nleft = STRINGSIZE << index;
643
}
644
sp->s_nleft -= size;
645
t = sp->s_nextfree;
646
sp->s_nextfree += size;
647
return t;
648
}
649
650
/*
651
* Reset the string area to be empty.
652
* Called to free all strings allocated
653
* since last reset.
654
*/
655
void
656
sreset(void)
657
{
658
register struct strings* sp;
659
register int index;
660
661
if (state.noreset)
662
return;
663
index = 0;
664
for (sp = &state.stringdope[0]; sp < &state.stringdope[elementsof(state.stringdope)]; sp++) {
665
if (!sp->s_topfree)
666
continue;
667
sp->s_nextfree = sp->s_topfree;
668
sp->s_nleft = STRINGSIZE << index;
669
index++;
670
}
671
dictreset();
672
}
673
674
/*
675
* Return lines/chars for display.
676
*/
677
char*
678
counts(int wide, off_t lines, off_t chars)
679
{
680
sfsprintf(state.counts, sizeof(state.counts), wide ? "%5ld/%-7ld" : "%3ld/%-5ld", (long)lines, (long)chars);
681
return state.counts;
682
}
683
684
/*
685
* Check if s matches `all'.
686
*/
687
int
688
isall(register const char* s)
689
{
690
return s && (streq(s, "all") || streq(s, "*"));
691
}
692
693
/*
694
* Check if name is a pipe command.
695
*/
696
char*
697
iscmd(register char* s)
698
{
699
if (!s)
700
return 0;
701
while (isspace(*s))
702
s++;
703
if (*s != '!' && *s != '|')
704
return 0;
705
do {
706
if (!*++s)
707
return 0;
708
} while (isspace(*s));
709
return s;
710
}
711
712
/*
713
* Set/Clear message flags
714
*/
715
716
void
717
msgflags(register struct msg* mp, int set, int clr)
718
{
719
if (state.folder == FIMAP)
720
imap_msgflags(mp, set, clr);
721
else {
722
if (clr)
723
mp->m_flag &= ~clr;
724
if (set)
725
mp->m_flag |= set;
726
}
727
}
728
729
/*
730
* strncpy() with trailing nul, even if n==0
731
* pointer to the copied nul returned
732
*/
733
734
char*
735
strncopy(register char* t, register const char* f, size_t n)
736
{
737
register char* e = t + n - 1;
738
739
do
740
{
741
if (t >= e)
742
{
743
*t = 0;
744
return t;
745
}
746
} while (*t++ = *f++);
747
return t - 1;
748
}
749
750
/*
751
* quote s into sp according to sh syntax
752
*/
753
754
void
755
shquote(register Sfio_t* sp, char* s)
756
{
757
register char* t;
758
register char* b;
759
register int c;
760
register int q;
761
762
if (*s == '"' && *(s + strlen(s) - 1) == '"')
763
{
764
sfprintf(sp, "\\\"%s\\\"", s);
765
return;
766
}
767
q = 0;
768
b = 0;
769
for (t = s;;)
770
{
771
switch (c = *t++)
772
{
773
case 0:
774
break;
775
case '\n':
776
case ';':
777
case '&':
778
case '|':
779
case '<':
780
case '>':
781
case '(':
782
case ')':
783
case '[':
784
case ']':
785
case '{':
786
case '}':
787
case '*':
788
case '?':
789
case ' ':
790
case '\t':
791
case '\\':
792
q |= 4;
793
continue;
794
case '\'':
795
q |= 1;
796
if (q & 2)
797
break;
798
continue;
799
case '"':
800
case '$':
801
q |= 2;
802
if (q & 1)
803
break;
804
continue;
805
case '=':
806
if (!q && !b && *(b = t) == '=')
807
b++;
808
continue;
809
default:
810
continue;
811
}
812
break;
813
}
814
if (!q)
815
sfputr(sp, s, -1);
816
else if (!(q & 1))
817
{
818
if (b)
819
sfprintf(sp, "%-.*s'%s'", b - s, s, b);
820
else
821
sfprintf(sp, "'%s'", s);
822
}
823
else if (!(q & 2))
824
{
825
if (b)
826
sfprintf(sp, "%-.*s\"%s\"", b - s, s, b);
827
else
828
sfprintf(sp, "\"%s\"", s);
829
}
830
else
831
for (t = s;;)
832
switch (c = *t++)
833
{
834
case 0:
835
return;
836
case '\n':
837
sfputc(sp, '"');
838
sfputc(sp, c);
839
sfputc(sp, '"');
840
break;
841
case ';':
842
case '&':
843
case '|':
844
case '<':
845
case '>':
846
case '(':
847
case ')':
848
case '[':
849
case ']':
850
case '{':
851
case '}':
852
case '$':
853
case '*':
854
case '?':
855
case ' ':
856
case '\t':
857
case '\\':
858
case '\'':
859
case '"':
860
sfputc(sp, '\\');
861
/*FALLTHROUGH*/
862
default:
863
sfputc(sp, c);
864
break;
865
}
866
}
867
868