view usr/src/cmd/mailx/cmd2.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "rcv.h"
#include <locale.h>

/*
 * mailx -- a modified version of a University of California at Berkeley
 *	mail program
 *
 * More user commands.
 */

static int	igshow(void);
static int	igcomp(const void *l, const void *r);
static int	save1(char str[], int mark);
static int	Save1(int *msgvec, int mark);
static void	savemsglist(char *file, int *msgvec, int flag);
static int	put1(char str[], int doign);
static int	svputs(const char *line, FILE *obuf);
static int	wrputs(const char *line, FILE *obuf);
static int	retshow(void);

/* flags for savemsglist() */
#define	S_MARK		1		/* mark the message as saved */
#define	S_NOHEADER	2		/* don't write out the header */
#define	S_SAVING	4		/* doing save/copy */
#define	S_NOIGNORE	8		/* don't do ignore processing */

/*
 * If any arguments were given, print the first message
 * identified by the first argument. If no arguments are given,
 * print the next applicable message after dot.
 */

int
next(int *msgvec)
{
	register struct message *mp;
	int list[2];

	if (*msgvec != NULL) {
		if (*msgvec < 0) {
			printf((gettext("Negative message given\n")));
			return (1);
		}
		mp = &message[*msgvec - 1];
		if ((mp->m_flag & MDELETED) == 0) {
			dot = mp;
			goto hitit;
		}
		printf(gettext("No applicable message\n"));
		return (1);
	}

	/*
	 * If this is the first command, select message 1.
	 * Note that this must exist for us to get here at all.
	 */
	if (!sawcom)
		goto hitit;

	/*
	 * Just find the next good message after dot, no
	 * wraparound.
	 */
	for (mp = dot+1; mp < &message[msgCount]; mp++)
		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
			break;
	if (mp >= &message[msgCount]) {
		printf(gettext("At EOF\n"));
		return (0);
	}
	dot = mp;
hitit:
	/*
	 * Print dot.
	 */
	list[0] = dot - &message[0] + 1;
	list[1] = NULL;
	return (type(list));
}

/*
 * Save a message in a file.  Mark the message as saved
 * so we can discard when the user quits.
 */
int
save(char str[])
{
	return (save1(str, S_MARK));
}

/*
 * Copy a message to a file without affected its saved-ness
 */
int
copycmd(char str[])
{
	return (save1(str, 0));
}

/*
 * Save/copy the indicated messages at the end of the passed file name.
 * If mark is true, mark the message "saved."
 */
static int
save1(char str[], int mark)
{
	char *file, *cmd;
	int f, *msgvec;

	cmd = mark ? "save" : "copy";
	msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec));
	if ((file = snarf(str, &f, 0)) == NOSTR)
		file = Getf("MBOX");
	if (f == -1)
		return (1);
	if (!f) {
		*msgvec = first(0, MMNORM);
		if (*msgvec == NULL) {
			printf(gettext("No messages to %s.\n"), cmd);
			return (1);
		}
		msgvec[1] = NULL;
	}
	if (f && getmsglist(str, msgvec, 0) < 0)
		return (1);
	if ((file = expand(file)) == NOSTR)
		return (1);
	savemsglist(file, msgvec, mark | S_SAVING);
	return (0);
}

int
Save(int *msgvec)
{
	return (Save1(msgvec, S_MARK));
}

int
Copy(int *msgvec)
{
	return (Save1(msgvec, 0));
}

/*
 * save/copy the indicated messages at the end of a file named
 * by the sender of the first message in the msglist.
 */
static int
Save1(int *msgvec, int mark)
{
	register char *from;
	char recfile[BUFSIZ];

#ifdef notdef
	from = striphosts(nameof(&message[*msgvec-1], 0));
#else
	from = nameof(&message[*msgvec-1]);
#endif
	getrecf(from, recfile, 1, sizeof (recfile));
	if (*recfile != '\0')
		savemsglist(safeexpand(recfile), msgvec, mark | S_SAVING);
	return (0);
}

int
sput(char str[])
{
	return (put1(str, 0));
}

int
Sput(char str[])
{
	return (put1(str, S_NOIGNORE));
}

/*
 * Put the indicated messages at the end of the passed file name.
 */
static int
put1(char str[], int doign)
{
	char *file;
	int f, *msgvec;

	msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec));
	if ((file = snarf(str, &f, 0)) == NOSTR)
		file = Getf("MBOX");
	if (f == -1)
		return (1);
	if (!f) {
		*msgvec = first(0, MMNORM);
		if (*msgvec == NULL) {
			printf(gettext("No messages to put.\n"));
			return (1);
		}
		msgvec[1] = NULL;
	}
	if (f && getmsglist(str, msgvec, 0) < 0)
		return (1);
	if ((file = expand(file)) == NOSTR)
		return (1);
	savemsglist(file, msgvec, doign);
	return (0);
}

/*
 * save a message list in a file.
 * if wr set, doing "write" instead
 * of "save" or "copy" so don't put
 * out header.
 */

static	int wr_linecount;		/* count of lines written */
static	int wr_charcount;		/* char count of lines written */
static	int wr_inlines;			/* count of lines read */
static	long wr_maxlines;		/* total lines in message */
static	int wr_inhead;			/* in header of message */

static void
savemsglist(char *file, int *msgvec, int flag)
{
	register int *ip, mesg;
	register struct message *mp;
	char *disp;
	FILE *obuf;
	struct stat statb;
	long lc, cc, t;
	int bnry, mflag;

	printf("\"%s\" ", file);
	flush();
	if (stat(file, &statb) >= 0)
		disp = "[Appended]";
	else
		disp = "[New file]";
	if ((obuf = fopen(file, "a")) == NULL) {
		perror("");
		return;
	}
	lc = cc = 0;
	bnry = 0;
	if (flag & S_SAVING)
		mflag = (int)value("alwaysignore")?(M_IGNORE|M_SAVING):M_SAVING;
	else if (flag & S_NOIGNORE)
		mflag = 0;
	else
		mflag = M_IGNORE;
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		mp = &message[mesg-1];
		if (!mp->m_text) {
			bnry = 1;
		}
		wr_linecount = 0;
		wr_charcount = 0;
		if (flag & S_NOHEADER) {
			wr_inhead = 1;
			wr_maxlines = mp->m_lines;
			wr_inlines = 0;
			t = msend(mp, obuf, 0, wrputs);
		} else {
			t = msend(mp, obuf, mflag, svputs);
		}
		if (t < 0) {
			perror(file);
			fclose(obuf);
			return;
		}
		touch(mesg);
		dot = mp;
		lc += wr_linecount;
		cc += wr_charcount;
		if (flag & S_MARK)
			mp->m_flag |= MSAVED;
	}
	fflush(obuf);
	if (fferror(obuf))
		perror(file);
	fclose(obuf);
	if (!bnry) {
		printf("%s %ld/%ld\n", disp, lc, cc);
	} else {
		printf("%s binary/%ld\n", disp, cc);
	}
}

static int
svputs(const char *line, FILE *obuf)
{
	wr_linecount++;
	wr_charcount += strlen(line);
	return (fputs(line, obuf));
}

static int
wrputs(const char *line, FILE *obuf)
{
	/*
	 * If this is a header line or
	 * the last line, don't write it out.  Since we may add a
	 * "Status" line the line count may be off by one so insist
	 * that the last line is blank before we skip it.
	 */
	wr_inlines++;
	if (wr_inhead) {
		if (strcmp(line, "\n") == 0)
			wr_inhead = 0;
		return (0);
	}
	if (wr_inlines >= wr_maxlines && strcmp(line, "\n") == 0)
		return (0);
	wr_linecount++;
	wr_charcount += strlen(line);
	return (fputs(line, obuf));
}

/*
 * Write the indicated messages at the end of the passed
 * file name, minus header and trailing blank line.
 */

int
swrite(char str[])
{
	register char *file;
	int f, *msgvec;

	msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec));
	if ((file = snarf(str, &f, 1)) == NOSTR)
		return (1);
	if (f == -1)
		return (1);
	if ((file = expand(file)) == NOSTR)
		return (1);
	if (!f) {
		*msgvec = first(0, MMNORM);
		if (*msgvec == NULL) {
			printf(gettext("No messages to write.\n"));
			return (1);
		}
		msgvec[1] = NULL;
	}
	if (f && getmsglist(str, msgvec, 0) < 0)
		return (1);
	savemsglist(file, msgvec, S_MARK|S_NOHEADER);
	return (0);
}

/*
 * Snarf the file from the end of the command line and
 * return a pointer to it.  If there is no file attached,
 * just return NOSTR.  Put a null in front of the file
 * name so that the message list processing won't see it,
 * unless the file name is the only thing on the line, in
 * which case, return 0 in the reference flag variable.
 */

/*
 * The following definitions are used to characterize the syntactic
 * category of the preceding character in the following parse procedure.
 * The variable pc_type assumes these values.
 */

#define	SN_DELIM	1	/* Delimiter (<blank> or line beginning) */
#define	SN_TOKEN	2	/* A part of a token */
#define	SN_QUOTE	4	/* An entire quoted string (ie, "...") */

char *
snarf(char linebuf[], int *flag, int erf)
{
	register char *p;		/* utility pointer */
	register char qc;		/* quotation character to match */
	register unsigned int  pc_type;	/* preceding character type */
	register char *tok_beg;		/* beginning of last token */
	register char *tok_end;		/* end of last token */
	char *line_beg;			/* beginning of line, after */
					/* leading whitespace */

	/*
	 * Skip leading whitespace.
	 */
	for (line_beg = linebuf;
	*line_beg && any(*line_beg, " \t");
		line_beg++) {
		/* empty body */
	}
	if (!*line_beg) {
		if (erf) {
			printf(gettext("No file specified\n."));
		}
		*flag = 0;
		return (NOSTR);
	}
	/*
	 * Process line from left-to-right, 1 char at a time.
	 */
	pc_type = SN_DELIM;
	tok_beg = tok_end = NOSTR;
	p = line_beg;
	while (*p != '\0') {
		if (any(*p, " \t")) {
			/* This character is a DELIMITER */
			if (pc_type & (SN_TOKEN|SN_QUOTE)) {
				tok_end = p - 1;
			}
			pc_type = SN_DELIM;
			p++;
		} else if ((qc = *p) == '"' || qc == '\'') {
			/* This character is a QUOTE character */
			if (pc_type == SN_TOKEN) {
				/* embedded quotation symbols are simply */
				/* token characters. */
				p++;
				continue;
			}
			/* Search for the matching QUOTE character */
			for (tok_beg = p, tok_end = NOSTR, p++;
			    *p != '\0' && *p != qc;
				p++) {
				if (*p == '\\' && *(p+1) == qc) {
					p++;
				}
			}
			if (*p == '\0') {
				printf(gettext("Syntax error: missing "
				    "%c.\n"), qc);
				*flag = -1;
				return (NOSTR);
			}
			tok_end = p;
			pc_type = SN_QUOTE;
			p++;
		} else {
			/* This character should be a TOKEN character */
			if (pc_type & (SN_DELIM|SN_TOKEN)) {
				if (pc_type & SN_DELIM) {
					tok_beg = p;
					tok_end = NOSTR;
				}
			} else {
				printf(gettext("improper quotes"
				    " at \"%s\".\n"), p);
				*flag = -1;
				return (NOSTR);
			}
			if (*p == '\\' && *++p == '\0') {
				printf(gettext("\'\\\' at "
				    "end of line.\n"));
				*flag = -1;
				return (NOSTR);
			}
			pc_type = SN_TOKEN;
			p++;
		}
	}
	if (pc_type == SN_TOKEN) {
		tok_end = p - 1;
	}
	if (tok_beg != NOSTR && tok_end != NOSTR) {
		if (tok_beg == line_beg) {
			*flag = 0;
		} else {
			tok_beg[-1] = '\0';
			*flag = 1;
		}
		tok_end[1] = '\0';
		return (tok_beg);
	} else {
		if (erf) {
			printf(gettext("No file specified\n."));
		}
		*flag = 0;
		return (NOSTR);
	}
}

/*
 * Delete messages, then type the new dot.
 */

int
deltype(int msgvec[])
{
	int list[2];
	int lastdot;

	lastdot = dot - &message[0] + 1;
	if (delm(msgvec) >= 0) {
		list[0] = dot - &message[0];
		list[0]++;
		if (list[0] > lastdot) {
			touch(list[0]);
			list[1] = NULL;
			return (type(list));
		}
		printf(gettext("At EOF\n"));
		return (0);
	} else {
		printf(gettext("No more messages\n"));
		return (0);
	}
}

/*
 * Delete the indicated messages.
 * Set dot to some nice place afterwards.
 */
int
delm(int *msgvec)
{
	register struct message *mp;
	int *ip, mesg;
	int last;

	last = NULL;
	for (ip = msgvec; *ip != NULL; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		mp->m_flag |= MDELETED|MTOUCH;
		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
		last = mesg;
	}
	if (last != NULL) {
		dot = &message[last-1];
		last = first(0, MDELETED);
		if (last != NULL) {
			dot = &message[last-1];
			return (0);
		} else {
			dot = &message[0];
			return (-1);
		}
	}

	/*
	 * Following can't happen -- it keeps lint happy
	 */

	return (-1);
}

/*
 * Undelete the indicated messages.
 */
int
undelete(int *msgvec)
{
	register struct message *mp;
	int *ip, mesg;

	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		if (mesg == 0)
			return (0);
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		mp->m_flag &= ~MDELETED;
	}
	return (0);
}

/*
 * Add the given header fields to the retained list.
 * If no arguments, print the current list of retained fields.
 */
int
retfield(char *list[])
{
	char field[BUFSIZ];
	register int h;
	register struct ignore *igp;
	char **ap;

	if (argcount(list) == 0)
		return (retshow());
	for (ap = list; *ap != 0; ap++) {
		istrcpy(field, sizeof (field), *ap);

		if (member(field, retain))
			continue;

		h = hash(field);
		if ((igp = (struct ignore *)
		    calloc(1, sizeof (struct ignore))) == NULL) {
			panic("Couldn't allocate memory");
		}
		if ((igp->i_field = (char *)
		    calloc(strlen(field) + 1, sizeof (char))) == NULL) {
			panic("Couldn't allocate memory");
		}
		strcpy(igp->i_field, field);
		igp->i_link = retain[h];
		retain[h] = igp;
		nretained++;
	}
	return (0);
}

/*
 * Print out all currently retained fields.
 */
static int
retshow(void)
{
	register int h, count;
	struct ignore *igp;
	char **ap, **ring;

	count = 0;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = retain[h]; igp != 0; igp = igp->i_link)
			count++;
	if (count == 0) {
		printf(gettext("No fields currently being retained.\n"));
		return (0);
	}
	ring = (char **)salloc((count + 1) * sizeof (char *));
	ap = ring;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = retain[h]; igp != 0; igp = igp->i_link)
			*ap++ = igp->i_field;
	*ap = 0;
	qsort(ring, count, sizeof (char *), igcomp);
	for (ap = ring; *ap != 0; ap++)
		printf("%s\n", *ap);
	return (0);
}

/*
 * Remove a list of fields from the retain list.
 */
int
unretfield(char *list[])
{
	char **ap, field[BUFSIZ];
	register int h, count = 0;
	register struct ignore *ig1, *ig2;

	if (argcount(list) == 0) {
		for (h = 0; h < HSHSIZE; h++) {
			ig1 = retain[h];
			while (ig1) {
				free(ig1->i_field);
				ig2 = ig1->i_link;
				free((char *)ig1);
				ig1 = ig2;
				count++;
			}
			retain[h] = NULL;
		}
		if (count == 0)
			printf(gettext(
			    "No fields currently being retained.\n"));
		nretained = 0;
		return (0);
	}
	for (ap = list; *ap; ap++) {
		istrcpy(field, sizeof (field), *ap);
		h = hash(field);
		for (ig1 = retain[h]; ig1; ig2 = ig1, ig1 = ig1->i_link)
			if (strcmp(ig1->i_field, field) == 0) {
				if (ig1 == retain[h])
					retain[h] = ig1->i_link;
				else
					ig2->i_link = ig1->i_link;
				free(ig1->i_field);
				free((char *)ig1);
				nretained--;
				break;
			}
	}
	return (0);
}

/*
 * Add the given header fields to the ignored list.
 * If no arguments, print the current list of ignored fields.
 */
int
igfield(char *list[])
{
	char field[BUFSIZ];
	register int h;
	register struct ignore *igp;
	char **ap;

	if (argcount(list) == 0)
		return (igshow());
	for (ap = list; *ap != 0; ap++) {
		if (isign(*ap, 0))
			continue;
		istrcpy(field, sizeof (field), *ap);
		h = hash(field);
		if ((igp = (struct ignore *)
		    calloc(1, sizeof (struct ignore))) == NULL) {
			panic("Couldn't allocate memory");
		}
		if ((igp->i_field = (char *)
		    calloc((unsigned)strlen(field) + 1,
		    sizeof (char))) == NULL) {
			panic("Couldn't allocate memory");
		}
		strcpy(igp->i_field, field);
		igp->i_link = ignore[h];
		ignore[h] = igp;
	}
	return (0);
}

/*
 * Print out all currently ignored fields.
 */
static int
igshow(void)
{
	register int h, count;
	struct ignore *igp;
	char **ap, **ring;

	count = 0;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
			count++;
	if (count == 0) {
		printf(gettext("No fields currently being ignored.\n"));
		return (0);
	}
	ring = (char **)salloc((count + 1) * sizeof (char *));
	ap = ring;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
			*ap++ = igp->i_field;
	*ap = 0;
	qsort((char *)ring, (unsigned)count, sizeof (char *), igcomp);
	for (ap = ring; *ap != 0; ap++)
		printf("%s\n", *ap);
	return (0);
}

/*
 * Compare two names for sorting ignored field list.
 */
static int
igcomp(const void *l, const void *r)
{
	return (strcmp(*(char **)l, *(char **)r));
}

/*
 * Remove a list of fields from the ignore list.
 */
int
unigfield(char *list[])
{
	char **ap, field[BUFSIZ];
	register int h, count = 0;
	register struct ignore *ig1, *ig2;

	if (argcount(list) == 0) {
		for (h = 0; h < HSHSIZE; h++) {
			ig1 = ignore[h];
			while (ig1) {
				free(ig1->i_field);
				ig2 = ig1->i_link;
				free((char *)ig1);
				ig1 = ig2;
				count++;
			}
			ignore[h] = NULL;
		}
		if (count == 0)
			printf(gettext("No fields currently being ignored.\n"));
		return (0);
	}
	for (ap = list; *ap; ap++) {
		istrcpy(field, sizeof (field), *ap);
		h = hash(field);
		for (ig1 = ignore[h]; ig1; ig2 = ig1, ig1 = ig1->i_link)
			if (strcmp(ig1->i_field, field) == 0) {
				if (ig1 == ignore[h])
					ignore[h] = ig1->i_link;
				else
					ig2->i_link = ig1->i_link;
				free(ig1->i_field);
				free((char *)ig1);
				break;
			}
	}
	return (0);
}