Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/main.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2010 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* Startup -- interface with user.
73
*/
74
75
#include "mailx.h"
76
77
#if _PACKAGE_ast
78
79
#include "stamp.h"
80
81
static const char usage[] =
82
"[-?" STAMP "]"
83
USAGE_LICENSE
84
"[+NAME?mailx - send and receive mail]"
85
"[+DESCRIPTION?\bmailx\b is a mail processing command. The options place"
86
" \bmailx\b in either \asend\a or \areceive\a mode. Send mode composes"
87
" and sends messages from text on the standard input. Receive mode"
88
" provides both an interactive command language and non-interactive"
89
" command line actions. Receive mode, on startup, reads commands and"
90
" variable settings from the file \b$HOME/.mailrc\b, if it exists.]"
91
"[+?\bmailx\b provides commands for saving, deleting and responding to"
92
" messages. Message composition supports editing, reviewing and"
93
" other modifications as the message is entered.]"
94
"[+?Incoming mail is stored in one or more unspecified locations for each"
95
" user, collectively called the system mailbox for that user. When"
96
" \bmailx\b is invoked in \areceive\a mode, the system mailbox is"
97
" searched by default.]"
98
"[+?For additional help run \bmailx\b with no options or operands,"
99
" and then enter the \bhelp\b command at the interactive prompt.]"
100
"[A:articles?Treat mail folders as netnews article.]"
101
"[F:followup?Automatically include the previous message (followup) text"
102
" when composing a reply.]"
103
"[H:headers?List all headers and exit.]"
104
"[I:interactive?Force interactive receive mode.]"
105
"[N!:list-headers?List a screen of headers on receive mode startup.]"
106
"[P:pipe?Coprocess receive mode from a pipe.]"
107
"[Q:query?List the status character and sender address for the \acount\a"
108
" most recent messages, one line per message, and exit. See \b--status\b"
109
" for message status characters details.]:[count]"
110
"[S:status?List the status character and sender address for all messages,"
111
" one line per message, and exit. The message status characters are:]{"
112
" [M?To be saved in \bmbox\b and marked for delete on exit.]"
113
" [N?New message.]"
114
" [P?Preserved and will not be deleted.]"
115
" [R?Already read.]"
116
" [U?Unread message from previous session.]"
117
" [X?Possible spam.]"
118
" [\b*\b?Saved to a folder and marked for delete on exit.]"
119
"}"
120
"[T:oldnews?Read/deleted netnews article names are appended to \afile\a]:[file]"
121
"[b:bcc?Prompt for the blind carbon copy recipient list when composing"
122
" messages.]"
123
"[c:cc?Prompt for the carbon copy recipient list when composing messages.]"
124
"[d:debug?Enable implementation specific debug output.]"
125
"[e:check?Silently exit 0 if there is mail, 1 otherwise.]"
126
"[f:folder?Mail is read from \afolder\a instead of the default"
127
" user mailbox.]:?[folder:=$HOME/mbox]"
128
"[i:ignore-interrupts?Ignore interrupts.]"
129
"[n!:master?Read commands from \b/etc/mailx.rc\b on receive mode startup.]"
130
"[o:set?Set \aname\a=\avalue\a options. The interactive mail command"
131
" \bhelp set all\b lists details for each option.]:[name=value]"
132
"[r:address?Set the reply to header address to \aaddress\a.]:[address]"
133
"[s:subject?The non-interactive send mode subject text.]:[text]"
134
"[t:sendheaders?Check for headers in send mode message text.]"
135
"[u:user?Pretend to be this \buser\b in send mode. For debugging.]:[user]"
136
"[v:verbose?Enable implementation specific send mode verbose trace.]"
137
"\n"
138
"\n[ address ... ]\n"
139
"\n"
140
"[+SEE ALSO?\b/bin/mail\b(1), \bMail\b(1)]"
141
;
142
143
#undef optarg
144
#define optarg opt_info.arg
145
#undef optnum
146
#define optnum opt_info.num
147
#undef optind
148
#define optind opt_info.index
149
150
#undef getopt
151
#define getopt(c,v,u) optget(v,u)
152
153
#else
154
155
static const char usage[] = "AFHINPQ:ST:b:c:defino:r:s:tu:v";
156
157
#endif
158
159
/*
160
* Interrupt printing of the headers.
161
*/
162
static void
163
hdrstop(int sig)
164
{
165
note(0, "\nInterrupt");
166
longjmp(state.jump.header, sig);
167
}
168
169
/*
170
* Set command line options and append to
171
* op list for resetopt() after the rc's.
172
*/
173
static struct list*
174
setopt(register struct list* op, char* s, char* v)
175
{
176
int n;
177
struct argvec vec;
178
179
n = strlen(s) + 1;
180
if (v)
181
n += strlen(v) + 1;
182
if (!(op->next = newof(0, struct list, 1, n)))
183
note(PANIC, "Out of space");
184
op = op->next;
185
s = strcopy(op->name, s);
186
if (v) {
187
*s++ = '=';
188
strcpy(s, v);
189
}
190
state.onstack++;
191
initargs(&vec);
192
getargs(&vec, op->name);
193
if (endargs(&vec) > 0) {
194
state.cmdline = 1;
195
set(vec.argv);
196
state.cmdline = 0;
197
}
198
sreset();
199
state.onstack--;
200
return op;
201
}
202
203
/*
204
* Reset the setopt() options after the rc's.
205
*/
206
static void
207
resetopt(register struct list* op)
208
{
209
register struct list* np;
210
struct argvec vec;
211
212
np = op->next;
213
while (op = np) {
214
initargs(&vec);
215
getargs(&vec, op->name);
216
if (endargs(&vec) > 0) {
217
state.cmdline = 1;
218
set(vec.argv);
219
state.cmdline = 0;
220
}
221
sreset();
222
np = op->next;
223
free(op);
224
}
225
}
226
227
int
228
main(int argc, char** argv)
229
{
230
register int i;
231
int sig;
232
char* ef;
233
int flags = 0;
234
sig_t prevint;
235
struct header head;
236
struct list options;
237
struct list* op;
238
#if _PACKAGE_ast
239
int fatal = 0;
240
#endif
241
242
#if _PACKAGE_ast
243
error_info.id = "mailx";
244
#endif
245
246
/*
247
* Set up a reasonable environment.
248
* Figure out whether we are being run interactively,
249
* and so forth.
250
*/
251
memset(&head, 0, sizeof(head));
252
(op = &options)->next = 0;
253
if (!(state.path.buf = sfstropen()) || !(state.path.move = sfstropen()) || !(state.path.part = sfstropen()) || !(state.path.temp = sfstropen()))
254
note(FATAL, "out of space");
255
varinit();
256
/*
257
* Now, determine how we are being used.
258
* We successively pick off - flags.
259
* If there is anything left, it is the base of the list
260
* of users to mail to. Argp will be set to point to the
261
* first of these users.
262
*/
263
ef = 0;
264
opterr = 0;
265
for (;;) {
266
switch (getopt(argc, argv, usage)) {
267
case 0:
268
case EOF:
269
break;
270
case 'A':
271
op = setopt(op, "news", NiL);
272
continue;
273
case 'F':
274
flags |= FOLLOWUP;
275
continue;
276
case 'H':
277
/*
278
* List all headers and exit.
279
*/
280
op = setopt(op, "justheaders", NiL);
281
state.var.quiet = state.on;
282
continue;
283
case 'I':
284
/*
285
* We're interactive
286
*/
287
op = setopt(op, "interactive", NiL);
288
continue;
289
case 'N':
290
/*
291
* Avoid initial header printing.
292
*/
293
op = setopt(op, "noheader", NiL);
294
state.var.quiet = state.on;
295
continue;
296
case 'P':
297
/*
298
* Coprocess on pipe.
299
*/
300
op = setopt(op, "coprocess", NiL);
301
continue;
302
case 'Q':
303
/*
304
* List all n most recent status and senders and exit.
305
*/
306
op = setopt(op, "justfrom", optarg);
307
state.var.quiet = state.on;
308
continue;
309
case 'S':
310
/*
311
* List all status and senders and exit.
312
*/
313
op = setopt(op, "justfrom", "-1");
314
state.var.quiet = state.on;
315
continue;
316
case 'T':
317
/*
318
* Next argument is temp file to write which
319
* articles have been read/deleted for netnews.
320
*/
321
op = setopt(op, "news", optarg);
322
continue;
323
case 'b':
324
/*
325
* Get Blind Carbon Copy Recipient list
326
*/
327
extract(&head, GBCC|GMETOO, optarg);
328
continue;
329
case 'c':
330
/*
331
* Get Carbon Copy Recipient list
332
*/
333
extract(&head, GCC|GMETOO, optarg);
334
continue;
335
case 'd':
336
/*
337
* Debug output.
338
*/
339
op = setopt(op, "debug", NiL);
340
continue;
341
case 'e':
342
/*
343
* Silently exit 0 if mail, 1, otherwise.
344
*/
345
op = setopt(op, "justcheck", NiL);
346
state.var.quiet = state.on;
347
continue;
348
case 'f':
349
#if _PACKAGE_ast
350
if (!(ef = opt_info.arg))
351
ef = "&";
352
#else
353
/*
354
* User is specifying file to "edit" with Mail,
355
* as opposed to reading system mailbox.
356
* If no argument is given after -f, we read his
357
* mbox file.
358
*
359
* getopt() can't handle optional arguments, so here
360
* is an ugly hack to get around it.
361
*/
362
if (argv[optind] && argv[optind][0] != '-')
363
ef = argv[optind++];
364
else
365
ef = "&";
366
#endif
367
continue;
368
case 'i':
369
/*
370
* User wants to ignore interrupts.
371
* Set the variable "ignore"
372
*/
373
op = setopt(op, "ignore", NiL);
374
continue;
375
case 'n':
376
/*
377
* User doesn't want to source state.var.master
378
*/
379
op = setopt(op, "nomaster", NiL);
380
continue;
381
case 'o':
382
/*
383
* Set option(s) by name.
384
*/
385
op = setopt(op, optarg, NiL);
386
continue;
387
case 'r':
388
/*
389
* Set replyto.
390
*/
391
{
392
char* s;
393
int n;
394
395
static const char h[] = "fixedheaders=Reply-To:\" \"";
396
397
n = strlen(optarg);
398
if (!(s = newof(0, char, n + sizeof(h) + 1, 0)))
399
note(PANIC, "Out of space");
400
memcpy(s, h, sizeof(h) - 1);
401
memcpy(s + sizeof(h) - 1, optarg, n);
402
op = setopt(op, s, NiL);
403
}
404
continue;
405
case 's':
406
/*
407
* Give a subject field for sending from
408
* non terminal
409
*/
410
if (head.h_subject = optarg)
411
head.h_flags |= GSUB;
412
continue;
413
case 't':
414
/*
415
* Check for headers in message text.
416
*/
417
op = setopt(op, "sendheaders", optarg);
418
state.mode = SEND;
419
continue;
420
case 'u':
421
/*
422
* Next argument is person to pretend to be.
423
*/
424
op = setopt(op, "user", optarg);
425
continue;
426
case 'v':
427
/*
428
* Send mailer verbose flag
429
*/
430
op = setopt(op, "verbose", NiL);
431
continue;
432
#if _PACKAGE_ast
433
case '?':
434
error(ERROR_USAGE|4, "%s", opt_info.arg);
435
break;
436
case ':':
437
error(2, "%s", opt_info.arg);
438
fatal = 1;
439
break;
440
#else
441
case '?':
442
note(FATAL, "\
443
Usage: mail [-o [no]name[=value]] [-s subject] [-c cc] [-b bcc] to ...\n\
444
mail [-o [no]name[=value]] [-f [folder]]");
445
break;
446
#endif
447
}
448
break;
449
}
450
#if _PACKAGE_ast
451
if (fatal)
452
error(ERROR_USAGE|4, "%s", optusage(NiL));
453
#endif
454
for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
455
extract(&head, GTO|GMETOO, argv[i]);
456
if (argv[i])
457
head.h_options = argv;
458
if (!state.mode)
459
state.mode = (head.h_flags & GTO) ? SEND : RECEIVE;
460
/*
461
* Check for inconsistent arguments.
462
*/
463
if (state.mode == RECEIVE && (head.h_flags & GSTD)) {
464
if (!state.var.sendheaders)
465
note(FATAL|IDENTIFY, "You must specify direct recipients with -s, -c, or -b");
466
state.mode = SEND;
467
}
468
if (state.mode == RECEIVE)
469
state.var.receive = state.on;
470
if (state.mode == SEND && ef)
471
note(FATAL|IDENTIFY, "Cannot give -f and people to send to");
472
if (state.var.justcheck && state.mode == SEND)
473
exit(1);
474
tempinit();
475
state.input = stdin;
476
/*
477
* Up to this point salloc()==malloc() by default.
478
* From now on salloc() space cleared by sreset().
479
*/
480
state.onstack = 1;
481
if (state.var.master)
482
load(expand(state.var.master, 1));
483
/*
484
* Expand returns a savestr, but load only uses the file name
485
* for fopen, so it's safe to do this.
486
*/
487
load(expand(state.var.mailrc, 1));
488
/*
489
* Reset command line options so they take precedence over the rc's.
490
*/
491
resetopt(&options);
492
if (state.mode == SEND) {
493
sendmail(&head, flags);
494
/*
495
* why wait?
496
*/
497
exit(state.senderr);
498
}
499
/*
500
* Ok, we are reading mail.
501
* Decide whether we are editing a mailbox or reading
502
* the system mailbox, and open up the right stuff.
503
*/
504
if (!ef)
505
ef = "%";
506
if (setfolder(ef) < 0)
507
exit(1);
508
if (sig = setjmp(state.jump.header))
509
resume(sig);
510
else {
511
if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
512
signal(SIGINT, hdrstop);
513
if (!state.var.quiet)
514
note(0, "Mail version %s. Type ? for help", state.version);
515
announce();
516
fflush(stdout);
517
signal(SIGINT, prevint);
518
}
519
if (!state.var.justheaders) {
520
commands();
521
signal(SIGHUP, SIG_IGN);
522
signal(SIGINT, SIG_IGN);
523
signal(SIGQUIT, SIG_IGN);
524
quit();
525
}
526
exit(0);
527
}
528
529