xref: /illumos-gate/usr/src/cmd/mailx/cmd2.c (revision 6c83d09f819e9de7126c8539546eca2e276df44a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California
34  * All Rights Reserved
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
42 
43 #include "rcv.h"
44 #include <locale.h>
45 
46 /*
47  * mailx -- a modified version of a University of California at Berkeley
48  *	mail program
49  *
50  * More user commands.
51  */
52 
53 static int	igshow(void);
54 static int	igcomp(const void *l, const void *r);
55 static int	save1(char str[], int mark);
56 static int	Save1(int *msgvec, int mark);
57 static void	savemsglist(char *file, int *msgvec, int flag);
58 static int	put1(char str[], int doign);
59 static int	svputs(const char *line, FILE *obuf);
60 static int	wrputs(const char *line, FILE *obuf);
61 static int	retshow(void);
62 
63 /* flags for savemsglist() */
64 #define	S_MARK		1		/* mark the message as saved */
65 #define	S_NOHEADER	2		/* don't write out the header */
66 #define	S_SAVING	4		/* doing save/copy */
67 #define	S_NOIGNORE	8		/* don't do ignore processing */
68 
69 /*
70  * If any arguments were given, go to the next applicable argument
71  * following dot, otherwise, go to the next applicable message.
72  * If given as first command with no arguments, print first message.
73  */
74 
75 int
76 next(int *msgvec)
77 {
78 	register struct message *mp;
79 	register int *ip, *ip2;
80 	int list[2], mdot;
81 
82 	if (*msgvec != NULL) {
83 
84 		/*
85 		 * If some messages were supplied, find the
86 		 * first applicable one following dot using
87 		 * wrap around.
88 		 */
89 
90 		mdot = dot - &message[0] + 1;
91 
92 		/*
93 		 * Find the first message in the supplied
94 		 * message list which follows dot.
95 		 */
96 
97 		for (ip = msgvec; *ip != NULL; ip++)
98 			if (*ip > mdot)
99 				break;
100 		if (*ip == NULL)
101 			ip = msgvec;
102 		ip2 = ip;
103 		do {
104 			mp = &message[*ip2 - 1];
105 			if ((mp->m_flag & MDELETED) == 0) {
106 				dot = mp;
107 				goto hitit;
108 			}
109 			if (*ip2 != NULL)
110 				ip2++;
111 			if (*ip2 == NULL)
112 				ip2 = msgvec;
113 		} while (ip2 != ip);
114 		printf(gettext("No messages applicable\n"));
115 		return(1);
116 	}
117 
118 	/*
119 	 * If this is the first command, select message 1.
120 	 * Note that this must exist for us to get here at all.
121 	 */
122 
123 	if (!sawcom)
124 		goto hitit;
125 
126 	/*
127 	 * Just find the next good message after dot, no
128 	 * wraparound.
129 	 */
130 
131 	for (mp = dot+1; mp < &message[msgCount]; mp++)
132 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
133 			break;
134 	if (mp >= &message[msgCount]) {
135 		printf(gettext("At EOF\n"));
136 		return(0);
137 	}
138 	dot = mp;
139 hitit:
140 	/*
141 	 * Print dot.
142 	 */
143 
144 	list[0] = dot - &message[0] + 1;
145 	list[1] = NULL;
146 	return(type(list));
147 }
148 
149 /*
150  * Save a message in a file.  Mark the message as saved
151  * so we can discard when the user quits.
152  */
153 int
154 save(char str[])
155 {
156 	return(save1(str, S_MARK));
157 }
158 
159 /*
160  * Copy a message to a file without affected its saved-ness
161  */
162 int
163 copycmd(char str[])
164 {
165 	return(save1(str, 0));
166 }
167 
168 /*
169  * Save/copy the indicated messages at the end of the passed file name.
170  * If mark is true, mark the message "saved."
171  */
172 static int
173 save1(char str[], int mark)
174 {
175 	char *file, *cmd;
176 	int f, *msgvec;
177 
178 	cmd = mark ? "save" : "copy";
179 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
180 	if ((file = snarf(str, &f, 0)) == NOSTR)
181 		file = Getf("MBOX");
182 	if (f==-1)
183 		return(1);
184 	if (!f) {
185 		*msgvec = first(0, MMNORM);
186 		if (*msgvec == NULL) {
187 			printf(gettext("No messages to %s.\n"), cmd);
188 			return(1);
189 		}
190 		msgvec[1] = NULL;
191 	}
192 	if (f && getmsglist(str, msgvec, 0) < 0)
193 		return(1);
194 	if ((file = expand(file)) == NOSTR)
195 		return(1);
196 	savemsglist(file, msgvec, mark | S_SAVING);
197 	return(0);
198 }
199 
200 int
201 Save(int *msgvec)
202 {
203 	return(Save1(msgvec, S_MARK));
204 }
205 
206 int
207 Copy(int *msgvec)
208 {
209 	return(Save1(msgvec, 0));
210 }
211 
212 /*
213  * save/copy the indicated messages at the end of a file named
214  * by the sender of the first message in the msglist.
215  */
216 static int
217 Save1(int *msgvec, int mark)
218 {
219 	register char *from;
220 	char recfile[BUFSIZ];
221 
222 #ifdef notdef
223 	from = striphosts(nameof(&message[*msgvec-1], 0));
224 #else
225 	from = nameof(&message[*msgvec-1]);
226 #endif
227 	getrecf(from, recfile, 1, sizeof (recfile));
228 	if (*recfile != '\0')
229 		savemsglist(safeexpand(recfile), msgvec, mark | S_SAVING);
230 	return(0);
231 }
232 
233 int
234 sput(char str[])
235 {
236 	return(put1(str, 0));
237 }
238 
239 int
240 Sput(char str[])
241 {
242 	return(put1(str, S_NOIGNORE));
243 }
244 
245 /*
246  * Put the indicated messages at the end of the passed file name.
247  */
248 static int
249 put1(char str[], int doign)
250 {
251 	char *file;
252 	int f, *msgvec;
253 
254 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
255 	if ((file = snarf(str, &f, 0)) == NOSTR)
256 		file = Getf("MBOX");
257 	if (f==-1)
258 		return(1);
259 	if (!f) {
260 		*msgvec = first(0, MMNORM);
261 		if (*msgvec == NULL) {
262 			printf(gettext("No messages to put.\n"));
263 			return(1);
264 		}
265 		msgvec[1] = NULL;
266 	}
267 	if (f && getmsglist(str, msgvec, 0) < 0)
268 		return(1);
269 	if ((file = expand(file)) == NOSTR)
270 		return(1);
271 	savemsglist(file, msgvec, doign);
272 	return(0);
273 }
274 
275 /*
276  * save a message list in a file.
277  * if wr set, doing "write" instead
278  * of "save" or "copy" so don't put
279  * out header.
280  */
281 
282 static	int wr_linecount;		/* count of lines written */
283 static	int wr_charcount;		/* char count of lines written */
284 static	int wr_inlines;			/* count of lines read */
285 static	long wr_maxlines;		/* total lines in message */
286 static	int wr_inhead;			/* in header of message */
287 
288 static void
289 savemsglist(char *file, int *msgvec, int flag)
290 {
291 	register int *ip, mesg;
292 	register struct message *mp;
293 	char *disp;
294 	FILE *obuf;
295 	struct stat statb;
296 	long lc, cc, t;
297 	int bnry, mflag;
298 
299 	printf("\"%s\" ", file);
300 	flush();
301 	if (stat(file, &statb) >= 0)
302 		disp = "[Appended]";
303 	else
304 		disp = "[New file]";
305 	if ((obuf = fopen(file, "a")) == NULL) {
306 		perror("");
307 		return;
308 	}
309 	lc = cc = 0;
310 	bnry = 0;
311 	if (flag & S_SAVING)
312 		mflag = (int)value("alwaysignore")?(M_IGNORE|M_SAVING):M_SAVING;
313 	else if (flag & S_NOIGNORE)
314 		mflag = 0;
315 	else
316 		mflag = M_IGNORE;
317 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
318 		mesg = *ip;
319 		mp = &message[mesg-1];
320 		if (!mp->m_text) {
321 			bnry = 1;
322 		}
323 		wr_linecount = 0;
324 		wr_charcount = 0;
325 		if (flag & S_NOHEADER) {
326 			wr_inhead = 1;
327 			wr_maxlines = mp->m_lines;
328 			wr_inlines = 0;
329 			t = msend(mp, obuf, 0, wrputs);
330 		} else {
331 			t = msend(mp, obuf, mflag, svputs);
332 		}
333 		if (t < 0) {
334 			perror(file);
335 			fclose(obuf);
336 			return;
337 		}
338 		touch(mesg);
339 		dot = mp;
340 		lc += wr_linecount;
341 		cc += wr_charcount;
342 		if (flag & S_MARK)
343 			mp->m_flag |= MSAVED;
344 	}
345 	fflush(obuf);
346 	if (fferror(obuf))
347 		perror(file);
348 	fclose(obuf);
349 	if (!bnry) {
350 		printf("%s %ld/%ld\n", disp, lc, cc);
351 	} else {
352 		printf("%s binary/%ld\n", disp, cc);
353 	}
354 }
355 
356 static int
357 svputs(const char *line, FILE *obuf)
358 {
359 	wr_linecount++;
360 	wr_charcount += strlen(line);
361 	return(fputs(line, obuf));
362 }
363 
364 static int
365 wrputs(const char *line, FILE *obuf)
366 {
367 	/*
368 	 * If this is a header line or
369 	 * the last line, don't write it out.  Since we may add a
370 	 * "Status" line the line count may be off by one so insist
371 	 * that the last line is blank before we skip it.
372 	 */
373 	wr_inlines++;
374 	if (wr_inhead) {
375 		if (strcmp(line, "\n") == 0)
376 			wr_inhead = 0;
377 		return(0);
378 	}
379 	if (wr_inlines >= wr_maxlines && strcmp(line, "\n") == 0)
380 		return(0);
381 	wr_linecount++;
382 	wr_charcount += strlen(line);
383 	return(fputs(line, obuf));
384 }
385 
386 /*
387  * Write the indicated messages at the end of the passed
388  * file name, minus header and trailing blank line.
389  */
390 
391 int
392 swrite(char str[])
393 {
394 	register char *file;
395 	int f, *msgvec;
396 
397 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
398 	if ((file = snarf(str, &f, 1)) == NOSTR)
399 		return(1);
400 	if (f==-1)
401 		return(1);
402 	if ((file = expand(file)) == NOSTR)
403 		return(1);
404 	if (!f) {
405 		*msgvec = first(0, MMNORM);
406 		if (*msgvec == NULL) {
407 			printf(gettext("No messages to write.\n"));
408 			return(1);
409 		}
410 		msgvec[1] = NULL;
411 	}
412 	if (f && getmsglist(str, msgvec, 0) < 0)
413 		return(1);
414 	savemsglist(file, msgvec, S_MARK|S_NOHEADER);
415 	return(0);
416 }
417 
418 /*
419  * Snarf the file from the end of the command line and
420  * return a pointer to it.  If there is no file attached,
421  * just return NOSTR.  Put a null in front of the file
422  * name so that the message list processing won't see it,
423  * unless the file name is the only thing on the line, in
424  * which case, return 0 in the reference flag variable.
425  */
426 
427 /*
428  * The following definitions are used to characterize the syntactic
429  * category of the preceding character in the following parse procedure.
430  * The variable pc_type assumes these values.
431  */
432 
433 #define	SN_DELIM	1	/* Delimiter (<blank> or line beginning) */
434 #define	SN_TOKEN	2	/* A part of a token */
435 #define	SN_QUOTE	4	/* An entire quoted string (ie, "...") */
436 
437 char *
438 snarf(char linebuf[], int *flag, int erf)
439 {
440 	register char *p;		/* utility pointer */
441 	register char qc;		/* quotation character to match */
442 	register unsigned int  pc_type;	/* preceding character type */
443 	register char *tok_beg;		/* beginning of last token */
444 	register char *tok_end;		/* end of last token */
445 	char *line_beg;			/* beginning of line, after */
446 					/* leading whitespace */
447 
448 	/*
449 	 * Skip leading whitespace.
450 	 */
451 	for (line_beg = linebuf;
452 	     *line_beg && any(*line_beg, " \t");
453 	     line_beg++) {
454 		/* empty body */
455 	}
456 	if (!*line_beg) {
457 		if (erf) {
458 			printf(gettext("No file specified\n."));
459 		}
460 		*flag = 0;
461 		return(NOSTR);
462 	}
463 	/*
464 	 * Process line from left-to-right, 1 char at a time.
465 	 */
466 	for (pc_type = SN_DELIM, tok_beg = tok_end = NOSTR, p = line_beg;
467 	     *p != '\0'; ) {
468 		if (any(*p, " \t")) {
469 			/* This character is a DELIMITER */
470 			if (pc_type & (SN_TOKEN|SN_QUOTE)) {
471 				tok_end = p - 1;
472 			}
473 			pc_type = SN_DELIM;
474 			p++;
475 		} else if ((qc = *p) == '"' || qc == '\'') {
476 			/* This character is a QUOTE character */
477 			if (pc_type == SN_TOKEN) {
478 				/* embedded quotation symbols are simply */
479 				/* token characters. */
480 				p++;
481 				continue;
482 			}
483 			/* Search for the matching QUOTE character */
484 			for (tok_beg = p, tok_end = NOSTR, p++;
485 			     *p != '\0' && *p != qc;
486 			     p++) {
487 				if (*p == '\\' && *(p+1) == qc) {
488 					p++;
489 				}
490 			}
491 			if (*p == '\0') {
492 				printf(gettext("Syntax error: missing "
493 					       "%c.\n"), qc);
494 				*flag = -1;
495 				return(NOSTR);
496 			}
497 			tok_end = p;
498 			pc_type = SN_QUOTE;
499 			p++;
500 		} else {
501 			/* This character should be a TOKEN character */
502 			if (pc_type & (SN_DELIM|SN_TOKEN)) {
503 				if (pc_type & SN_DELIM) {
504 					tok_beg = p;
505 					tok_end = NOSTR;
506 				}
507 			} else {
508 				printf(gettext("improper quotes"
509 					       " at \"%s\".\n"), p);
510 				*flag = -1;
511 				return(NOSTR);
512 			}
513 			if (*p == '\\' && *++p == '\0') {
514 				printf(gettext("\'\\\' at "
515 					       "end of line.\n"));
516 				*flag = -1;
517 				return(NOSTR);
518 			}
519 			pc_type = SN_TOKEN;
520 			p++;
521 		}
522 	}
523 	if (pc_type == SN_TOKEN) {
524 		tok_end = p - 1;
525 	}
526 	if (tok_beg != NOSTR && tok_end != NOSTR) {
527 		if (tok_beg == line_beg) {
528 			*flag = 0;
529 		} else {
530 			tok_beg[-1] = '\0';
531 			*flag = 1;
532 		}
533 		tok_end[1] = '\0';
534 		return(tok_beg);
535 	} else {
536 		if (erf) {
537 			printf(gettext("No file specified\n."));
538 		}
539 		*flag = 0;
540 		return(NOSTR);
541 	}
542 }
543 
544 /*
545  * Delete messages, then type the new dot.
546  */
547 
548 int
549 deltype(int msgvec[])
550 {
551 	int list[2];
552 	int lastdot;
553 
554 	lastdot = dot - &message[0] + 1;
555 	if (delm(msgvec) >= 0) {
556 		list[0] = dot - &message[0];
557 		list[0]++;
558 		if (list[0] > lastdot) {
559 			touch(list[0]);
560 			list[1] = NULL;
561 			return(type(list));
562 		}
563 		printf(gettext("At EOF\n"));
564 		return(0);
565 	}
566 	else {
567 		printf(gettext("No more messages\n"));
568 		return(0);
569 	}
570 }
571 
572 /*
573  * Delete the indicated messages.
574  * Set dot to some nice place afterwards.
575  */
576 int
577 delm(int *msgvec)
578 {
579 	register struct message *mp;
580 	int *ip, mesg;
581 	int last;
582 
583 	last = NULL;
584 	for (ip = msgvec; *ip != NULL; ip++) {
585 		mesg = *ip;
586 		touch(mesg);
587 		mp = &message[mesg-1];
588 		mp->m_flag |= MDELETED|MTOUCH;
589 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
590 		last = mesg;
591 	}
592 	if (last != NULL) {
593 		dot = &message[last-1];
594 		last = first(0, MDELETED);
595 		if (last != NULL) {
596 			dot = &message[last-1];
597 			return(0);
598 		}
599 		else {
600 			dot = &message[0];
601 			return(-1);
602 		}
603 	}
604 
605 	/*
606 	 * Following can't happen -- it keeps lint happy
607 	 */
608 
609 	return(-1);
610 }
611 
612 /*
613  * Undelete the indicated messages.
614  */
615 int
616 undelete(int *msgvec)
617 {
618 	register struct message *mp;
619 	int *ip, mesg;
620 
621 	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
622 		mesg = *ip;
623 		if (mesg == 0)
624 			return(0);
625 		touch(mesg);
626 		mp = &message[mesg-1];
627 		dot = mp;
628 		mp->m_flag &= ~MDELETED;
629 	}
630 	return(0);
631 }
632 
633 /*
634  * Add the given header fields to the retained list.
635  * If no arguments, print the current list of retained fields.
636  */
637 int
638 retfield(char *list[])
639 {
640 	char field[BUFSIZ];
641 	register int h;
642 	register struct ignore *igp;
643 	char **ap;
644 
645 	if (argcount(list) == 0)
646 		return(retshow());
647 	for (ap = list; *ap != 0; ap++) {
648 		istrcpy(field, sizeof (field), *ap);
649 
650 		if (member(field, retain))
651 			continue;
652 
653 		h = hash(field);
654 		if ((igp = (struct ignore *)
655 		    calloc(1, sizeof (struct ignore))) == NULL) {
656 			panic("Couldn't allocate memory");
657 		}
658 		if ((igp->i_field = (char *)
659 		    calloc(strlen(field) + 1, sizeof (char))) == NULL) {
660 			panic("Couldn't allocate memory");
661 		}
662 		strcpy(igp->i_field, field);
663 		igp->i_link = retain[h];
664 		retain[h] = igp;
665 		nretained++;
666 	}
667 	return(0);
668 }
669 
670 /*
671  * Print out all currently retained fields.
672  */
673 static int
674 retshow(void)
675 {
676 	register int h, count;
677 	struct ignore *igp;
678 	char **ap, **ring;
679 
680 	count = 0;
681 	for (h = 0; h < HSHSIZE; h++)
682 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
683 			count++;
684 	if (count == 0) {
685 		printf(gettext("No fields currently being retained.\n"));
686 		return(0);
687 	}
688 	ring = (char **) salloc((count + 1) * sizeof (char *));
689 	ap = ring;
690 	for (h = 0; h < HSHSIZE; h++)
691 		for (igp = retain[h]; igp != 0; igp = igp->i_link)
692 			*ap++ = igp->i_field;
693 	*ap = 0;
694 	qsort(ring, count, sizeof (char *), igcomp);
695 	for (ap = ring; *ap != 0; ap++)
696 		printf("%s\n", *ap);
697 	return(0);
698 }
699 
700 /*
701  * Remove a list of fields from the retain list.
702  */
703 int
704 unretfield(char *list[])
705 {
706 	char **ap, field[BUFSIZ];
707 	register int h, count = 0;
708 	register struct ignore *ig1, *ig2;
709 
710 	if (argcount(list) == 0) {
711 		for (h = 0; h < HSHSIZE; h++) {
712 			ig1 = retain[h];
713 			while (ig1) {
714 				free(ig1->i_field);
715 				ig2 = ig1->i_link;
716 				free((char *) ig1);
717 				ig1 = ig2;
718 				count++;
719 			}
720 			retain[h] = NULL;
721 		}
722 		if (count == 0)
723 			printf(gettext(
724 			    "No fields currently being retained.\n"));
725 		nretained = 0;
726 		return 0;
727 	}
728 	for (ap = list; *ap; ap++) {
729 		istrcpy(field, sizeof (field), *ap);
730 		h = hash(field);
731 		for (ig1 = retain[h]; ig1; ig2 = ig1, ig1 = ig1->i_link)
732 			if (strcmp(ig1->i_field, field) == 0) {
733 				if (ig1 == retain[h])
734 					retain[h] = ig1->i_link;
735 				else
736 					ig2->i_link = ig1->i_link;
737 				free(ig1->i_field);
738 				free((char *) ig1);
739 				nretained--;
740 				break;
741 			}
742 	}
743 	return 0;
744 }
745 
746 /*
747  * Add the given header fields to the ignored list.
748  * If no arguments, print the current list of ignored fields.
749  */
750 int
751 igfield(char *list[])
752 {
753 	char field[BUFSIZ];
754 	register int h;
755 	register struct ignore *igp;
756 	char **ap;
757 
758 	if (argcount(list) == 0)
759 		return(igshow());
760 	for (ap = list; *ap != 0; ap++) {
761 		if (isign(*ap, 0))
762 			continue;
763 		istrcpy(field, sizeof (field), *ap);
764 		h = hash(field);
765 		if ((igp = (struct ignore *)
766 		    calloc(1, sizeof (struct ignore))) == NULL) {
767 			panic("Couldn't allocate memory");
768 		}
769 		if ((igp->i_field = (char *)
770 		    calloc((unsigned)strlen(field) + 1,
771 		    sizeof (char))) == NULL) {
772 			panic("Couldn't allocate memory");
773 		}
774 		strcpy(igp->i_field, field);
775 		igp->i_link = ignore[h];
776 		ignore[h] = igp;
777 	}
778 	return(0);
779 }
780 
781 /*
782  * Print out all currently ignored fields.
783  */
784 static int
785 igshow(void)
786 {
787 	register int h, count;
788 	struct ignore *igp;
789 	char **ap, **ring;
790 
791 	count = 0;
792 	for (h = 0; h < HSHSIZE; h++)
793 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
794 			count++;
795 	if (count == 0) {
796 		printf(gettext("No fields currently being ignored.\n"));
797 		return(0);
798 	}
799 	ring = (char **) salloc((count + 1) * sizeof (char *));
800 	ap = ring;
801 	for (h = 0; h < HSHSIZE; h++)
802 		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
803 			*ap++ = igp->i_field;
804 	*ap = 0;
805 	qsort((char *) ring, (unsigned) count, sizeof (char *), igcomp);
806 	for (ap = ring; *ap != 0; ap++)
807 		printf("%s\n", *ap);
808 	return(0);
809 }
810 
811 /*
812  * Compare two names for sorting ignored field list.
813  */
814 static int
815 igcomp(const void *l, const void *r)
816 {
817 	return(strcmp(*(char **)l, *(char **)r));
818 }
819 
820 /*
821  * Remove a list of fields from the ignore list.
822  */
823 int
824 unigfield(char *list[])
825 {
826 	char **ap, field[BUFSIZ];
827 	register int h, count = 0;
828 	register struct ignore *ig1, *ig2;
829 
830 	if (argcount(list) == 0) {
831 		for (h = 0; h < HSHSIZE; h++) {
832 			ig1 = ignore[h];
833 			while (ig1) {
834 				free(ig1->i_field);
835 				ig2 = ig1->i_link;
836 				free((char *) ig1);
837 				ig1 = ig2;
838 				count++;
839 			}
840 			ignore[h] = NULL;
841 		}
842 		if (count == 0)
843 			printf(gettext("No fields currently being ignored.\n"));
844 		return 0;
845 	}
846 	for (ap = list; *ap; ap++) {
847 		istrcpy(field, sizeof (field), *ap);
848 		h = hash(field);
849 		for (ig1 = ignore[h]; ig1; ig2 = ig1, ig1 = ig1->i_link)
850 			if (strcmp(ig1->i_field, field) == 0) {
851 				if (ig1 == ignore[h])
852 					ignore[h] = ig1->i_link;
853 				else
854 					ig2->i_link = ig1->i_link;
855 				free(ig1->i_field);
856 				free((char *) ig1);
857 				break;
858 			}
859 	}
860 	return 0;
861 }
862