1 /* $NetBSD: cmd2.c,v 1.23 2007/10/27 15:14:50 christos Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93";
37 __RCSID("$NetBSD: cmd2.c,v 1.23 2007/10/27 15:14:50 christos Exp $");
50 * Mail -- a mail program
56 * If any arguments were given, go to the next applicable argument
57 * following dot, otherwise, go to the next applicable message.
58 * If given as first command with no arguments, print first message.
72 * If some messages were supplied, find the
73 * first applicable one following dot using
76 mdot
= get_msgnum(dot
);
79 * Find the first message in the supplied
80 * message list which follows dot.
83 for (ip
= msgvec
; *ip
!= 0; ip
++)
90 mp
= get_message(*ip2
);
91 if ((mp
->m_flag
& MDELETED
) == 0) {
100 (void)printf("No messages applicable\n");
105 * If this is the first command, select message 1.
106 * Note that this must exist for us to get here at all.
113 * Just find the next good message after dot, no
117 for (mp
= next_message(dot
); mp
; mp
= next_message(mp
))
118 if ((mp
->m_flag
& (MDELETED
|MSAVED
)) == 0)
122 (void)printf("At EOF\n");
131 list
[0] = get_msgnum(dot
);
137 * Snarf the file from the end of the command line and
138 * return a pointer to it. If there is no file attached,
139 * just return NULL. Put a null in front of the file
140 * name so that the message list processing won't see it,
141 * unless the file name is the only thing on the line, in
142 * which case, return 0 in the reference flag variable.
145 snarf(char linebuf
[], int *flag
, const char *string
)
150 cp
= strlen(linebuf
) + linebuf
- 1;
153 * Strip away trailing blanks.
155 while (cp
>= linebuf
&& isspace((unsigned char)*cp
))
160 * Now search for the beginning of the file name.
162 while (cp
> linebuf
&& !isspace((unsigned char)*cp
))
165 (void)printf("No %s specified.\n", string
);
168 if (isspace((unsigned char)*cp
))
175 struct save1_core_args_s
{
177 struct ignoretab
*igtab
;
181 save1_core(struct message
*mp
, void *v
)
183 struct save1_core_args_s
*args
;
188 if (sendmessage(mp
, args
->obuf
, args
->igtab
, NULL
, NULL
) < 0)
192 mp
->m_flag
|= MSAVED
;
198 * Save/copy the indicated messages at the end of the passed file name.
199 * If markmsg is true, mark the message "saved."
202 save1(char str
[], int markmsg
, const char *cmd
, struct ignoretab
*igtab
)
211 msgCount
= get_msgCount();
212 msgvec
= salloc((msgCount
+ 2) * sizeof(*msgvec
));
213 if ((fn
= snarf(str
, &f
, "file")) == NULL
)
216 *msgvec
= first(0, MMNORM
);
218 (void)printf("No messages to %s.\n", cmd
);
223 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
225 if ((fn
= expand(fn
)) == NULL
)
227 (void)printf("\"%s\" ", fn
);
228 (void)fflush(stdout
);
229 if (access(fn
, 0) >= 0)
233 if ((obuf
= Fopen(fn
, "a")) == NULL
) {
237 for (ip
= msgvec
; *ip
&& ip
- msgvec
< msgCount
; ip
++) {
238 struct save1_core_args_s args
;
243 args
.markmsg
= markmsg
;
244 mp
= get_message(*ip
);
245 if (thread_recursion(mp
, save1_core
, &args
)) {
255 (void)printf("%s\n", disp
);
260 * Save a message in a file. Mark the message as saved
261 * so we can discard when the user quits.
269 return save1(str
, 1, "save", saveignore
);
273 * Save a message in a file. Mark the message as saved
274 * so we can discard when the user quits. Save all fields
275 * overriding saveignore and saveretain.
283 return save1(str
, 1, "Save", NULL
);
287 * Copy a message to a file without affected its saved-ness
295 return save1(str
, 0, "copy", saveignore
);
299 * Write the indicated messages at the end of the passed
300 * file name, minus header and trailing blank line.
308 return save1(str
, 1, "write", ignoreall
);
312 * Delete the indicated messages.
313 * Set dot to some nice place afterwards.
314 * Internal interface.
324 for (ip
= msgvec
; *ip
!= 0; ip
++) {
326 ~(MPRESERVE
|MSAVED
|MBOX
|MDELETED
|MTOUCH
), MDELETED
|MTOUCH
);
331 dot
= get_message(last
);
332 last
= first(0, MDELETED
);
334 dot
= get_message(last
);
338 dot
= get_message(1);
344 * Following can't happen -- it keeps lint happy
363 * Delete messages, then type the new dot.
373 lastdot
= get_msgnum(dot
);
374 if (delm(msgvec
) >= 0) {
375 list
[0] = get_msgnum(dot
);
376 if (list
[0] > lastdot
) {
381 (void)printf("At EOF\n");
383 (void)printf("No more messages\n");
388 * Undelete the indicated messages.
398 msgCount
= get_msgCount();
399 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
400 dot
= set_m_flag(*ip
, ~MDELETED
, 0);
402 dot
->m_flag
&= ~MDELETED
;
407 /*************************************************************************/
410 * Interactively dump core on "core"
414 core(void *v __unused
)
418 switch (pid
= vfork()) {
426 (void)printf("Okie dokie");
427 (void)fflush(stdout
);
428 (void)wait_child(pid
);
429 if (WCOREDUMP(wait_status
))
430 (void)printf(" -- Core dumped.\n");
432 (void)printf(" -- Can't dump core.\n");
447 for (cp
= buf
; cp
< &buf
[512]; *cp
++ = (char)0xFF)
453 * Clobber as many bytes of stack as the user requests.
465 times
= (atoi(argv
[0]) + 511) / 512;
471 * Compare two names for sorting ignored field list.
474 igcomp(const void *l
, const void *r
)
476 return strcmp(*(const char *const *)l
, *(const char *const *)r
);
480 * Print out all currently retained fields.
483 igshow(struct ignoretab
*tab
, const char *which
)
489 if (tab
->i_count
== 0) {
490 (void)printf("No fields currently being %s.\n", which
);
493 ring
= salloc((tab
->i_count
+ 1) * sizeof(char *));
495 for (h
= 0; h
< HSHSIZE
; h
++)
496 for (igp
= tab
->i_head
[h
]; igp
!= 0; igp
= igp
->i_link
)
497 *ap
++ = igp
->i_field
;
499 qsort(ring
, tab
->i_count
, sizeof(char *), igcomp
);
500 for (ap
= ring
; *ap
!= 0; ap
++)
501 (void)printf("%s\n", *ap
);
506 * core ignore routine.
509 ignore1(char *list
[], struct ignoretab
*tab
, const char *which
)
514 return igshow(tab
, which
);
516 for (ap
= list
; *ap
!= 0; ap
++)
517 add_ignore(*ap
, tab
);
523 * Add the given header fields to the retained list.
524 * If no arguments, print the current list of retained fields.
532 return ignore1(list
, ignore
+ 1, "retained");
536 * Add the given header fields to the ignored list.
537 * If no arguments, print the current list of ignored fields.
545 return ignore1(list
, ignore
, "ignored");
549 * Add the given header fields to the save retained list.
550 * If no arguments, print the current list of save retained fields.
553 saveretfield(void *v
)
558 return ignore1(list
, saveignore
+ 1, "retained");
562 * Add the given header fields to the save ignored list.
563 * If no arguments, print the current list of save ignored fields.
571 return ignore1(list
, saveignore
, "ignored");
577 check_dirname(char *filename
)
581 char canon_buf
[MAXPATHLEN
];
584 canon_name
= canon_buf
;
586 if (fname
[0] == '~' && fname
[1] == '/') {
587 if (homedir
&& homedir
[0] != '~')
588 (void)easprintf(&fname
, "%s/%s",
591 if (realpath(fname
, canon_name
) == NULL
) {
592 warn("realpath: %s", filename
);
596 if (stat(canon_name
, &sb
) == -1) {
597 warn("stat: %s", canon_name
);
601 if (!S_ISDIR(sb
.st_mode
)) {
602 warnx("stat: %s is not a directory", canon_name
);
606 if (access(canon_name
, W_OK
|X_OK
) == -1) {
607 warnx("access: %s is not writable", canon_name
);
612 if (fname
!= filename
)
615 return canon_name
? savestr(canon_name
) : NULL
;
618 struct detach1_core_args_s
{
619 struct message
*parent
;
620 struct ignoretab
*igtab
;
624 detach1_core(struct message
*mp
, void *v
)
626 struct mime_info
*mip
;
627 struct detach1_core_args_s
*args
;
631 show_msgnum(stdout
, mp
, args
->parent
);
632 mip
= mime_decode_open(mp
);
633 mime_detach_msgnum(mip
, sget_msgnum(mp
, args
->parent
));
634 (void)mime_sendmessage(mp
, NULL
, args
->igtab
, args
->dstdir
, mip
);
635 mime_decode_close(mip
);
640 * detach attachments.
643 detach1(void *v
, int do_unnamed
)
656 * Get the destination directory.
658 if ((dstdir
= snarf(str
, &f
, "directory")) == NULL
&&
659 (dstdir
= value(ENAME_MIME_DETACH_DIR
)) == NULL
&&
660 (dstdir
= origdir
) == NULL
)
663 if ((dstdir
= check_dirname(dstdir
)) == NULL
)
667 * Setup the message list.
669 msgCount
= get_msgCount();
670 msgvec
= salloc((msgCount
+ 2) * sizeof(*msgvec
));
672 *msgvec
= first(0, MMNORM
);
674 (void)printf("No messages to detach.\n");
679 if (f
&& getmsglist(str
, msgvec
, 0) < 0)
682 if (mime_detach_control() != 0)
686 * do 'dot' if nothing else was selected.
688 if (msgvec
[0] == 0 && dot
!= NULL
) {
689 msgvec
[0] = get_msgnum(dot
);
692 recursive
= do_recursion();
693 for (ip
= msgvec
; *ip
&& ip
- msgvec
< msgCount
; ip
++) {
694 struct detach1_core_args_s args
;
697 mp
= get_message(*ip
);
699 args
.parent
= recursive
? mp
: NULL
;
700 args
.igtab
= do_unnamed
? detachall
: ignoreall
;
701 args
.dstdir
= dstdir
;
702 (void)thread_recursion(mp
, detach1_core
, &args
);
708 * detach named attachments.
714 return detach1(v
, 0);
718 * detach all attachments.
724 return detach1(v
, 1);
726 #endif /* MIME_SUPPORT */