cmd2.c revision 6c83d09f819e9de7126c8539546eca2e276df44a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2002 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, go to the next applicable argument
* following dot, otherwise, go to the next applicable message.
* If given as first command with no arguments, print first message.
*/
int
next(int *msgvec)
{
register struct message *mp;
register int *ip, *ip2;
int list[2], mdot;
if (*msgvec != NULL) {
/*
* If some messages were supplied, find the
* first applicable one following dot using
* wrap around.
*/
mdot = dot - &message[0] + 1;
/*
* Find the first message in the supplied
* message list which follows dot.
*/
for (ip = msgvec; *ip != NULL; ip++)
if (*ip > mdot)
break;
if (*ip == NULL)
ip = msgvec;
ip2 = ip;
do {
mp = &message[*ip2 - 1];
if ((mp->m_flag & MDELETED) == 0) {
dot = mp;
goto hitit;
}
if (*ip2 != NULL)
ip2++;
if (*ip2 == NULL)
ip2 = msgvec;
} while (ip2 != ip);
printf(gettext("No messages applicable\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.
*/
for (pc_type = SN_DELIM, tok_beg = tok_end = NOSTR, p = line_beg;
*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;
}