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