Home | History | Annotate | Line # | Download | only in mail
cmd2.c revision 1.13
      1 /*	$NetBSD: cmd2.c,v 1.13 2002/03/02 15:27:51 wiz Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 #if 0
     39 static char sccsid[] = "@(#)cmd2.c	8.1 (Berkeley) 6/6/93";
     40 #else
     41 __RCSID("$NetBSD: cmd2.c,v 1.13 2002/03/02 15:27:51 wiz Exp $");
     42 #endif
     43 #endif /* not lint */
     44 
     45 #include "rcv.h"
     46 #include "extern.h"
     47 
     48 /*
     49  * Mail -- a mail program
     50  *
     51  * More user commands.
     52  */
     53 extern int wait_status;
     54 static int igcomp(const void *, const void *);
     55 
     56 /*
     57  * If any arguments were given, go to the next applicable argument
     58  * following dot, otherwise, go to the next applicable message.
     59  * If given as first command with no arguments, print first message.
     60  */
     61 int
     62 next(void *v)
     63 {
     64 	int *msgvec = v;
     65 	struct message *mp;
     66 	int *ip, *ip2;
     67 	int list[2], mdot;
     68 
     69 	if (*msgvec != 0) {
     70 
     71 		/*
     72 		 * If some messages were supplied, find the
     73 		 * first applicable one following dot using
     74 		 * wrap around.
     75 		 */
     76 
     77 		mdot = dot - &message[0] + 1;
     78 
     79 		/*
     80 		 * Find the first message in the supplied
     81 		 * message list which follows dot.
     82 		 */
     83 
     84 		for (ip = msgvec; *ip != 0; ip++)
     85 			if (*ip > mdot)
     86 				break;
     87 		if (*ip == 0)
     88 			ip = msgvec;
     89 		ip2 = ip;
     90 		do {
     91 			mp = &message[*ip2 - 1];
     92 			if ((mp->m_flag & MDELETED) == 0) {
     93 				dot = mp;
     94 				goto hitit;
     95 			}
     96 			if (*ip2 != 0)
     97 				ip2++;
     98 			if (*ip2 == 0)
     99 				ip2 = msgvec;
    100 		} while (ip2 != ip);
    101 		printf("No messages applicable\n");
    102 		return(1);
    103 	}
    104 
    105 	/*
    106 	 * If this is the first command, select message 1.
    107 	 * Note that this must exist for us to get here at all.
    108 	 */
    109 
    110 	if (!sawcom)
    111 		goto hitit;
    112 
    113 	/*
    114 	 * Just find the next good message after dot, no
    115 	 * wraparound.
    116 	 */
    117 
    118 	for (mp = dot+1; mp < &message[msgCount]; mp++)
    119 		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
    120 			break;
    121 	if (mp >= &message[msgCount]) {
    122 		printf("At EOF\n");
    123 		return(0);
    124 	}
    125 	dot = mp;
    126 hitit:
    127 	/*
    128 	 * Print dot.
    129 	 */
    130 
    131 	list[0] = dot - &message[0] + 1;
    132 	list[1] = 0;
    133 	return(type(list));
    134 }
    135 
    136 /*
    137  * Save a message in a file.  Mark the message as saved
    138  * so we can discard when the user quits.
    139  */
    140 int
    141 save(void *v)
    142 {
    143 	char *str = v;
    144 
    145 	return save1(str, 1, "save", saveignore);
    146 }
    147 
    148 /*
    149  * Copy a message to a file without affected its saved-ness
    150  */
    151 int
    152 copycmd(void *v)
    153 {
    154 	char *str = v;
    155 
    156 	return save1(str, 0, "copy", saveignore);
    157 }
    158 
    159 /*
    160  * Save/copy the indicated messages at the end of the passed file name.
    161  * If markmsg is true, mark the message "saved."
    162  */
    163 int
    164 save1(char str[], int markmsg, char *cmd, struct ignoretab *ignoretabs)
    165 {
    166 	int *ip;
    167 	struct message *mp;
    168 	char *fn, *disp;
    169 	int f, *msgvec;
    170 	FILE *obuf;
    171 
    172 	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
    173 	if ((fn = snarf(str, &f)) == NOSTR)
    174 		return(1);
    175 	if (!f) {
    176 		*msgvec = first(0, MMNORM);
    177 		if (*msgvec == 0) {
    178 			printf("No messages to %s.\n", cmd);
    179 			return(1);
    180 		}
    181 		msgvec[1] = 0;
    182 	}
    183 	if (f && getmsglist(str, msgvec, 0) < 0)
    184 		return(1);
    185 	if ((fn = expand(fn)) == NOSTR)
    186 		return(1);
    187 	printf("\"%s\" ", fn);
    188 	fflush(stdout);
    189 	if (access(fn, 0) >= 0)
    190 		disp = "[Appended]";
    191 	else
    192 		disp = "[New file]";
    193 	if ((obuf = Fopen(fn, "a")) == NULL) {
    194 		perror(NOSTR);
    195 		return(1);
    196 	}
    197 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
    198 		mp = &message[*ip - 1];
    199 		touch(mp);
    200 		if (sendmessage(mp, obuf, ignoretabs, NOSTR) < 0) {
    201 			perror(fn);
    202 			Fclose(obuf);
    203 			return(1);
    204 		}
    205 		if (markmsg)
    206 			mp->m_flag |= MSAVED;
    207 	}
    208 	fflush(obuf);
    209 	if (ferror(obuf))
    210 		perror(fn);
    211 	Fclose(obuf);
    212 	printf("%s\n", disp);
    213 	return(0);
    214 }
    215 
    216 /*
    217  * Write the indicated messages at the end of the passed
    218  * file name, minus header and trailing blank line.
    219  */
    220 int
    221 swrite(void *v)
    222 {
    223 	char *str = v;
    224 
    225 	return save1(str, 1, "write", ignoreall);
    226 }
    227 
    228 /*
    229  * Snarf the file from the end of the command line and
    230  * return a pointer to it.  If there is no file attached,
    231  * just return NOSTR.  Put a null in front of the file
    232  * name so that the message list processing won't see it,
    233  * unless the file name is the only thing on the line, in
    234  * which case, return 0 in the reference flag variable.
    235  */
    236 
    237 char *
    238 snarf(char linebuf[], int *flag)
    239 {
    240 	char *cp;
    241 
    242 	*flag = 1;
    243 	cp = strlen(linebuf) + linebuf - 1;
    244 
    245 	/*
    246 	 * Strip away trailing blanks.
    247 	 */
    248 
    249 	while (cp > linebuf && isspace((unsigned char)*cp))
    250 		cp--;
    251 	*++cp = 0;
    252 
    253 	/*
    254 	 * Now search for the beginning of the file name.
    255 	 */
    256 
    257 	while (cp > linebuf && !isspace((unsigned char)*cp))
    258 		cp--;
    259 	if (*cp == '\0') {
    260 		printf("No file specified.\n");
    261 		return(NOSTR);
    262 	}
    263 	if (isspace((unsigned char)*cp))
    264 		*cp++ = 0;
    265 	else
    266 		*flag = 0;
    267 	return(cp);
    268 }
    269 
    270 /*
    271  * Delete messages.
    272  */
    273 int
    274 delete(void *v)
    275 {
    276 	int *msgvec = v;
    277 	delm(msgvec);
    278 	return 0;
    279 }
    280 
    281 /*
    282  * Delete messages, then type the new dot.
    283  */
    284 int
    285 deltype(void *v)
    286 {
    287 	int *msgvec = v;
    288 	int list[2];
    289 	int lastdot;
    290 
    291 	lastdot = dot - &message[0] + 1;
    292 	if (delm(msgvec) >= 0) {
    293 		list[0] = dot - &message[0] + 1;
    294 		if (list[0] > lastdot) {
    295 			touch(dot);
    296 			list[1] = 0;
    297 			return(type(list));
    298 		}
    299 		printf("At EOF\n");
    300 	} else
    301 		printf("No more messages\n");
    302 	return(0);
    303 }
    304 
    305 /*
    306  * Delete the indicated messages.
    307  * Set dot to some nice place afterwards.
    308  * Internal interface.
    309  */
    310 int
    311 delm(int *msgvec)
    312 {
    313 	struct message *mp;
    314 	int *ip;
    315 	int last;
    316 
    317 	last = 0;
    318 	for (ip = msgvec; *ip != 0; ip++) {
    319 		mp = &message[*ip - 1];
    320 		touch(mp);
    321 		mp->m_flag |= MDELETED|MTOUCH;
    322 		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
    323 		last = *ip;
    324 	}
    325 	if (last != 0) {
    326 		dot = &message[last-1];
    327 		last = first(0, MDELETED);
    328 		if (last != 0) {
    329 			dot = &message[last-1];
    330 			return(0);
    331 		}
    332 		else {
    333 			dot = &message[0];
    334 			return(-1);
    335 		}
    336 	}
    337 
    338 	/*
    339 	 * Following can't happen -- it keeps lint happy
    340 	 */
    341 
    342 	return(-1);
    343 }
    344 
    345 /*
    346  * Undelete the indicated messages.
    347  */
    348 int
    349 undeletecmd(void *v)
    350 {
    351 	int *msgvec = v;
    352 	struct message *mp;
    353 	int *ip;
    354 
    355 	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
    356 		mp = &message[*ip - 1];
    357 		touch(mp);
    358 		dot = mp;
    359 		mp->m_flag &= ~MDELETED;
    360 	}
    361 	return 0;
    362 }
    363 
    364 /*
    365  * Interactively dump core on "core"
    366  */
    367 int
    368 core(void *v)
    369 {
    370 	int pid;
    371 
    372 	switch (pid = vfork()) {
    373 	case -1:
    374 		perror("fork");
    375 		return(1);
    376 	case 0:
    377 		abort();
    378 		_exit(1);
    379 	}
    380 	printf("Okie dokie");
    381 	fflush(stdout);
    382 	wait_child(pid);
    383 	if (WCOREDUMP(wait_status))
    384 		printf(" -- Core dumped.\n");
    385 	else
    386 		printf(" -- Can't dump core.\n");
    387 	return 0;
    388 }
    389 
    390 /*
    391  * Clobber as many bytes of stack as the user requests.
    392  */
    393 int
    394 clobber(void *v)
    395 {
    396 	char **argv = v;
    397 	int times;
    398 
    399 	if (argv[0] == 0)
    400 		times = 1;
    401 	else
    402 		times = (atoi(argv[0]) + 511) / 512;
    403 	clob1(times);
    404 	return 0;
    405 }
    406 
    407 /*
    408  * Clobber the stack.
    409  */
    410 void
    411 clob1(int n)
    412 {
    413 	char buf[512];
    414 	char *cp;
    415 
    416 	if (n <= 0)
    417 		return;
    418 	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
    419 		;
    420 	clob1(n - 1);
    421 }
    422 
    423 /*
    424  * Add the given header fields to the retained list.
    425  * If no arguments, print the current list of retained fields.
    426  */
    427 int
    428 retfield(void *v)
    429 {
    430 	char **list = v;
    431 
    432 	return ignore1(list, ignore + 1, "retained");
    433 }
    434 
    435 /*
    436  * Add the given header fields to the ignored list.
    437  * If no arguments, print the current list of ignored fields.
    438  */
    439 int
    440 igfield(void *v)
    441 {
    442 	char **list = v;
    443 
    444 	return ignore1(list, ignore, "ignored");
    445 }
    446 
    447 int
    448 saveretfield(void *v)
    449 {
    450 	char **list = v;
    451 
    452 	return ignore1(list, saveignore + 1, "retained");
    453 }
    454 
    455 int
    456 saveigfield(void *v)
    457 {
    458 	char **list = v;
    459 
    460 	return ignore1(list, saveignore, "ignored");
    461 }
    462 
    463 int
    464 ignore1(char *list[], struct ignoretab *tab, char *which)
    465 {
    466 	char field[LINESIZE];
    467 	int h;
    468 	struct ignore *igp;
    469 	char **ap;
    470 
    471 	if (*list == NOSTR)
    472 		return igshow(tab, which);
    473 	for (ap = list; *ap != 0; ap++) {
    474 		istrcpy(field, *ap);
    475 		if (member(field, tab))
    476 			continue;
    477 		h = hash(field);
    478 		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
    479 		igp->i_field = calloc((unsigned) strlen(field) + 1,
    480 			sizeof (char));
    481 		strcpy(igp->i_field, field);
    482 		igp->i_link = tab->i_head[h];
    483 		tab->i_head[h] = igp;
    484 		tab->i_count++;
    485 	}
    486 	return 0;
    487 }
    488 
    489 /*
    490  * Print out all currently retained fields.
    491  */
    492 int
    493 igshow(struct ignoretab *tab, char *which)
    494 {
    495 	int h;
    496 	struct ignore *igp;
    497 	char **ap, **ring;
    498 
    499 	if (tab->i_count == 0) {
    500 		printf("No fields currently being %s.\n", which);
    501 		return 0;
    502 	}
    503 	ring = (char **) salloc((tab->i_count + 1) * sizeof (char *));
    504 	ap = ring;
    505 	for (h = 0; h < HSHSIZE; h++)
    506 		for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
    507 			*ap++ = igp->i_field;
    508 	*ap = 0;
    509 	qsort(ring, tab->i_count, sizeof (char *), igcomp);
    510 	for (ap = ring; *ap != 0; ap++)
    511 		printf("%s\n", *ap);
    512 	return 0;
    513 }
    514 
    515 /*
    516  * Compare two names for sorting ignored field list.
    517  */
    518 static int
    519 igcomp(const void *l, const void *r)
    520 {
    521 	return (strcmp(*(char **)l, *(char **)r));
    522 }
    523