Added optional argument to "file" command which allows
[unix-history] / usr / src / usr.bin / mail / cmd3.c
CommitLineData
ca6bb034
KS
1#
2
3#include "rcv.h"
4#include <sys/stat.h>
5
6/*
7 * Mail -- a mail program
8 *
9 * Still more user commands.
10 */
11
1031a04a 12static char *SccsId = "@(#)cmd3.c 1.3 %G%";
ca6bb034
KS
13
14/*
15 * Process a shell escape by saving signals, ignoring signals,
16 * and forking a sh -c
17 */
18
19shell(str)
20 char *str;
21{
22 int (*sig[2])(), stat[1];
23 register int t;
24 char *Shell;
e8adee6e 25 char cmd[BUFSIZ];
ca6bb034 26
e8adee6e
KS
27 strcpy(cmd, str);
28 if (bangexp(cmd) < 0)
29 return(-1);
ca6bb034
KS
30 if ((Shell = value("SHELL")) == NOSTR)
31 Shell = SHELL;
32 for (t = 2; t < 4; t++)
33 sig[t-2] = signal(t, SIG_IGN);
34 t = vfork();
35 if (t == 0) {
36 for (t = 2; t < 4; t++)
37 if (sig[t-2] != SIG_IGN)
38 signal(t, SIG_DFL);
e8adee6e 39 execl(Shell, Shell, "-c", cmd, 0);
ca6bb034
KS
40 perror(Shell);
41 _exit(1);
42 }
43 while (wait(stat) != t)
44 ;
45 if (t == -1)
46 perror("fork");
47 for (t = 2; t < 4; t++)
48 signal(t, sig[t-2]);
49 printf("!\n");
50 return(0);
51}
52
53/*
54 * Fork an interactive shell.
55 */
56
57dosh(str)
58 char *str;
59{
60 int (*sig[2])(), stat[1];
61 register int t;
62 char *Shell;
ca6bb034
KS
63 if ((Shell = value("SHELL")) == NOSTR)
64 Shell = SHELL;
65 for (t = 2; t < 4; t++)
66 sig[t-2] = signal(t, SIG_IGN);
67 t = vfork();
68 if (t == 0) {
69 for (t = 2; t < 4; t++)
70 if (sig[t-2] != SIG_IGN)
71 signal(t, SIG_DFL);
72 execl(Shell, Shell, 0);
73 perror(Shell);
74 _exit(1);
75 }
76 while (wait(stat) != t)
77 ;
78 if (t == -1)
79 perror("fork");
80 for (t = 2; t < 4; t++)
81 signal(t, sig[t-2]);
82 putchar('\n');
83 return(0);
84}
85
e8adee6e
KS
86/*
87 * Expand the shell escape by expanding unescaped !'s into the
88 * last issued command where possible.
89 */
90
91char lastbang[128];
92
93bangexp(str)
94 char *str;
95{
96 char bangbuf[BUFSIZ];
97 register char *cp, *cp2;
98 register int n;
99 int changed = 0;
100
101 cp = str;
102 cp2 = bangbuf;
103 n = BUFSIZ;
104 while (*cp) {
105 if (*cp == '!') {
106 if (n < strlen(lastbang)) {
107overf:
108 printf("Command buffer overflow\n");
109 return(-1);
110 }
111 changed++;
112 strcpy(cp2, lastbang);
113 cp2 += strlen(lastbang);
114 n -= strlen(lastbang);
115 cp++;
116 continue;
117 }
118 if (*cp == '\\' && cp[1] == '!') {
119 if (--n <= 1)
120 goto overf;
121 *cp2++ = '!';
122 cp += 2;
123 changed++;
124 }
125 if (--n <= 1)
126 goto overf;
127 *cp2++ = *cp++;
128 }
129 *cp2 = 0;
130 if (changed) {
131 printf("!%s\n", bangbuf);
132 fflush(stdout);
133 }
134 strcpy(str, bangbuf);
135 strncpy(lastbang, bangbuf, 128);
136 lastbang[127] = 0;
137 return(0);
138}
139
ca6bb034
KS
140/*
141 * Print out a nice help message from some file or another.
142 */
143
144help()
145{
146 register c;
147 register FILE *f;
148
149 if ((f = fopen(HELPFILE, "r")) == NULL) {
150 printf("No help just now.\n");
151 return(1);
152 }
153 while ((c = getc(f)) != EOF)
154 putchar(c);
155 fclose(f);
156 return(0);
157}
158
159/*
160 * Change user's working directory.
161 */
162
163schdir(str)
164 char *str;
165{
166 register char *cp;
167
168 for (cp = str; *cp == ' '; cp++)
169 ;
170 if (*cp == '\0')
171 cp = homedir;
172 else
173 if ((cp = expand(cp)) == NOSTR)
174 return(1);
175 if (chdir(cp) < 0) {
176 perror(cp);
177 return(1);
178 }
179 return(0);
180}
181
182/*
183 * Reply to a list of messages. Extract each name from the
184 * message header and send them off to mail1()
185 */
186
187respond(msgvec)
188 int *msgvec;
189{
190 struct message *mp;
191 char *cp, buf[2 * LINESIZE], *rcv;
192 struct name *np;
193 struct header head;
194 char *netmap();
195
196 if (msgvec[1] != 0) {
197 printf("Sorry, can't reply to multiple messages at once\n");
198 return(1);
199 }
200 mp = &message[msgvec[0] - 1];
201 dot = mp;
202 rcv = nameof(mp);
203 strcpy(buf, "");
204 cp = hfield("to", mp);
205 if (cp != NOSTR)
206 strcpy(buf, cp);
207 np = elide(extract(buf, GTO));
208 /* rcv = rename(rcv); */
209 mapf(np, rcv);
210 np = delname(np, myname);
211 head.h_seq = 1;
212 cp = detract(np, 0);
213 if (cp != NOSTR) {
214 strcpy(buf, cp);
215 strcat(buf, " ");
216 strcat(buf, rcv);
217 }
218 else
219 strcpy(buf, rcv);
220 head.h_to = buf;
221 head.h_subject = hfield("subject", mp);
222 if (head.h_subject == NOSTR)
223 head.h_subject = hfield("subj", mp);
224 head.h_cc = NOSTR;
225 cp = hfield("cc", mp);
226 if (cp != NOSTR) {
227 np = elide(extract(cp, GCC));
228 mapf(np, rcv);
229 np = delname(np, myname);
230 head.h_cc = detract(np, 0);
231 }
232 head.h_bcc = NOSTR;
233 mail1(&head);
234 return(0);
235}
236
237/*
238 * Preserve the named messages, so that they will be sent
239 * back to the system mailbox.
240 */
241
242preserve(msgvec)
243 int *msgvec;
244{
245 register struct message *mp;
246 register int *ip, mesg;
247
248 if (edit) {
249 printf("Cannot \"preserve\" in edit mode\n");
250 return(1);
251 }
252 for (ip = msgvec; *ip != NULL; ip++) {
253 mesg = *ip;
254 mp = &message[mesg-1];
255 mp->m_flag |= MPRESERVE;
256 dot = mp;
257 }
258 return(0);
259}
260
261/*
262 * Print the size of each message.
263 */
264
265messize(msgvec)
266 int *msgvec;
267{
268 register struct message *mp;
269 register int *ip, mesg;
270
271 for (ip = msgvec; *ip != NULL; ip++) {
272 mesg = *ip;
273 mp = &message[mesg-1];
274 printf("%d: %d\n", mesg, msize(mp));
275 }
276 return(0);
277}
278
279/*
280 * Quit quickly. If we are sourcing, just pop the input level
281 * by returning an error.
282 */
283
284rexit(e)
285{
286 if (sourcing)
287 return(1);
288 exit(e);
289}
290
291/*
292 * Set or display a variable value. Syntax is similar to that
293 * of csh.
294 */
295
296set(arglist)
297 char **arglist;
298{
299 register struct var *vp;
300 register char *cp, *cp2;
301 char varbuf[BUFSIZ], **ap, **p;
302 int errs, h, s;
303
304 if (argcount(arglist) == 0) {
305 for (h = 0, s = 1; h < HSHSIZE; h++)
306 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
307 s++;
308 ap = (char **) salloc(s * sizeof *ap);
309 for (h = 0, p = ap; h < HSHSIZE; h++)
310 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
311 *p++ = vp->v_name;
312 *p = NOSTR;
313 sort(ap);
314 for (p = ap; *p != NOSTR; p++)
315 printf("%s\t%s\n", *p, value(*p));
316 return(0);
317 }
318 errs = 0;
319 for (ap = arglist; *ap != NOSTR; ap++) {
320 cp = *ap;
321 cp2 = varbuf;
322 while (*cp != '=' && *cp != '\0')
323 *cp2++ = *cp++;
324 *cp2 = '\0';
325 if (*cp == '\0')
326 cp = "";
327 else
328 cp++;
329 if (equal(varbuf, "")) {
330 printf("Non-null variable name required\n");
331 errs++;
332 continue;
333 }
334 assign(varbuf, cp);
335 }
336 return(errs);
337}
338
339/*
340 * Unset a bunch of variable values.
341 */
342
343unset(arglist)
344 char **arglist;
345{
346 register struct var *vp, *vp2;
347 register char *cp;
348 int errs, h;
349 char **ap;
350
351 errs = 0;
352 for (ap = arglist; *ap != NOSTR; ap++) {
353 if ((vp2 = lookup(*ap)) == NOVAR) {
354 if (!sourcing) {
355 printf("\"%s\": undefined variable\n", *ap);
356 errs++;
357 }
358 continue;
359 }
360 h = hash(*ap);
361 if (vp2 == variables[h]) {
362 variables[h] = variables[h]->v_link;
363 vfree(vp2->v_name);
364 vfree(vp2->v_value);
365 cfree(vp2);
366 continue;
367 }
368 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
369 ;
370 vp->v_link = vp2->v_link;
371 vfree(vp2->v_name);
372 vfree(vp2->v_value);
373 cfree(vp2);
374 }
375 return(errs);
376}
377
378/*
379 * Put add users to a group.
380 */
381
382group(argv)
383 char **argv;
384{
385 register struct grouphead *gh;
386 register struct group *gp;
387 register int h;
388 int s;
389 char **ap, *gname, **p;
390
391 if (argcount(argv) == 0) {
392 for (h = 0, s = 1; h < HSHSIZE; h++)
393 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
394 s++;
395 ap = (char **) salloc(s * sizeof *ap);
396 for (h = 0, p = ap; h < HSHSIZE; h++)
397 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
398 *p++ = gh->g_name;
399 *p = NOSTR;
400 sort(ap);
401 for (p = ap; *p != NOSTR; p++)
402 printgroup(*p);
403 return(0);
404 }
405 if (argcount(argv) == 1) {
406 printgroup(*argv);
407 return(0);
408 }
409 gname = *argv;
410 h = hash(gname);
411 if ((gh = findgroup(gname)) == NOGRP) {
412 gh = (struct grouphead *) calloc(sizeof *gh, 1);
413 gh->g_name = vcopy(gname);
414 gh->g_list = NOGE;
415 gh->g_link = groups[h];
416 groups[h] = gh;
417 }
418
419 /*
420 * Insert names from the command list into the group.
421 * Who cares if there are duplicates? They get tossed
422 * later anyway.
423 */
424
425 for (ap = argv+1; *ap != NOSTR; ap++) {
426 gp = (struct group *) calloc(sizeof *gp, 1);
427 gp->ge_name = vcopy(*ap);
428 gp->ge_link = gh->g_list;
429 gh->g_list = gp;
430 }
431 return(0);
432}
433
434/*
435 * Sort the passed string vecotor into ascending dictionary
436 * order.
437 */
438
439sort(list)
440 char **list;
441{
442 register char **ap;
443 int diction();
444
445 for (ap = list; *ap != NOSTR; ap++)
446 ;
447 if (ap-list < 2)
448 return;
449 qsort(list, ap-list, sizeof *list, diction);
450}
451
452/*
453 * Do a dictionary order comparison of the arguments from
454 * qsort.
455 */
456
457diction(a, b)
458 register char **a, **b;
459{
460 return(strcmp(*a, *b));
461}
462
463/*
464 * The do nothing command for comments.
465 */
466
467null(e)
468{
469 return(0);
470}
471
472/*
1031a04a 473 * Print out the current edit file, if we are editing.
ca6bb034
KS
474 * Otherwise, print the name of the person who's mail
475 * we are reading.
476 */
477
1031a04a
KS
478file(argv)
479 char **argv;
ca6bb034
KS
480{
481 register char *cp;
1031a04a 482 char fname[BUFSIZ];
ca6bb034 483
1031a04a
KS
484 if (argv[0] == NOSTR) {
485 if (edit)
486 printf("Reading \"%s\"", editfile);
487 else
488 printf("Reading %s's mail",
489 rindex(mailname, '/') + 1);
490 printf("; %d message(s)\n", msgCount);
491 return(0);
492 }
493
494 /*
495 * Acker's! Must switch to the new file.
496 * We use a funny interpretation --
497 * # -- gets the previous file
498 * % -- gets the invoker's post office box
499 * %user -- gets someone else's post office box
500 * string -- reads the given file
501 */
502
503 cp = getfilename(argv[0]);
504 if (cp == NOSTR)
505 return(-1);
506 return(setfile(cp, 1));
507}
508
509/*
510 * Evaluate the string given as a new mailbox name.
511 * Ultimately, we want this to support a number of meta characters.
512 * Possibly:
513 * % -- for my system mail box
514 * %user -- for user's system mail box
515 * # -- for previous file
516 * file name -- for any other file
517 */
518
519char *
520getfilename(name)
521 char *name;
522{
523 register char *cp;
524
525 cp = expand(name);
526 return(cp);
ca6bb034
KS
527}
528
529/*
530 * Expand file names like echo
531 */
532
533echo(argv)
534 char **argv;
535{
536 register char **ap;
537 register char *cp;
538
539 for (ap = argv; *ap != NOSTR; ap++) {
540 cp = *ap;
541 if ((cp = expand(cp)) != NOSTR)
542 printf("%s\n", cp);
543 }
544 return(0);
545}
546
547/*
548 * Reply to a series of messages by simply mailing to the senders
549 * and not messing around with the To: and Cc: lists as in normal
550 * reply.
551 */
552
553Respond(msgvec)
554 int msgvec[];
555{
556 struct header head;
557 struct message *mp;
558 register int s, *ap;
559 register char *cp, *subject;
560
561 for (s = 0, ap = msgvec; *ap != 0; ap++) {
562 mp = &message[*ap - 1];
563 dot = mp;
564 s += strlen(nameof(mp)) + 1;
565 }
566 if (s == 0)
567 return(0);
568 cp = salloc(s + 2);
569 head.h_to = cp;
570 for (ap = msgvec; *ap != 0; ap++) {
571 mp = &message[*ap - 1];
572 cp = copy(nameof(mp), cp);
573 *cp++ = ' ';
574 }
575 *--cp = 0;
576 mp = &message[msgvec[0] - 1];
577 subject = hfield("subject", mp);
578 head.h_seq = 0;
579 if (subject == NOSTR)
580 subject = hfield("subj", mp);
581 head.h_subject = subject;
582 if (subject != NOSTR)
583 head.h_seq++;
584 head.h_cc = NOSTR;
585 head.h_bcc = NOSTR;
586 mail1(&head);
587 return(0);
588}