cmd2.c revision 1.26 1 1.26 christos /* $NetBSD: cmd2.c,v 1.26 2017/11/09 20:27:50 christos Exp $ */
2 1.5 christos
3 1.1 cgd /*
4 1.3 deraadt * Copyright (c) 1980, 1993
5 1.3 deraadt * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.17 agc * 3. Neither the name of the University nor the names of its contributors
16 1.1 cgd * may be used to endorse or promote products derived from this software
17 1.1 cgd * without specific prior written permission.
18 1.1 cgd *
19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cgd * SUCH DAMAGE.
30 1.1 cgd */
31 1.1 cgd
32 1.8 lukem #include <sys/cdefs.h>
33 1.1 cgd #ifndef lint
34 1.5 christos #if 0
35 1.5 christos static char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93";
36 1.5 christos #else
37 1.26 christos __RCSID("$NetBSD: cmd2.c,v 1.26 2017/11/09 20:27:50 christos Exp $");
38 1.5 christos #endif
39 1.1 cgd #endif /* not lint */
40 1.1 cgd
41 1.1 cgd #include "rcv.h"
42 1.20 christos #include <util.h>
43 1.3 deraadt #include "extern.h"
44 1.22 christos #ifdef MIME_SUPPORT
45 1.22 christos #include "mime.h"
46 1.22 christos #endif
47 1.22 christos #include "thread.h"
48 1.1 cgd
49 1.1 cgd /*
50 1.1 cgd * Mail -- a mail program
51 1.1 cgd *
52 1.1 cgd * More user commands.
53 1.1 cgd */
54 1.1 cgd
55 1.1 cgd /*
56 1.1 cgd * If any arguments were given, go to the next applicable argument
57 1.1 cgd * following dot, otherwise, go to the next applicable message.
58 1.1 cgd * If given as first command with no arguments, print first message.
59 1.1 cgd */
60 1.22 christos PUBLIC int
61 1.12 wiz next(void *v)
62 1.1 cgd {
63 1.24 christos int *msgvec;
64 1.8 lukem struct message *mp;
65 1.8 lukem int *ip, *ip2;
66 1.1 cgd int list[2], mdot;
67 1.1 cgd
68 1.24 christos msgvec = v;
69 1.7 pk if (*msgvec != 0) {
70 1.1 cgd
71 1.1 cgd /*
72 1.7 pk * If some messages were supplied, find the
73 1.1 cgd * first applicable one following dot using
74 1.1 cgd * wrap around.
75 1.1 cgd */
76 1.22 christos mdot = get_msgnum(dot);
77 1.1 cgd
78 1.1 cgd /*
79 1.1 cgd * Find the first message in the supplied
80 1.1 cgd * message list which follows dot.
81 1.1 cgd */
82 1.1 cgd
83 1.7 pk for (ip = msgvec; *ip != 0; ip++)
84 1.1 cgd if (*ip > mdot)
85 1.1 cgd break;
86 1.7 pk if (*ip == 0)
87 1.1 cgd ip = msgvec;
88 1.1 cgd ip2 = ip;
89 1.1 cgd do {
90 1.22 christos mp = get_message(*ip2);
91 1.1 cgd if ((mp->m_flag & MDELETED) == 0) {
92 1.1 cgd dot = mp;
93 1.1 cgd goto hitit;
94 1.1 cgd }
95 1.7 pk if (*ip2 != 0)
96 1.1 cgd ip2++;
97 1.7 pk if (*ip2 == 0)
98 1.1 cgd ip2 = msgvec;
99 1.1 cgd } while (ip2 != ip);
100 1.19 christos (void)printf("No messages applicable\n");
101 1.22 christos return 1;
102 1.1 cgd }
103 1.1 cgd
104 1.1 cgd /*
105 1.1 cgd * If this is the first command, select message 1.
106 1.1 cgd * Note that this must exist for us to get here at all.
107 1.1 cgd */
108 1.1 cgd
109 1.1 cgd if (!sawcom)
110 1.1 cgd goto hitit;
111 1.1 cgd
112 1.1 cgd /*
113 1.1 cgd * Just find the next good message after dot, no
114 1.1 cgd * wraparound.
115 1.1 cgd */
116 1.1 cgd
117 1.22 christos for (mp = next_message(dot); mp; mp = next_message(mp))
118 1.1 cgd if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
119 1.1 cgd break;
120 1.22 christos
121 1.22 christos if (mp == NULL) {
122 1.19 christos (void)printf("At EOF\n");
123 1.22 christos return 0;
124 1.1 cgd }
125 1.1 cgd dot = mp;
126 1.1 cgd hitit:
127 1.1 cgd /*
128 1.1 cgd * Print dot.
129 1.1 cgd */
130 1.1 cgd
131 1.22 christos list[0] = get_msgnum(dot);
132 1.7 pk list[1] = 0;
133 1.22 christos return type(list);
134 1.1 cgd }
135 1.1 cgd
136 1.1 cgd /*
137 1.22 christos * Snarf the file from the end of the command line and
138 1.22 christos * return a pointer to it. If there is no file attached,
139 1.22 christos * just return NULL. Put a null in front of the file
140 1.22 christos * name so that the message list processing won't see it,
141 1.22 christos * unless the file name is the only thing on the line, in
142 1.22 christos * which case, return 0 in the reference flag variable.
143 1.1 cgd */
144 1.22 christos static char *
145 1.22 christos snarf(char linebuf[], int *flag, const char *string)
146 1.1 cgd {
147 1.22 christos char *cp;
148 1.22 christos
149 1.22 christos *flag = 1;
150 1.22 christos cp = strlen(linebuf) + linebuf - 1;
151 1.22 christos
152 1.22 christos /*
153 1.22 christos * Strip away trailing blanks.
154 1.22 christos */
155 1.24 christos while (cp >= linebuf && isspace((unsigned char)*cp))
156 1.22 christos cp--;
157 1.24 christos *++cp = '\0';
158 1.22 christos
159 1.22 christos /*
160 1.22 christos * Now search for the beginning of the file name.
161 1.22 christos */
162 1.22 christos while (cp > linebuf && !isspace((unsigned char)*cp))
163 1.22 christos cp--;
164 1.22 christos if (*cp == '\0') {
165 1.22 christos (void)printf("No %s specified.\n", string);
166 1.22 christos return NULL;
167 1.22 christos }
168 1.22 christos if (isspace((unsigned char)*cp))
169 1.24 christos *cp++ = '\0';
170 1.22 christos else
171 1.22 christos *flag = 0;
172 1.22 christos return cp;
173 1.16 ross }
174 1.16 ross
175 1.22 christos struct save1_core_args_s {
176 1.22 christos FILE *obuf;
177 1.22 christos struct ignoretab *igtab;
178 1.22 christos int markmsg;
179 1.22 christos };
180 1.22 christos static int
181 1.22 christos save1_core(struct message *mp, void *v)
182 1.16 ross {
183 1.22 christos struct save1_core_args_s *args;
184 1.24 christos
185 1.22 christos args = v;
186 1.22 christos touch(mp);
187 1.16 ross
188 1.22 christos if (sendmessage(mp, args->obuf, args->igtab, NULL, NULL) < 0)
189 1.22 christos return -1;
190 1.1 cgd
191 1.22 christos if (args->markmsg)
192 1.22 christos mp->m_flag |= MSAVED;
193 1.1 cgd
194 1.22 christos return 0;
195 1.1 cgd }
196 1.1 cgd
197 1.1 cgd /*
198 1.1 cgd * Save/copy the indicated messages at the end of the passed file name.
199 1.13 wiz * If markmsg is true, mark the message "saved."
200 1.1 cgd */
201 1.22 christos static int
202 1.22 christos save1(char str[], int markmsg, const char *cmd, struct ignoretab *igtab)
203 1.1 cgd {
204 1.8 lukem int *ip;
205 1.18 christos const char *fn;
206 1.18 christos const char *disp;
207 1.1 cgd int f, *msgvec;
208 1.22 christos int msgCount;
209 1.1 cgd FILE *obuf;
210 1.1 cgd
211 1.22 christos msgCount = get_msgCount();
212 1.23 christos msgvec = salloc((msgCount + 2) * sizeof(*msgvec));
213 1.22 christos if ((fn = snarf(str, &f, "file")) == NULL)
214 1.22 christos return 1;
215 1.1 cgd if (!f) {
216 1.1 cgd *msgvec = first(0, MMNORM);
217 1.7 pk if (*msgvec == 0) {
218 1.19 christos (void)printf("No messages to %s.\n", cmd);
219 1.22 christos return 1;
220 1.1 cgd }
221 1.7 pk msgvec[1] = 0;
222 1.1 cgd }
223 1.1 cgd if (f && getmsglist(str, msgvec, 0) < 0)
224 1.22 christos return 1;
225 1.14 wiz if ((fn = expand(fn)) == NULL)
226 1.22 christos return 1;
227 1.19 christos (void)printf("\"%s\" ", fn);
228 1.19 christos (void)fflush(stdout);
229 1.13 wiz if (access(fn, 0) >= 0)
230 1.1 cgd disp = "[Appended]";
231 1.1 cgd else
232 1.1 cgd disp = "[New file]";
233 1.26 christos if ((obuf = Fopen(fn, "aef")) == NULL) {
234 1.15 wiz warn(NULL);
235 1.22 christos return 1;
236 1.1 cgd }
237 1.22 christos for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
238 1.22 christos struct save1_core_args_s args;
239 1.22 christos struct message *mp;
240 1.22 christos
241 1.22 christos args.obuf = obuf;
242 1.22 christos args.igtab = igtab;
243 1.22 christos args.markmsg = markmsg;
244 1.22 christos mp = get_message(*ip);
245 1.22 christos if (thread_recursion(mp, save1_core, &args)) {
246 1.15 wiz warn("%s", fn);
247 1.19 christos (void)Fclose(obuf);
248 1.22 christos return 1;
249 1.1 cgd }
250 1.1 cgd }
251 1.19 christos (void)fflush(obuf);
252 1.1 cgd if (ferror(obuf))
253 1.15 wiz warn("%s", fn);
254 1.19 christos (void)Fclose(obuf);
255 1.19 christos (void)printf("%s\n", disp);
256 1.22 christos return 0;
257 1.1 cgd }
258 1.1 cgd
259 1.1 cgd /*
260 1.22 christos * Save a message in a file. Mark the message as saved
261 1.22 christos * so we can discard when the user quits.
262 1.1 cgd */
263 1.22 christos PUBLIC int
264 1.22 christos save(void *v)
265 1.1 cgd {
266 1.24 christos char *str;
267 1.1 cgd
268 1.24 christos str = v;
269 1.22 christos return save1(str, 1, "save", saveignore);
270 1.1 cgd }
271 1.1 cgd
272 1.1 cgd /*
273 1.22 christos * Save a message in a file. Mark the message as saved
274 1.22 christos * so we can discard when the user quits. Save all fields
275 1.22 christos * overriding saveignore and saveretain.
276 1.1 cgd */
277 1.22 christos PUBLIC int
278 1.22 christos Save(void *v)
279 1.1 cgd {
280 1.24 christos char *str;
281 1.1 cgd
282 1.24 christos str = v;
283 1.22 christos return save1(str, 1, "Save", NULL);
284 1.1 cgd }
285 1.1 cgd
286 1.1 cgd /*
287 1.22 christos * Copy a message to a file without affected its saved-ness
288 1.1 cgd */
289 1.22 christos PUBLIC int
290 1.22 christos copycmd(void *v)
291 1.1 cgd {
292 1.24 christos char *str;
293 1.22 christos
294 1.24 christos str = v;
295 1.22 christos return save1(str, 0, "copy", saveignore);
296 1.1 cgd }
297 1.1 cgd
298 1.1 cgd /*
299 1.22 christos * Write the indicated messages at the end of the passed
300 1.22 christos * file name, minus header and trailing blank line.
301 1.1 cgd */
302 1.22 christos PUBLIC int
303 1.22 christos swrite(void *v)
304 1.1 cgd {
305 1.24 christos char *str;
306 1.1 cgd
307 1.24 christos str = v;
308 1.22 christos return save1(str, 1, "write", ignoreall);
309 1.1 cgd }
310 1.1 cgd
311 1.1 cgd /*
312 1.1 cgd * Delete the indicated messages.
313 1.1 cgd * Set dot to some nice place afterwards.
314 1.1 cgd * Internal interface.
315 1.1 cgd */
316 1.22 christos static int
317 1.12 wiz delm(int *msgvec)
318 1.1 cgd {
319 1.8 lukem struct message *mp;
320 1.8 lukem int *ip;
321 1.1 cgd int last;
322 1.1 cgd
323 1.7 pk last = 0;
324 1.7 pk for (ip = msgvec; *ip != 0; ip++) {
325 1.22 christos mp = set_m_flag(*ip,
326 1.22 christos ~(MPRESERVE|MSAVED|MBOX|MDELETED|MTOUCH), MDELETED|MTOUCH);
327 1.1 cgd touch(mp);
328 1.1 cgd last = *ip;
329 1.1 cgd }
330 1.7 pk if (last != 0) {
331 1.22 christos dot = get_message(last);
332 1.1 cgd last = first(0, MDELETED);
333 1.7 pk if (last != 0) {
334 1.22 christos dot = get_message(last);
335 1.22 christos return 0;
336 1.1 cgd }
337 1.1 cgd else {
338 1.22 christos dot = get_message(1);
339 1.22 christos return -1;
340 1.1 cgd }
341 1.1 cgd }
342 1.1 cgd
343 1.1 cgd /*
344 1.1 cgd * Following can't happen -- it keeps lint happy
345 1.1 cgd */
346 1.22 christos return -1;
347 1.22 christos }
348 1.1 cgd
349 1.22 christos /*
350 1.22 christos * Delete messages.
351 1.22 christos */
352 1.22 christos PUBLIC int
353 1.22 christos delete(void *v)
354 1.22 christos {
355 1.24 christos int *msgvec;
356 1.24 christos
357 1.24 christos msgvec = v;
358 1.22 christos (void)delm(msgvec);
359 1.22 christos return 0;
360 1.22 christos }
361 1.22 christos
362 1.22 christos /*
363 1.22 christos * Delete messages, then type the new dot.
364 1.22 christos */
365 1.22 christos PUBLIC int
366 1.22 christos deltype(void *v)
367 1.22 christos {
368 1.24 christos int *msgvec;
369 1.22 christos int list[2];
370 1.22 christos int lastdot;
371 1.22 christos
372 1.24 christos msgvec = v;
373 1.22 christos lastdot = get_msgnum(dot);
374 1.22 christos if (delm(msgvec) >= 0) {
375 1.22 christos list[0] = get_msgnum(dot);
376 1.22 christos if (list[0] > lastdot) {
377 1.22 christos touch(dot);
378 1.22 christos list[1] = 0;
379 1.22 christos return type(list);
380 1.22 christos }
381 1.22 christos (void)printf("At EOF\n");
382 1.22 christos } else
383 1.22 christos (void)printf("No more messages\n");
384 1.22 christos return 0;
385 1.1 cgd }
386 1.1 cgd
387 1.1 cgd /*
388 1.1 cgd * Undelete the indicated messages.
389 1.1 cgd */
390 1.22 christos PUBLIC int
391 1.12 wiz undeletecmd(void *v)
392 1.1 cgd {
393 1.22 christos int msgCount;
394 1.22 christos int *msgvec;
395 1.8 lukem int *ip;
396 1.1 cgd
397 1.22 christos msgvec = v;
398 1.22 christos msgCount = get_msgCount();
399 1.1 cgd for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
400 1.22 christos dot = set_m_flag(*ip, ~MDELETED, 0);
401 1.22 christos touch(dot);
402 1.22 christos dot->m_flag &= ~MDELETED;
403 1.1 cgd }
404 1.1 cgd return 0;
405 1.1 cgd }
406 1.1 cgd
407 1.22 christos /*************************************************************************/
408 1.22 christos
409 1.1 cgd /*
410 1.1 cgd * Interactively dump core on "core"
411 1.1 cgd */
412 1.19 christos /*ARGSUSED*/
413 1.22 christos PUBLIC int
414 1.20 christos core(void *v __unused)
415 1.1 cgd {
416 1.1 cgd int pid;
417 1.1 cgd
418 1.1 cgd switch (pid = vfork()) {
419 1.1 cgd case -1:
420 1.15 wiz warn("fork");
421 1.22 christos return 1;
422 1.1 cgd case 0:
423 1.1 cgd abort();
424 1.1 cgd _exit(1);
425 1.1 cgd }
426 1.19 christos (void)printf("Okie dokie");
427 1.19 christos (void)fflush(stdout);
428 1.19 christos (void)wait_child(pid);
429 1.9 christos if (WCOREDUMP(wait_status))
430 1.19 christos (void)printf(" -- Core dumped.\n");
431 1.1 cgd else
432 1.19 christos (void)printf(" -- Can't dump core.\n");
433 1.1 cgd return 0;
434 1.1 cgd }
435 1.1 cgd
436 1.1 cgd /*
437 1.22 christos * Clobber the stack.
438 1.22 christos */
439 1.22 christos static void
440 1.22 christos clob1(int n)
441 1.22 christos {
442 1.22 christos char buf[512];
443 1.22 christos char *cp;
444 1.22 christos
445 1.22 christos if (n <= 0)
446 1.22 christos return;
447 1.22 christos for (cp = buf; cp < &buf[512]; *cp++ = (char)0xFF)
448 1.22 christos continue;
449 1.22 christos clob1(n - 1);
450 1.22 christos }
451 1.22 christos
452 1.22 christos /*
453 1.1 cgd * Clobber as many bytes of stack as the user requests.
454 1.1 cgd */
455 1.22 christos PUBLIC int
456 1.12 wiz clobber(void *v)
457 1.1 cgd {
458 1.24 christos char **argv;
459 1.8 lukem int times;
460 1.1 cgd
461 1.24 christos argv = v;
462 1.1 cgd if (argv[0] == 0)
463 1.1 cgd times = 1;
464 1.1 cgd else
465 1.1 cgd times = (atoi(argv[0]) + 511) / 512;
466 1.1 cgd clob1(times);
467 1.1 cgd return 0;
468 1.1 cgd }
469 1.1 cgd
470 1.1 cgd /*
471 1.22 christos * Compare two names for sorting ignored field list.
472 1.22 christos */
473 1.22 christos static int
474 1.22 christos igcomp(const void *l, const void *r)
475 1.22 christos {
476 1.22 christos return strcmp(*(const char *const *)l, *(const char *const *)r);
477 1.22 christos }
478 1.22 christos
479 1.22 christos /*
480 1.22 christos * Print out all currently retained fields.
481 1.22 christos */
482 1.22 christos static int
483 1.22 christos igshow(struct ignoretab *tab, const char *which)
484 1.22 christos {
485 1.22 christos int h;
486 1.22 christos struct ignore *igp;
487 1.22 christos char **ap, **ring;
488 1.22 christos
489 1.22 christos if (tab->i_count == 0) {
490 1.22 christos (void)printf("No fields currently being %s.\n", which);
491 1.22 christos return 0;
492 1.22 christos }
493 1.23 christos ring = salloc((tab->i_count + 1) * sizeof(char *));
494 1.22 christos ap = ring;
495 1.22 christos for (h = 0; h < HSHSIZE; h++)
496 1.22 christos for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
497 1.22 christos *ap++ = igp->i_field;
498 1.22 christos *ap = 0;
499 1.23 christos qsort(ring, tab->i_count, sizeof(char *), igcomp);
500 1.22 christos for (ap = ring; *ap != 0; ap++)
501 1.22 christos (void)printf("%s\n", *ap);
502 1.22 christos return 0;
503 1.22 christos }
504 1.22 christos
505 1.22 christos /*
506 1.22 christos * core ignore routine.
507 1.1 cgd */
508 1.22 christos static int
509 1.22 christos ignore1(char *list[], struct ignoretab *tab, const char *which)
510 1.1 cgd {
511 1.22 christos char **ap;
512 1.1 cgd
513 1.22 christos if (*list == NULL)
514 1.22 christos return igshow(tab, which);
515 1.23 christos
516 1.23 christos for (ap = list; *ap != 0; ap++)
517 1.23 christos add_ignore(*ap, tab);
518 1.23 christos
519 1.22 christos return 0;
520 1.1 cgd }
521 1.1 cgd
522 1.1 cgd /*
523 1.1 cgd * Add the given header fields to the retained list.
524 1.1 cgd * If no arguments, print the current list of retained fields.
525 1.1 cgd */
526 1.22 christos PUBLIC int
527 1.12 wiz retfield(void *v)
528 1.1 cgd {
529 1.24 christos char **list;
530 1.1 cgd
531 1.24 christos list = v;
532 1.1 cgd return ignore1(list, ignore + 1, "retained");
533 1.1 cgd }
534 1.1 cgd
535 1.1 cgd /*
536 1.1 cgd * Add the given header fields to the ignored list.
537 1.1 cgd * If no arguments, print the current list of ignored fields.
538 1.1 cgd */
539 1.22 christos PUBLIC int
540 1.12 wiz igfield(void *v)
541 1.1 cgd {
542 1.24 christos char **list;
543 1.1 cgd
544 1.24 christos list = v;
545 1.1 cgd return ignore1(list, ignore, "ignored");
546 1.1 cgd }
547 1.1 cgd
548 1.22 christos /*
549 1.22 christos * Add the given header fields to the save retained list.
550 1.22 christos * If no arguments, print the current list of save retained fields.
551 1.22 christos */
552 1.22 christos PUBLIC int
553 1.12 wiz saveretfield(void *v)
554 1.1 cgd {
555 1.24 christos char **list;
556 1.1 cgd
557 1.24 christos list = v;
558 1.1 cgd return ignore1(list, saveignore + 1, "retained");
559 1.1 cgd }
560 1.1 cgd
561 1.22 christos /*
562 1.22 christos * Add the given header fields to the save ignored list.
563 1.22 christos * If no arguments, print the current list of save ignored fields.
564 1.22 christos */
565 1.22 christos PUBLIC int
566 1.12 wiz saveigfield(void *v)
567 1.1 cgd {
568 1.24 christos char **list;
569 1.1 cgd
570 1.24 christos list = v;
571 1.1 cgd return ignore1(list, saveignore, "ignored");
572 1.1 cgd }
573 1.1 cgd
574 1.22 christos #ifdef MIME_SUPPORT
575 1.22 christos
576 1.22 christos static char*
577 1.22 christos check_dirname(char *filename)
578 1.22 christos {
579 1.22 christos struct stat sb;
580 1.22 christos char *fname;
581 1.22 christos char canon_buf[MAXPATHLEN];
582 1.22 christos char *canon_name;
583 1.22 christos
584 1.22 christos canon_name = canon_buf;
585 1.22 christos fname = filename;
586 1.22 christos if (fname[0] == '~' && fname[1] == '/') {
587 1.22 christos if (homedir && homedir[0] != '~')
588 1.22 christos (void)easprintf(&fname, "%s/%s",
589 1.22 christos homedir, fname + 2);
590 1.22 christos }
591 1.22 christos if (realpath(fname, canon_name) == NULL) {
592 1.22 christos warn("realpath: %s", filename);
593 1.22 christos canon_name = NULL;
594 1.22 christos goto done;
595 1.22 christos }
596 1.22 christos if (stat(canon_name, &sb) == -1) {
597 1.22 christos warn("stat: %s", canon_name);
598 1.22 christos canon_name = NULL;
599 1.22 christos goto done;
600 1.22 christos }
601 1.22 christos if (!S_ISDIR(sb.st_mode)) {
602 1.22 christos warnx("stat: %s is not a directory", canon_name);
603 1.22 christos canon_name = NULL;
604 1.22 christos goto done;
605 1.22 christos }
606 1.22 christos if (access(canon_name, W_OK|X_OK) == -1) {
607 1.22 christos warnx("access: %s is not writable", canon_name);
608 1.22 christos canon_name = NULL;
609 1.24 christos goto done;
610 1.22 christos }
611 1.22 christos done:
612 1.22 christos if (fname != filename)
613 1.22 christos free(fname);
614 1.22 christos
615 1.22 christos return canon_name ? savestr(canon_name) : NULL;
616 1.22 christos }
617 1.22 christos
618 1.22 christos struct detach1_core_args_s {
619 1.22 christos struct message *parent;
620 1.22 christos struct ignoretab *igtab;
621 1.22 christos const char *dstdir;
622 1.22 christos };
623 1.22 christos static int
624 1.22 christos detach1_core(struct message *mp, void *v)
625 1.1 cgd {
626 1.22 christos struct mime_info *mip;
627 1.22 christos struct detach1_core_args_s *args;
628 1.1 cgd
629 1.22 christos args = v;
630 1.22 christos touch(mp);
631 1.22 christos show_msgnum(stdout, mp, args->parent);
632 1.22 christos mip = mime_decode_open(mp);
633 1.22 christos mime_detach_msgnum(mip, sget_msgnum(mp, args->parent));
634 1.22 christos (void)mime_sendmessage(mp, NULL, args->igtab, args->dstdir, mip);
635 1.22 christos mime_decode_close(mip);
636 1.1 cgd return 0;
637 1.1 cgd }
638 1.1 cgd
639 1.1 cgd /*
640 1.22 christos * detach attachments.
641 1.1 cgd */
642 1.22 christos static int
643 1.22 christos detach1(void *v, int do_unnamed)
644 1.1 cgd {
645 1.22 christos int recursive;
646 1.22 christos int f;
647 1.22 christos int msgCount;
648 1.22 christos int *msgvec;
649 1.22 christos int *ip;
650 1.22 christos char *str;
651 1.22 christos char *dstdir;
652 1.22 christos
653 1.22 christos str = v;
654 1.22 christos
655 1.22 christos /*
656 1.22 christos * Get the destination directory.
657 1.22 christos */
658 1.22 christos if ((dstdir = snarf(str, &f, "directory")) == NULL &&
659 1.22 christos (dstdir = value(ENAME_MIME_DETACH_DIR)) == NULL &&
660 1.22 christos (dstdir = origdir) == NULL)
661 1.22 christos return 1;
662 1.22 christos
663 1.22 christos if ((dstdir = check_dirname(dstdir)) == NULL)
664 1.22 christos return 1;
665 1.22 christos
666 1.22 christos /*
667 1.22 christos * Setup the message list.
668 1.22 christos */
669 1.22 christos msgCount = get_msgCount();
670 1.23 christos msgvec = salloc((msgCount + 2) * sizeof(*msgvec));
671 1.22 christos if (!f) {
672 1.22 christos *msgvec = first(0, MMNORM);
673 1.22 christos if (*msgvec == 0) {
674 1.22 christos (void)printf("No messages to detach.\n");
675 1.22 christos return 1;
676 1.22 christos }
677 1.22 christos msgvec[1] = 0;
678 1.22 christos }
679 1.22 christos if (f && getmsglist(str, msgvec, 0) < 0)
680 1.22 christos return 1;
681 1.22 christos
682 1.22 christos if (mime_detach_control() != 0)
683 1.22 christos return 1;
684 1.1 cgd
685 1.22 christos /*
686 1.22 christos * do 'dot' if nothing else was selected.
687 1.22 christos */
688 1.22 christos if (msgvec[0] == 0 && dot != NULL) {
689 1.22 christos msgvec[0] = get_msgnum(dot);
690 1.22 christos msgvec[1] = 0;
691 1.22 christos }
692 1.22 christos recursive = do_recursion();
693 1.22 christos for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
694 1.22 christos struct detach1_core_args_s args;
695 1.22 christos struct message *mp;
696 1.24 christos
697 1.22 christos mp = get_message(*ip);
698 1.22 christos dot = mp;
699 1.22 christos args.parent = recursive ? mp : NULL;
700 1.22 christos args.igtab = do_unnamed ? detachall : ignoreall;
701 1.22 christos args.dstdir = dstdir;
702 1.22 christos (void)thread_recursion(mp, detach1_core, &args);
703 1.1 cgd }
704 1.1 cgd return 0;
705 1.1 cgd }
706 1.1 cgd
707 1.1 cgd /*
708 1.22 christos * detach named attachments.
709 1.22 christos */
710 1.22 christos PUBLIC int
711 1.22 christos detach(void *v)
712 1.22 christos {
713 1.24 christos
714 1.22 christos return detach1(v, 0);
715 1.22 christos }
716 1.22 christos
717 1.22 christos /*
718 1.22 christos * detach all attachments.
719 1.1 cgd */
720 1.22 christos PUBLIC int
721 1.22 christos Detach(void *v)
722 1.1 cgd {
723 1.24 christos
724 1.22 christos return detach1(v, 1);
725 1.1 cgd }
726 1.22 christos #endif /* MIME_SUPPORT */
727