1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3 *
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)cmd2.c 2.46 (gritter) 3/4/06";
42 #endif
43 #endif /* not lint */
44
45 #include "rcv.h"
46 #include "extern.h"
47 #include <sys/wait.h>
48 #include <sys/stat.h>
49 #include <unistd.h>
50
51 /*
52 * Mail -- a mail program
53 *
54 * More user commands.
55 */
56
57 static int save1(char *str, int mark, char *cmd, struct ignoretab *ignore,
58 int convert, int sender_record, int domove);
59 static char *snarf(char *linebuf, int *flag, int usembox);
60 static int delm(int *msgvec);
61 static int ignore1(char **list, struct ignoretab *tab, char *which);
62 static int igshow(struct ignoretab *tab, char *which);
63 static int igcomp(const void *l, const void *r);
64 static void unignore_one(const char *name, struct ignoretab *tab);
65 static int unignore1(char **list, struct ignoretab *tab, char *which);
66
67 /*
68 * If any arguments were given, go to the next applicable argument
69 * following dot, otherwise, go to the next applicable message.
70 * If given as first command with no arguments, print first message.
71 */
72 int
next(void * v)73 next(void *v)
74 {
75 int *msgvec = v;
76 struct message *mp;
77 int *ip, *ip2;
78 int list[2], mdot;
79
80 if (*msgvec != 0) {
81
82 /*
83 * If some messages were supplied, find the
84 * first applicable one following dot using
85 * wrap around.
86 */
87
88 mdot = dot - &message[0] + 1;
89
90 /*
91 * Find the first message in the supplied
92 * message list which follows dot.
93 */
94
95 for (ip = msgvec; *ip != 0; ip++) {
96 #ifdef _CRAY
97 /*
98 * Work around an optimizer bug in Cray Standard C Version 4.0.3 (057126).
99 * Otherwise, SIGFPE is received when mb.mb_threaded != 0.
100 */
101 #pragma _CRI suppress ip
102 #endif /* _CRAY */
103 if (mb.mb_threaded ? message[*ip-1].m_threadpos >
104 dot->m_threadpos :
105 *ip > mdot)
106 break;
107 }
108 if (*ip == 0)
109 ip = msgvec;
110 ip2 = ip;
111 do {
112 mp = &message[*ip2 - 1];
113 if ((mp->m_flag & (MDELETED|MHIDDEN)) == 0) {
114 setdot(mp);
115 goto hitit;
116 }
117 if (*ip2 != 0)
118 ip2++;
119 if (*ip2 == 0)
120 ip2 = msgvec;
121 } while (ip2 != ip);
122 printf(catgets(catd, CATSET, 21, "No messages applicable\n"));
123 return(1);
124 }
125
126 /*
127 * If this is the first command, select message 1.
128 * Note that this must exist for us to get here at all.
129 */
130
131 if (!sawcom) {
132 if (msgCount == 0)
133 goto ateof;
134 goto hitit;
135 }
136
137 /*
138 * Just find the next good message after dot, no
139 * wraparound.
140 */
141
142 if (mb.mb_threaded == 0) {
143 for (mp = dot + did_print_dot; mp < &message[msgCount]; mp++)
144 if ((mp->m_flag & (MDELETED|MSAVED|MHIDDEN|MKILL)) == 0)
145 break;
146 } else {
147 mp = dot;
148 if (did_print_dot)
149 mp = next_in_thread(mp);
150 while (mp && mp->m_flag & (MDELETED|MSAVED|MHIDDEN|MKILL))
151 mp = next_in_thread(mp);
152 }
153 if (mp == NULL || mp >= &message[msgCount]) {
154 ateof:
155 printf(catgets(catd, CATSET, 22, "At EOF\n"));
156 return(0);
157 }
158 setdot(mp);
159 hitit:
160 /*
161 * Print dot.
162 */
163
164 list[0] = dot - &message[0] + 1;
165 list[1] = 0;
166 return(type(list));
167 }
168
169 /*
170 * Save a message in a file. Mark the message as saved
171 * so we can discard when the user quits.
172 */
173 int
save(void * v)174 save(void *v)
175 {
176 char *str = v;
177
178 return save1(str, 1, "save", saveignore, SEND_MBOX, 0, 0);
179 }
180
181 int
Save(void * v)182 Save(void *v)
183 {
184 char *str = v;
185
186 return save1(str, 1, "save", saveignore, SEND_MBOX, 1, 0);
187 }
188
189 /*
190 * Copy a message to a file without affected its saved-ness
191 */
192 int
copycmd(void * v)193 copycmd(void *v)
194 {
195 char *str = v;
196
197 return save1(str, 0, "copy", saveignore, SEND_MBOX, 0, 0);
198 }
199
200 int
Copycmd(void * v)201 Copycmd(void *v)
202 {
203 char *str = v;
204
205 return save1(str, 0, "copy", saveignore, SEND_MBOX, 1, 0);
206 }
207
208 /*
209 * Move a message to a file.
210 */
211 int
cmove(void * v)212 cmove(void *v)
213 {
214 char *str = v;
215
216 return save1(str, 0, "move", saveignore, SEND_MBOX, 0, 1);
217 }
218
219 int
cMove(void * v)220 cMove(void *v)
221 {
222 char *str = v;
223
224 return save1(str, 0, "move", saveignore, SEND_MBOX, 1, 1);
225 }
226
227 /*
228 * Decrypt and copy a message to a file.
229 */
230 int
cdecrypt(void * v)231 cdecrypt(void *v)
232 {
233 char *str = v;
234
235 return save1(str, 0, "decrypt", saveignore, SEND_DECRYPT, 0, 0);
236 }
237
238 int
cDecrypt(void * v)239 cDecrypt(void *v)
240 {
241 char *str = v;
242
243 return save1(str, 0, "decrypt", saveignore, SEND_DECRYPT, 1, 0);
244 }
245
246 /*
247 * Save/copy the indicated messages at the end of the passed file name.
248 * If mark is true, mark the message "saved."
249 */
250 static int
save1(char * str,int mark,char * cmd,struct ignoretab * ignore,int convert,int sender_record,int domove)251 save1(char *str, int mark, char *cmd, struct ignoretab *ignore,
252 int convert, int sender_record, int domove)
253 {
254 struct stat st;
255 int *ip;
256 struct message *mp;
257 char *file = NULL, *disp = "";
258 int f, *msgvec;
259 FILE *obuf;
260 int newfile = 0;
261 char *cp, *cq;
262 off_t mstats[2], tstats[2];
263 int compressed = 0;
264 enum protocol prot;
265 int success = 1, last = 0;
266
267 /*LINTED*/
268 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
269 if (sender_record) {
270 for (cp = str; *cp && blankchar(*cp & 0377); cp++);
271 f = (*cp != '\0');
272 } else {
273 if ((file = snarf(str, &f, convert != SEND_TOFILE)) == NULL)
274 return(1);
275 }
276 if (!f) {
277 *msgvec = first(0, MMNORM);
278 if (*msgvec == 0) {
279 if (inhook)
280 return 0;
281 printf(catgets(catd, CATSET, 23,
282 "No messages to %s.\n"), cmd);
283 return(1);
284 }
285 msgvec[1] = 0;
286 }
287 if (f && getmsglist(str, msgvec, 0) < 0)
288 return(1);
289 if (*msgvec == 0) {
290 if (inhook)
291 return 0;
292 printf("No applicable messages.\n");
293 return 1;
294 }
295 if (sender_record) {
296 if ((cp = nameof(&message[*msgvec - 1], 0)) == NULL) {
297 printf(catgets(catd, CATSET, 24,
298 "Cannot determine message sender to %s.\n"),
299 cmd);
300 return 1;
301 }
302 for (cq = cp; *cq && *cq != '@'; cq++);
303 *cq = '\0';
304 if (value("outfolder")) {
305 file = salloc(strlen(cp) + 2);
306 file[0] = '+';
307 strcpy(&file[1], cp);
308 } else
309 file = cp;
310 }
311 if ((file = expand(file)) == NULL)
312 return(1);
313 prot = which_protocol(file);
314 if (prot != PROTO_IMAP) {
315 if (access(file, 0) >= 0) {
316 newfile = 0;
317 disp = catgets(catd, CATSET, 25, "[Appended]");
318 } else {
319 newfile = 1;
320 disp = catgets(catd, CATSET, 26, "[New file]");
321 }
322 }
323 if ((obuf = convert == SEND_TOFILE ? Fopen(file, "a+") :
324 Zopen(file, "a+", &compressed)) == NULL) {
325 if ((obuf = convert == SEND_TOFILE ? Fopen(file, "wx") :
326 Zopen(file, "wx", &compressed)) == NULL) {
327 perror(file);
328 return(1);
329 }
330 } else {
331 if (compressed) {
332 newfile = 0;
333 disp = catgets(catd, CATSET, 25, "[Appended]");
334 }
335 if (!newfile && fstat(fileno(obuf), &st) &&
336 S_ISREG(st.st_mode) &&
337 fseek(obuf, -2L, SEEK_END) == 0) {
338 char buf[2];
339 int prependnl = 0;
340
341 switch (fread(buf, sizeof *buf, 2, obuf)) {
342 case 2:
343 if (buf[1] != '\n') {
344 prependnl = 1;
345 break;
346 }
347 /*FALLTHRU*/
348 case 1:
349 if (buf[0] != '\n')
350 prependnl = 1;
351 break;
352 default:
353 if (ferror(obuf)) {
354 perror(file);
355 return(1);
356 }
357 prependnl = 0;
358 }
359 fflush(obuf);
360 if (prependnl) {
361 putc('\n', obuf);
362 fflush(obuf);
363 }
364 }
365 }
366 tstats[0] = tstats[1] = 0;
367 imap_created_mailbox = 0;
368 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
369 mp = &message[*ip - 1];
370 if (prot == PROTO_IMAP &&
371 ignore[0].i_count == 0 &&
372 ignore[1].i_count == 0 &&
373 imap_thisaccount(file)) {
374 if (imap_copy(mp, *ip, file) == STOP)
375 goto ferr;
376 mstats[0] = -1;
377 mstats[1] = mp->m_xsize;
378 } else if (send(mp, obuf, ignore, NULL,
379 convert, mstats) < 0) {
380 perror(file);
381 goto ferr;
382 }
383 touch(mp);
384 if (mark)
385 mp->m_flag |= MSAVED;
386 if (domove) {
387 mp->m_flag |= MDELETED|MSAVED;
388 last = *ip;
389 }
390 tstats[0] += mstats[0];
391 tstats[1] += mstats[1];
392 }
393 fflush(obuf);
394 if (ferror(obuf)) {
395 perror(file);
396 ferr: success = 0;
397 }
398 if (Fclose(obuf) != 0)
399 success = 0;
400 if (success) {
401 if (prot == PROTO_IMAP || prot == PROTO_MAILDIR)
402 disp = prot == PROTO_IMAP && disconnected(file) ?
403 "[Queued]" : imap_created_mailbox ?
404 "[New file]" : "[Appended]";
405 printf("\"%s\" %s ", file, disp);
406 if (tstats[0] >= 0)
407 printf("%lu", (long)tstats[0]);
408 else
409 printf(catgets(catd, CATSET, 27, "binary"));
410 printf("/%lu\n", (long)tstats[1]);
411 } else if (mark) {
412 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
413 mp = &message[*ip - 1];
414 mp->m_flag &= ~MSAVED;
415 }
416 } else if (domove) {
417 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
418 mp = &message[*ip - 1];
419 mp->m_flag &= ~(MSAVED|MDELETED);
420 }
421 }
422 if (domove && last && success) {
423 setdot(&message[last-1]);
424 last = first(0, MDELETED);
425 setdot(&message[last ? last-1 : 0]);
426 }
427 return(success == 0);
428 }
429
430 /*
431 * Write the indicated messages at the end of the passed
432 * file name, minus header and trailing blank line.
433 * This is the MIME save function.
434 */
435 int
cwrite(void * v)436 cwrite(void *v)
437 {
438 char *str = v;
439
440 return save1(str, 0, "write", allignore, SEND_TOFILE, 0, 0);
441 }
442
443 /*
444 * Snarf the file from the end of the command line and
445 * return a pointer to it. If there is no file attached,
446 * return the mbox file. Put a null in front of the file
447 * name so that the message list processing won't see it,
448 * unless the file name is the only thing on the line, in
449 * which case, return 0 in the reference flag variable.
450 */
451
452 static char *
snarf(char * linebuf,int * flag,int usembox)453 snarf(char *linebuf, int *flag, int usembox)
454 {
455 char *cp;
456
457 *flag = 1;
458 if ((cp = laststring(linebuf, flag, 0)) == NULL) {
459 if (usembox) {
460 *flag = 0;
461 return expand("&");
462 } else {
463 printf(catgets(catd, CATSET, 28,
464 "No file specified.\n"));
465 return NULL;
466 }
467 }
468 return(cp);
469 }
470
471 /*
472 * Delete messages.
473 */
474 int
delete(void * v)475 delete(void *v)
476 {
477 int *msgvec = v;
478 delm(msgvec);
479 return 0;
480 }
481
482 /*
483 * Delete messages, then type the new dot.
484 */
485 int
deltype(void * v)486 deltype(void *v)
487 {
488 int *msgvec = v;
489 int list[2];
490 int lastdot;
491
492 lastdot = dot - &message[0] + 1;
493 if (delm(msgvec) >= 0) {
494 list[0] = dot - &message[0] + 1;
495 if (list[0] > lastdot) {
496 touch(dot);
497 list[1] = 0;
498 return(type(list));
499 }
500 printf(catgets(catd, CATSET, 29, "At EOF\n"));
501 } else
502 printf(catgets(catd, CATSET, 30, "No more messages\n"));
503 return(0);
504 }
505
506 /*
507 * Delete the indicated messages.
508 * Set dot to some nice place afterwards.
509 * Internal interface.
510 */
511 static int
delm(int * msgvec)512 delm(int *msgvec)
513 {
514 struct message *mp;
515 int *ip;
516 int last;
517
518 last = 0;
519 for (ip = msgvec; *ip != 0; ip++) {
520 mp = &message[*ip - 1];
521 touch(mp);
522 mp->m_flag |= MDELETED|MTOUCH;
523 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
524 last = *ip;
525 }
526 if (last != 0) {
527 setdot(&message[last-1]);
528 last = first(0, MDELETED);
529 if (last != 0) {
530 setdot(&message[last-1]);
531 return(0);
532 }
533 else {
534 setdot(&message[0]);
535 return(-1);
536 }
537 }
538
539 /*
540 * Following can't happen -- it keeps lint happy
541 */
542
543 return(-1);
544 }
545
546 /*
547 * Undelete the indicated messages.
548 */
549 int
undeletecmd(void * v)550 undeletecmd(void *v)
551 {
552 int *msgvec = v;
553 struct message *mp;
554 int *ip;
555
556 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
557 mp = &message[*ip - 1];
558 touch(mp);
559 setdot(mp);
560 if (mp->m_flag & (MDELETED|MSAVED))
561 mp->m_flag &= ~(MDELETED|MSAVED);
562 else
563 mp->m_flag &= ~MDELETED;
564 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
565 imap_undelete(mp, *ip);
566 }
567 return 0;
568 }
569
570 #ifdef DEBUG_COMMANDS
571 /*
572 * Interactively dump core on "core"
573 */
574 /*ARGSUSED*/
575 int
core(void * v)576 core(void *v)
577 {
578 int pid;
579 #ifdef WCOREDUMP
580 extern int wait_status;
581 #endif
582
583 switch (pid = fork()) {
584 case -1:
585 perror("fork");
586 return(1);
587 case 0:
588 abort();
589 _exit(1);
590 }
591 printf(catgets(catd, CATSET, 31, "Okie dokie"));
592 fflush(stdout);
593 wait_child(pid);
594 #ifdef WCOREDUMP
595 if (WCOREDUMP(wait_status))
596 printf(catgets(catd, CATSET, 32, " -- Core dumped.\n"));
597 else
598 printf(catgets(catd, CATSET, 33, " -- Can't dump core.\n"));
599 #endif
600 return 0;
601 }
602
603 /*
604 * Clobber as many bytes of stack as the user requests.
605 */
606 int
clobber(void * v)607 clobber(void *v)
608 {
609 char **argv = v;
610 int times;
611
612 if (argv[0] == 0)
613 times = 1;
614 else
615 times = (atoi(argv[0]) + 511) / 512;
616 clob1(times);
617 return 0;
618 }
619
620 /*
621 * Clobber the stack.
622 */
623 static void
clob1(int n)624 clob1(int n)
625 {
626 char buf[512];
627 char *cp;
628
629 if (n <= 0)
630 return;
631 for (cp = buf; cp < &buf[512]; *cp++ = (char)0xFF)
632 ;
633 clob1(n - 1);
634 }
635 #endif /* DEBUG_COMMANDS */
636
637 /*
638 * Add the given header fields to the retained list.
639 * If no arguments, print the current list of retained fields.
640 */
641 int
retfield(void * v)642 retfield(void *v)
643 {
644 char **list = v;
645
646 return ignore1(list, ignore + 1, "retained");
647 }
648
649 /*
650 * Add the given header fields to the ignored list.
651 * If no arguments, print the current list of ignored fields.
652 */
653 int
igfield(void * v)654 igfield(void *v)
655 {
656 char **list = v;
657
658 return ignore1(list, ignore, "ignored");
659 }
660
661 int
saveretfield(void * v)662 saveretfield(void *v)
663 {
664 char **list = v;
665
666 return ignore1(list, saveignore + 1, "retained");
667 }
668
669 int
saveigfield(void * v)670 saveigfield(void *v)
671 {
672 char **list = v;
673
674 return ignore1(list, saveignore, "ignored");
675 }
676
677 int
fwdretfield(void * v)678 fwdretfield(void *v)
679 {
680 char **list = v;
681
682 return ignore1(list, fwdignore + 1, "retained");
683 }
684
685 int
fwdigfield(void * v)686 fwdigfield(void *v)
687 {
688 char **list = v;
689
690 return ignore1(list, fwdignore, "ignored");
691 }
692
693 static int
ignore1(char ** list,struct ignoretab * tab,char * which)694 ignore1(char **list, struct ignoretab *tab, char *which)
695 {
696 int h;
697 struct ignore *igp;
698 char **ap;
699
700 if (*list == NULL)
701 return igshow(tab, which);
702 for (ap = list; *ap != 0; ap++) {
703 char *field;
704 size_t sz;
705
706 sz = strlen(*ap);
707 field = ac_alloc(sz + 1);
708 i_strcpy(field, *ap, sz + 1);
709 field[sz]='\0';
710 if (member(field, tab)) {
711 ac_free(field);
712 continue;
713 }
714 h = hash(field);
715 igp = (struct ignore *)scalloc(1, sizeof (struct ignore));
716 igp->i_field = smalloc(strlen(field) + 1);
717 strcpy(igp->i_field, field);
718 igp->i_link = tab->i_head[h];
719 tab->i_head[h] = igp;
720 tab->i_count++;
721 ac_free(field);
722 }
723 return 0;
724 }
725
726 /*
727 * Print out all currently retained fields.
728 */
729 static int
igshow(struct ignoretab * tab,char * which)730 igshow(struct ignoretab *tab, char *which)
731 {
732 int h;
733 struct ignore *igp;
734 char **ap, **ring;
735
736 if (tab->i_count == 0) {
737 printf(catgets(catd, CATSET, 34,
738 "No fields currently being %s.\n"), which);
739 return 0;
740 }
741 /*LINTED*/
742 ring = (char **)salloc((tab->i_count + 1) * sizeof (char *));
743 ap = ring;
744 for (h = 0; h < HSHSIZE; h++)
745 for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
746 *ap++ = igp->i_field;
747 *ap = 0;
748 qsort(ring, tab->i_count, sizeof (char *), igcomp);
749 for (ap = ring; *ap != 0; ap++)
750 printf("%s\n", *ap);
751 return 0;
752 }
753
754 /*
755 * Compare two names for sorting ignored field list.
756 */
757 static int
igcomp(const void * l,const void * r)758 igcomp(const void *l, const void *r)
759 {
760 return (strcmp(*(char **)l, *(char **)r));
761 }
762
763 int
unignore(void * v)764 unignore(void *v)
765 {
766 return unignore1((char **)v, ignore, "ignored");
767 }
768
769 int
unretain(void * v)770 unretain(void *v)
771 {
772 return unignore1((char **)v, ignore + 1, "retained");
773 }
774
775 int
unsaveignore(void * v)776 unsaveignore(void *v)
777 {
778 return unignore1((char **)v, saveignore, "ignored");
779 }
780
781 int
unsaveretain(void * v)782 unsaveretain(void *v)
783 {
784 return unignore1((char **)v, saveignore + 1, "retained");
785 }
786
787 int
unfwdignore(void * v)788 unfwdignore(void *v)
789 {
790 return unignore1((char **)v, fwdignore, "ignored");
791 }
792
793 int
unfwdretain(void * v)794 unfwdretain(void *v)
795 {
796 return unignore1((char **)v, fwdignore + 1, "retained");
797 }
798
799 static void
unignore_one(const char * name,struct ignoretab * tab)800 unignore_one(const char *name, struct ignoretab *tab)
801 {
802 struct ignore *ip, *iq = NULL;
803 int h = hash(name);
804
805 for (ip = tab->i_head[h]; ip; ip = ip->i_link) {
806 if (asccasecmp(ip->i_field, name) == 0) {
807 free(ip->i_field);
808 if (iq != NULL)
809 iq->i_link = ip->i_link;
810 else
811 tab->i_head[h] = ip->i_link;
812 free(ip);
813 tab->i_count--;
814 break;
815 }
816 iq = ip;
817 }
818 }
819
820 static int
unignore1(char ** list,struct ignoretab * tab,char * which)821 unignore1(char **list, struct ignoretab *tab, char *which)
822 {
823 if (tab->i_count == 0) {
824 printf(catgets(catd, CATSET, 34,
825 "No fields currently being %s.\n"), which);
826 return 0;
827 }
828 while (*list)
829 unignore_one(*list++, tab);
830 return 0;
831 }
832