xref: /netbsd-src/usr.bin/mail/main.c (revision b7ae68fde0d8ef1c03714e8bbb1ee7c6118ea93b)
1 /*	$NetBSD: main.c,v 1.22 2006/09/18 19:46:21 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 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
35 	The Regents of the University of California.  All rights reserved.\n");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 4/20/95";
41 #else
42 __RCSID("$NetBSD: main.c,v 1.22 2006/09/18 19:46:21 christos Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #define EXTERN
47 #include "rcv.h"
48 #undef EXTERN
49 
50 #include "extern.h"
51 
52 #ifdef USE_READLINE
53 #include "complete.h"
54 #endif
55 
56 int	main(int, char **);
57 
58 /*
59  * Mail -- a mail program
60  *
61  * Startup -- interface with user.
62  */
63 
64 jmp_buf	hdrjmp;
65 
66 
67 /*
68  * Break up a white-space or comma delimited name list so that aliases
69  * can get expanded.  Without this, the CC: or BCC: list is broken too
70  * late for alias expansion to occur.
71  */
72 struct name *
73 lexpand(char *str, int ntype)
74 {
75 	char *list;
76 	struct name *np = NULL;
77 	char *word, *p;
78 
79 	list = strdup(str);
80 	if (list == NULL)
81 		err(EXIT_FAILURE, "strdup failed: %s", str);
82 
83 	word = list;
84 	for (word = list ; *word ; word = p) {
85 		while (*word == ' ' || *word == '\t')
86 			continue;
87 		for (p = word;
88 		     *p && *p != ' ' && *p != '\t' && *p != ',';
89 		      p++ )
90 			continue;
91 		if (*p)
92 			*p++ = '\0';
93 		np = cat(np, nalloc(word, ntype));
94 	}
95 
96 	free(list);
97 	return np;
98 }
99 
100 int
101 main(int argc, char *argv[])
102 {
103 	int i;
104 	struct name *to, *cc, *bcc, *smopts;
105 	char *subject;
106 	const char *ef;
107 	char nosrc = 0;
108 	sig_t prevint;
109 	const char *rc;
110 
111 	/*
112 	 * Set up a reasonable environment.
113 	 * Figure out whether we are being run interactively,
114 	 * start the SIGCHLD catcher, and so forth.
115 	 */
116 	(void)signal(SIGCHLD, sigchild);
117 	if (isatty(0))
118 		assign("interactive", "");
119 	image = -1;
120 	/*
121 	 * Now, determine how we are being used.
122 	 * We successively pick off - flags.
123 	 * If there is anything left, it is the base of the list
124 	 * of users to mail to.  Argp will be set to point to the
125 	 * first of these users.
126 	 */
127 	ef = NULL;
128 	to = NULL;
129 	cc = NULL;
130 	bcc = NULL;
131 	smopts = NULL;
132 	subject = NULL;
133 	Bflag = 0;
134 	while ((i = getopt(argc, argv, "~BEINT:b:c:dfins:u:v")) != -1)
135 	{
136 		switch (i) {
137 		case 'T':
138 			/*
139 			 * Next argument is temp file to write which
140 			 * articles have been read/deleted for netnews.
141 			 */
142 			Tflag = optarg;
143 			if ((i = creat(Tflag, 0600)) < 0) {
144 				warn("%s", Tflag);
145 				exit(1);
146 			}
147 			(void)close(i);
148 			break;
149 		case 'u':
150 			/*
151 			 * Next argument is person to pretend to be.
152 			 */
153 			myname = optarg;
154 			(void)unsetenv("MAIL");
155 			break;
156 		case 'i':
157 			/*
158 			 * User wants to ignore interrupts.
159 			 * Set the variable "ignore"
160 			 */
161 			assign("ignore", "");
162 			break;
163 		case 'd':
164 			debug++;
165 			break;
166 		case 's':
167 			/*
168 			 * Give a subject field for sending from
169 			 * non terminal
170 			 */
171 			subject = optarg;
172 			break;
173 		case 'f':
174 			/*
175 			 * User is specifying file to "edit" with Mail,
176 			 * as opposed to reading system mailbox.
177 			 * If no argument is given after -f, we read his
178 			 * mbox file.
179 			 *
180 			 * getopt() can't handle optional arguments, so here
181 			 * is an ugly hack to get around it.
182 			 */
183 			if ((argv[optind]) && (argv[optind][0] != '-'))
184 				ef = argv[optind++];
185 			else
186 				ef = "&";
187 			break;
188 		case 'n':
189 			/*
190 			 * User doesn't want to source /usr/lib/Mail.rc
191 			 */
192 			nosrc++;
193 			break;
194 		case 'N':
195 			/*
196 			 * Avoid initial header printing.
197 			 */
198 			assign("noheader", "");
199 			break;
200 		case 'v':
201 			/*
202 			 * Send mailer verbose flag
203 			 */
204 			assign("verbose", "");
205 			break;
206 		case 'I':
207 		case '~':
208 			/*
209 			 * We're interactive
210 			 */
211 			assign("interactive", "");
212 			break;
213 		case 'c':
214 			/*
215 			 * Get Carbon Copy Recipient list
216 			 */
217 			cc = cat(cc, lexpand(optarg, GCC));
218 			break;
219 		case 'b':
220 			/*
221 			 * Get Blind Carbon Copy Recipient list
222 			 */
223 			bcc = cat(bcc, lexpand(optarg, GBCC));
224 
225 			break;
226 		case 'B':
227 			/*
228 			 * Suppress the output of the "To:" line to allow
229 			 * sendmail to apply the NoRecipientAction option.
230 			 */
231 			Bflag = 1;
232 			break;
233 		case 'E':
234 			/*
235 			 * Don't send empty files.
236 			 */
237 			assign("dontsendempty", "");
238 			break;
239 		case '?':
240 			(void)fputs("\
241 Usage: mail [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
242             [- sendmail-options ...]\n\
243        mail [-EiInNv] -f [name]\n\
244        mail [-EiInNv] [-u user]\n",
245 				stderr);
246 			exit(1);
247 		}
248 	}
249 	for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
250 		to = cat(to, nalloc(argv[i], GTO));
251 	for (; argv[i]; i++)
252 		smopts = cat(smopts, nalloc(argv[i], GSMOPTS));
253 	/*
254 	 * Check for inconsistent arguments.
255 	 */
256 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
257 		errx(1, "You must specify direct recipients with -s, -c, or -b.");
258 	if (ef != NULL && to != NULL) {
259 		errx(1, "Cannot give -f and people to send to.");
260 	}
261 	tinit();
262 	setscreensize();
263 	input = stdin;
264 	rcvmode = !to;
265 	spreserve();
266 	if (!nosrc)
267 		load(_PATH_MASTER_RC);
268 	/*
269 	 * Expand returns a savestr, but load only uses the file name
270 	 * for fopen, so it's safe to do this.
271 	 */
272 	if ((rc = getenv("MAILRC")) == 0)
273 		rc = "~/.mailrc";
274 	load(expand(rc));
275 
276 #ifdef USE_READLINE
277 	/* this is after loading the MAILRC so we can use value() */
278 	init_readline();
279 #endif
280 
281 	if (!rcvmode) {
282 		(void)mail(to, cc, bcc, smopts, subject);
283 		/*
284 		 * why wait?
285 		 */
286 		exit(senderr);
287 	}
288 	/*
289 	 * Ok, we are reading mail.
290 	 * Decide whether we are editing a mailbox or reading
291 	 * the system mailbox, and open up the right stuff.
292 	 */
293 	if (ef == NULL)
294 		ef = "%";
295 	if (setfile(ef) < 0)
296 		exit(1);		/* error already reported */
297 	if (setjmp(hdrjmp) == 0) {
298 		if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
299 			(void)signal(SIGINT, hdrstop);
300 		if (value("quiet") == NULL)
301 			(void)printf("Mail version %s.  Type ? for help.\n",
302 				version);
303 		announce();
304 		(void)fflush(stdout);
305 		(void)signal(SIGINT, prevint);
306 	}
307 	commands();
308 	(void)signal(SIGHUP, SIG_IGN);
309 	(void)signal(SIGINT, SIG_IGN);
310 	(void)signal(SIGQUIT, SIG_IGN);
311 	quit();
312 	return 0;
313 }
314 
315 /*
316  * Interrupt printing of the headers.
317  */
318 void
319 /*ARGSUSED*/
320 hdrstop(int signo)
321 {
322 
323 	(void)fflush(stdout);
324 	(void)fprintf(stderr, "\nInterrupt\n");
325 	longjmp(hdrjmp, 1);
326 }
327 
328 /*
329  * Compute what the screen size for printing headers should be.
330  * We use the following algorithm for the height:
331  *	If baud rate < 1200, use  9
332  *	If baud rate = 1200, use 14
333  *	If baud rate > 1200, use 24 or ws_row
334  * Width is either 80 or ws_col;
335  */
336 void
337 setscreensize(void)
338 {
339 	struct termios tbuf;
340 	struct winsize ws;
341 	speed_t ospeed;
342 
343 	if (ioctl(1, TIOCGWINSZ, &ws) < 0)
344 		ws.ws_col = ws.ws_row = 0;
345 	if (tcgetattr(1, &tbuf) < 0)
346 		ospeed = 9600;
347 	else
348 		ospeed = cfgetospeed(&tbuf);
349 	if (ospeed < 1200)
350 		screenheight = 9;
351 	else if (ospeed == 1200)
352 		screenheight = 14;
353 	else if (ws.ws_row != 0)
354 		screenheight = ws.ws_row;
355 	else
356 		screenheight = 24;
357 	if ((realscreenheight = ws.ws_row) == 0)
358 		realscreenheight = 24;
359 	if ((screenwidth = ws.ws_col) == 0)
360 		screenwidth = 80;
361 }
362