Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/vars.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2012 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
* Variable handling stuff.
73
*/
74
75
#include "mailx.h"
76
77
/*
78
* Initialize the variables before command line options and rc sourcing.
79
*/
80
81
void
82
varinit(void)
83
{
84
register const struct var* vp;
85
register int c;
86
register char* s;
87
register char* t;
88
register FILE* fp;
89
int i;
90
char buf[LINESIZE];
91
92
static const char* domains[] = { _PATH_RESCONF };
93
94
state.version = fmtident(state.version);
95
setscreensize();
96
for (vp = state.vartab; vp->name; vp++) {
97
if ((vp->flags & E) && (s = getenv(vp->name)) && *s)
98
varset(vp->name, s);
99
else if (vp->initialize)
100
varset(vp->name, vp->initialize);
101
}
102
103
/*
104
* Get the local host name.
105
*/
106
107
#if _lib_gethostname
108
if (!gethostname(buf, sizeof(buf)))
109
state.var.hostname = varkeep(buf);
110
#endif
111
112
/*
113
* Get the local domain name.
114
*/
115
116
if ((s = strchr(state.var.hostname, '.')) && *++s)
117
state.var.domain = varkeep(s);
118
else
119
for (i = 0; i < sizeof(domains) / sizeof(domains[0]); i++) {
120
s = (char*)domains[i];
121
do {
122
if (fp = fileopen(s, "r")) {
123
while (s = fgets(buf, sizeof(buf), fp)) {
124
while (isspace(*s))
125
s++;
126
if (((c = *s++) == 'd' || c == 'D') &&
127
((c = *s++) == 'o' || c == 'O') &&
128
((c = *s++) == 'm' || c == 'M') &&
129
((c = *s++) == 'a' || c == 'A') &&
130
((c = *s++) == 'i' || c == 'M') &&
131
((c = *s++) == 'n' || c == 'N') &&
132
(isspace(c = *s++) || c == ':' || c == '=')) {
133
while (isspace(*s))
134
s++;
135
t = s;
136
while (*t && !isspace(*t))
137
t++;
138
while (t > s && *(t - 1) == '.')
139
t--;
140
if (*t)
141
*t = 0;
142
if (*s)
143
{
144
state.var.domain = varkeep(s);
145
break;
146
}
147
}
148
}
149
fileclose(fp);
150
i = sizeof(domains) / sizeof(domains[0]);
151
break;
152
}
153
} while (!i && (s = strchr(s + 1, '/')));
154
}
155
156
/*
157
* Interactive.
158
*/
159
160
if (isatty(0))
161
state.var.interactive = state.on;
162
}
163
164
/*
165
* Copy a variable value into permanent (ie, not collected after each
166
* command) space. Do not bother to alloc space for "".
167
*/
168
169
char*
170
varkeep(const char* val)
171
{
172
char* p;
173
int len;
174
175
if (!val)
176
return 0;
177
if (!*val)
178
return state.on;
179
len = strlen(val) + 1;
180
if (!(p = (char*)malloc(len)))
181
note(PANIC, "Out of space");
182
memcpy(p, val, len);
183
stresc(p);
184
return p;
185
}
186
187
/*
188
* List variable settings.
189
*/
190
191
int
192
varlist(int all)
193
{
194
register const struct var* vp;
195
196
for (vp = state.vartab; vp->name; vp++)
197
if (*vp->variable) {
198
if (all > 0 || !all && !(vp->flags & A)) {
199
printf("%16s", vp->name);
200
if (vp->flags & I)
201
printf("=%ld\n", *((long*)vp->variable));
202
else if (**vp->variable)
203
printf("=\"%s\"\n", fmtesc(*vp->variable));
204
else
205
putchar('\n');
206
}
207
}
208
else if (all) {
209
sfprintf(state.path.temp, "no%s", vp->name);
210
printf("%16s\n", struse(state.path.temp));
211
}
212
return 0;
213
}
214
215
/*
216
* Set a variable value.
217
*/
218
219
int
220
varset(register const char* name, register const char* value)
221
{
222
register struct var* vp;
223
char* s;
224
int n;
225
int m;
226
227
note(DEBUG, "set name=%s value=%s", name, value);
228
if (name[0] == '-') {
229
name += 1;
230
value = 0;
231
}
232
else if (name[0] == 'n' && name[1] == 'o') {
233
name += 2;
234
value = 0;
235
}
236
for (;;) {
237
if (!(vp = (struct var*)strsearch(state.vartab, state.varnum, sizeof(struct var), stracmp, name, NiL))) {
238
if (state.sourcing)
239
return 0;
240
note(0, "\"%s\": unknown variable", name);
241
return 1;
242
}
243
if (!(vp->flags & A))
244
break;
245
name = (const char*)vp->help;
246
}
247
if ((vp->flags & C) && !state.cmdline) {
248
note(0, "\"%s\": can only set variable on command line", name);
249
return 1;
250
}
251
if ((vp->flags & S) && state.sourcing) {
252
note(0, "\"%s\": cannot set variable from script", name);
253
return 1;
254
}
255
if (vp->flags & R) {
256
note(0, "\"%s\": readonly variable", name);
257
return 1;
258
}
259
else if (vp->flags & I) {
260
if (!value)
261
*((long*)vp->variable) = 0;
262
else if (isdigit(*value) || *value == '-' || *value == '+')
263
*((long*)vp->variable) = strtol(value, NiL, 0);
264
}
265
else {
266
if (value && (*value || !(vp->flags & N))) {
267
if ((vp->flags & L) && *value && *vp->variable && **vp->variable) {
268
if (state.cmdline)
269
return 0;
270
m = strlen(*vp->variable);
271
n = m + strlen(value) + 2;
272
if (!(s = newof(0, char, n, 0)))
273
note(PANIC, "Out of space");
274
memcpy(s, *vp->variable, m);
275
s[m] = '\n';
276
strcpy(s + m + 1, value);
277
value = (const char*)s;
278
}
279
else if (value != (char*)vp->initialize)
280
value = varkeep(value);
281
}
282
else if (vp->flags & D)
283
value = vp->initialize;
284
else
285
value = 0;
286
if (*vp->variable && *vp->variable != state.on && *vp->variable != (char*)vp->initialize)
287
free(*vp->variable);
288
*vp->variable = (char*)value;
289
}
290
if (vp->set)
291
(*vp->set)(vp, value);
292
return 0;
293
}
294
295
/*
296
* Get a variable value.
297
*/
298
299
char*
300
varget(register const char* name)
301
{
302
register struct var* vp;
303
char* s;
304
305
for (;;) {
306
if (!(vp = (struct var*)strsearch(state.vartab, state.varnum, sizeof(struct var), stracmp, name, NiL))) {
307
if (s = getenv(name))
308
return s;
309
if (!state.sourcing)
310
note(0, "\"%s\": unknown variable", name);
311
return 0;
312
}
313
if (!(vp->flags & A))
314
break;
315
name = (const char*)vp->help;
316
}
317
if (vp->flags & I) {
318
sfsprintf(state.number, sizeof(state.number), "%ld", *((long*)vp->variable));
319
return state.number;
320
}
321
return *vp->variable;
322
}
323
324
/*
325
* Trap unimplemented variable assignment.
326
*/
327
328
void
329
set_notyet(struct var* vp, const char* value)
330
{
331
if (value) {
332
note(0, "\"%s\": variable not implemented yet", vp->name);
333
*vp->variable = 0;
334
}
335
}
336
337
/*
338
* Trap askbcc variable assignment.
339
*/
340
341
void
342
set_askbcc(struct var* vp, const char* value)
343
{
344
if (value)
345
state.askheaders |= GBCC;
346
else
347
state.askheaders &= ~GBCC;
348
}
349
350
/*
351
* Trap askcc variable assignment.
352
*/
353
354
void
355
set_askcc(struct var* vp, const char* value)
356
{
357
if (value)
358
state.askheaders |= GCC;
359
else
360
state.askheaders &= ~GCC;
361
}
362
363
/*
364
* Low level for askheaders and editheaders.
365
*/
366
367
static unsigned long
368
setheaders(struct var* vp, const char* value)
369
{
370
register char* s;
371
register const char* t;
372
register char* b;
373
char* p;
374
register const struct lab* lp;
375
unsigned long flags;
376
377
flags = GTO;
378
p = (char*)value;
379
while (b = wordnext(&p, state.path.path))
380
for (lp = state.hdrtab;; lp++) {
381
if (!(t = lp->name)) {
382
note(0, "\"%s %s\": unknown header field", vp->name, b);
383
break;
384
}
385
s = b;
386
if (upper(*s) == *t) {
387
while (lower(*++s) == *++t);
388
if (!*s && *t == ':') {
389
flags |= lp->type;
390
break;
391
}
392
}
393
}
394
return flags;
395
}
396
397
/*
398
* Trap askheaders variable assignment.
399
*/
400
401
void
402
set_askheaders(struct var* vp, const char* value)
403
{
404
state.askheaders = setheaders(vp, value);
405
}
406
407
/*
408
* Trap asksub variable assignment.
409
*/
410
411
void
412
set_asksub(struct var* vp, const char* value)
413
{
414
if (value)
415
state.askheaders |= GSUB;
416
else
417
state.askheaders &= ~GSUB;
418
}
419
420
/*
421
* Trap coprocess variable assignment.
422
*/
423
424
void
425
set_coprocess(struct var* vp, const char* value)
426
{
427
if (value) {
428
if (!*value)
429
state.var.coprocess = varkeep("From @");
430
state.var.crt = 0;
431
state.var.interactive = state.on;
432
state.var.header = 0;
433
state.var.more = 0;
434
}
435
}
436
437
/*
438
* Trap crt variable assignment.
439
*/
440
441
void
442
set_crt(struct var* vp, const char* value)
443
{
444
if (value && !*value)
445
state.var.crt = state.realscreenheight;
446
}
447
448
/*
449
* Trap editheaders variable assignment.
450
*/
451
452
void
453
set_editheaders(struct var* vp, const char* value)
454
{
455
state.editheaders = setheaders(vp, value);
456
}
457
458
/*
459
* Trap justfrom variable assignment.
460
*/
461
462
void
463
set_justfrom(struct var* vp, const char* value)
464
{
465
if (value) {
466
state.var.justheaders = state.var.justfrom ? "" : (char*)0;
467
state.var.interactive = 0;
468
state.var.screen = 0;
469
}
470
}
471
472
/*
473
* Trap list variable assignment.
474
*/
475
476
void
477
set_list(struct var* vp, const char* value)
478
{
479
register char* s;
480
481
*vp->variable = s = varkeep(value);
482
while (s = strchr(s, ' '))
483
*s++ = ',';
484
}
485
486
/*
487
* Trap mail variable assignment.
488
*/
489
490
void
491
set_mail(struct var* vp, const char* value)
492
{
493
state.var.mail = varkeep(mailbox(state.var.user, value));
494
}
495
496
/*
497
* Trap mailcap variable assignment.
498
*/
499
500
void
501
set_mailcap(struct var* vp, const char* value)
502
{
503
state.part.init = (value && *value) ? 0 : -1;
504
}
505
506
/*
507
* Trap news variable assignment.
508
*/
509
510
void
511
set_news(struct var* vp, const char* value)
512
{
513
if (value && *value && close(open(state.var.news, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY|O_cloexec, MAILMODE)))
514
note(FATAL|SYSTEM, "\"%s\"", state.var.news);
515
}
516
517
/*
518
* Trap screen variable assignment.
519
*/
520
521
void
522
set_pwd(struct var* vp, const char* value)
523
{
524
if (value && !*value) {
525
if (!getcwd(state.path.pwd[0], sizeof(state.path.pwd[0])))
526
strcpy(state.path.pwd[0], ".");
527
state.var.pwd = state.path.pwd[0];
528
state.var.oldpwd = state.path.pwd[1];
529
}
530
}
531
532
/*
533
* Trap sender variable assignment.
534
*/
535
536
void
537
set_sender(struct var* vp, const char* value)
538
{
539
struct sender* sp;
540
struct sendor* op;
541
struct sendand* ap;
542
char* v;
543
int c;
544
int d;
545
int n;
546
547
if (value && *value) {
548
if (!(sp = newof(0, struct sender, 1, strlen(value))))
549
note(PANIC, "Out of space");
550
v = sp->address;
551
d = *value++;
552
while ((c = *value++) && c != d)
553
*v++ = c;
554
*v++ = 0;
555
op = &sp->sendor;
556
ap = &op->sendand;
557
n = 1;
558
value--;
559
while (c = *value++) {
560
if (c == d) {
561
*v++ = 0;
562
if (n == 1) {
563
n = 2;
564
ap->head = v;
565
}
566
else if (n == 2) {
567
n = 3;
568
ap->pattern = v;
569
if (!strcasecmp(ap->head, "received"))
570
ap->flags |= GLAST;
571
}
572
else if (!(c = *value++) || *value != d)
573
break;
574
else if (c == '|') {
575
if (!(op->next = newof(0, struct sendor, 1, 0)))
576
note(PANIC, "Out of space");
577
op = op->next;
578
ap = &op->sendand;
579
n = 1;
580
}
581
else if (c == '&') {
582
if (!(ap->next = newof(0, struct sendand, 1, 0)))
583
note(PANIC, "Out of space");
584
ap = ap->next;
585
n = 1;
586
}
587
else
588
break;
589
}
590
else
591
*v++ = c;
592
}
593
if (c)
594
note(ERROR, "sender match operand syntax error: '%s' not expected", value - 1);
595
else {
596
sp->next = state.sender;
597
state.sender = sp;
598
}
599
}
600
else
601
state.sender = 0;
602
}
603
604
/*
605
* Trap screen variable assignment.
606
*/
607
608
void
609
set_screen(struct var* vp, const char* value)
610
{
611
if (value && !*value)
612
state.var.screen = state.screenheight > 4 ? (state.screenheight - 4) : state.screenheight;
613
}
614
615
/*
616
* Trap sendmail variable assignment.
617
*/
618
619
void
620
set_sendmail(struct var* vp, const char* value)
621
{
622
if (value && !*value) {
623
sfprintf(state.path.temp, "%s -oi", _PATH_SENDMAIL);
624
state.var.sendmail = varkeep(struse(state.path.temp));
625
}
626
}
627
628
/*
629
* Trap shell variable assignment.
630
*/
631
632
void
633
set_shell(struct var* vp, const char* value)
634
{
635
if (!value || !*value)
636
state.var.shell = varkeep(pathshell());
637
}
638
639
/*
640
* Trap spambody variable assignment.
641
*/
642
643
void
644
set_spambody(struct var* vp, const char* value)
645
{
646
register char* s;
647
register char* t;
648
register int n;
649
register struct linematch* mp;
650
register struct match* xp;
651
652
if (!(mp = state.bodymatch)) {
653
if (!(mp = newof(0, struct linematch, 1, 0)))
654
note(PANIC, "Out of space");
655
state.bodymatch = mp;
656
}
657
s = (char*)value;
658
for (;;) {
659
if (t = strchr(s, '|'))
660
n = t - s;
661
else
662
n = strlen(s);
663
if (n) {
664
if (mp->minline < n)
665
mp->minline = n;
666
if (!(xp = newof(0, struct match, 1, n)))
667
note(PANIC, "Out of space");
668
memcpy(xp->string, s, n);
669
xp->length = n;
670
mp->beg[xp->beg = s[0]] = 1;
671
mp->mid[xp->mid = s[n/2]] = 1;
672
mp->end[xp->end = s[n-1]] = 1;
673
if (mp->last)
674
mp->last = mp->last->next = xp;
675
else
676
mp->match = mp->last = xp;
677
}
678
if (!t)
679
break;
680
s = t + 1;
681
}
682
}
683
684
/*
685
* Trap toplines variable assignment.
686
*/
687
688
void
689
set_toplines(struct var* vp, const char* value)
690
{
691
if (state.var.toplines < 0 || state.var.toplines >= 1000)
692
state.var.toplines = 5;
693
}
694
695
/*
696
* Trap trace variable assignment.
697
*/
698
699
void
700
set_trace(struct var* vp, const char* value)
701
{
702
register int c;
703
register const char* s;
704
705
state.trace = 0;
706
if (s = value)
707
while (c = *s++)
708
TRACE(c);
709
}
710
711
/*
712
* Trap user variable assignment.
713
*/
714
715
void
716
set_user(struct var* vp, const char* value)
717
{
718
if (!value || !*value)
719
state.var.user = varkeep(username());
720
else if (userid(state.var.user) < 0)
721
note(FATAL|IDENTIFY, "\"%s\": unknown user", state.var.user);
722
}
723
724
/*
725
* struct name case insensitive comparf
726
*/
727
728
static int
729
ignorecase(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
730
{
731
return strcasecmp(a, b);
732
}
733
734
/*
735
* struct name freef
736
*/
737
738
static void
739
drop(Dt_t* dt, void* obj, Dtdisc_t* disc)
740
{
741
register struct name* np = (struct name*)obj;
742
register struct list* ap;
743
register struct list* bp;
744
745
if (np->value) {
746
if (!(np->flags & (GALIAS|GALTERNATE)))
747
free(np->value);
748
else {
749
ap = (struct list*)np->value;
750
do {
751
bp = ap->next;
752
free(ap);
753
} while (ap = bp);
754
}
755
}
756
free(np);
757
}
758
759
/*
760
* Find/insert name in dict.
761
* The dict*() cdt wrappers create dictionaries as needed.
762
*
763
* op: LOOKUP lookup
764
* CREATE INSERT, return 0 if already there
765
* IGNORECASE case insensitive
766
* INSERT insert
767
* DELETE delete
768
* OBJECT name is punned object
769
* STACK object on stack
770
*/
771
772
struct name*
773
dictsearch(Dt_t** dp, const char* name, register int op)
774
{
775
register Dt_t* dt;
776
register struct dict* dict;
777
register struct name* np;
778
register struct name* xp;
779
struct name* object;
780
781
if (!(dt = *dp)) {
782
if (!(op & (CREATE|INSERT)))
783
return 0;
784
if (!(dict = (op & STACK) ? (struct dict*)salloc(sizeof(struct dict)) : newof(0, struct dict, 1, 0)))
785
note(PANIC, "Out of space");
786
memset(dict, 0, sizeof(*dict));
787
dict->disc.key = offsetof(struct name, name);
788
if (op & IGNORECASE)
789
dict->disc.comparf = ignorecase;
790
if (!(op & STACK))
791
dict->disc.freef = drop;
792
if (!(dt = dtopen(&dict->disc, Dtoset)))
793
note(PANIC, "Out of space");
794
if ((op & STACK) && state.onstack > 0) {
795
dict->next = state.stacked;
796
state.stacked = dt;
797
}
798
*dp = dt;
799
}
800
if (op & OBJECT) {
801
object = (struct name*)name;
802
name = (const char*)object->name;
803
}
804
else object = 0;
805
if (!(np = (struct name*)dtmatch(dt, name))) {
806
if (!(op & (CREATE|INSERT)))
807
return 0;
808
if (!(np = object) || (op & COPY)) {
809
if (!(xp = (op & STACK) ? (struct name*)salloc(sizeof(struct name) + strlen(name) + 1) : newof(0, struct name, 1, strlen(name) + 1)))
810
note(PANIC, "Out of space");
811
strcpy(xp->name, name);
812
xp->value = np ? np->value : 0;
813
xp->flags = np ? np->flags : 0;
814
np = xp;
815
}
816
np = (struct name*)dtinsert(dt, np);
817
}
818
else if (op & DELETE) {
819
dtdelete(dt, np);
820
np = 0;
821
}
822
else if (op & CREATE)
823
np = 0;
824
return np;
825
}
826
827
/*
828
* Apply walkf(node,handle) to each node in the dictionary.
829
* Non-zero return from walkf terminates walk with that value.
830
*/
831
832
int
833
dictwalk(Dt_t** dp, int (*walkf)(Dt_t*, void*, void*), void* context)
834
{
835
return *dp ? dtwalk(*dp, walkf, context) : 0;
836
}
837
838
/*
839
* Drop STACK dictionaries.
840
*/
841
842
void
843
dictreset(void)
844
{
845
register Dt_t* dt;
846
847
while (dt = state.stacked) {
848
state.stacked = ((struct dict*)dt->disc)->next;
849
dtclose(dt);
850
}
851
}
852
853
/*
854
* Drop all entries from the dictionary.
855
*/
856
857
void
858
dictclear(Dt_t** dp)
859
{
860
if (*dp)
861
dtclear(*dp);
862
}
863
864