1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 #pragma ident "%Z%%M% %I% %E% SMI"
41
42 #include "rcv.h"
43 #include <locale.h>
44
45 /*
46 * mailx -- a modified version of a University of California at Berkeley
47 * mail program
48 *
49 * Lexical processing of commands.
50 */
51
52 #ifdef SIGCONT
53 static void contin(int);
54 #endif
55 static int isprefix(char *as1, char *as2);
56 static const struct cmd *lex(char word[]);
57 static int Passeren(void);
58 static void setmsize(int sz);
59
60 /*
61 * Set up editing on the given file name.
62 * If isedit is true, we are considered to be editing the file,
63 * otherwise we are reading our mail which has signficance for
64 * mbox and so forth.
65 */
66
67 int
setfile(char * name,int isedit)68 setfile(char *name, int isedit)
69 {
70 FILE *ibuf;
71 int i;
72 static int shudclob;
73 static char efile[PATHSIZE];
74 char fortest[128];
75 struct stat stbuf;
76 int exrc = 1;
77 int rc = -1;
78 int fd = -1;
79
80 if (!isedit && issysmbox)
81 lockmail();
82 if (stat(name, &stbuf) < 0 && errno == EOVERFLOW) {
83 fprintf(stderr, gettext("mailbox %s is too large to"
84 " accept incoming mail\n"), name);
85 goto doret;
86 }
87 if ((ibuf = fopen(name, "r")) == NULL) {
88 extern int errno;
89 int sverrno = errno;
90 int filethere = (access(name, 0) == 0);
91 errno = sverrno;
92 if (exitflg)
93 goto doexit; /* no mail, return error */
94 if (isedit || filethere)
95 perror(name);
96 else if (!Hflag) {
97 char *f = strrchr(name, '/');
98 if (f == NOSTR)
99 fprintf(stderr, gettext("No mail.\n"));
100 else
101 fprintf(stderr, gettext("No mail for %s\n"),
102 f+1);
103 }
104 goto doret;
105 }
106 fstat(fileno(ibuf), &stbuf);
107 if (stbuf.st_size == 0L || (stbuf.st_mode&S_IFMT) != S_IFREG) {
108 if (exitflg)
109 goto doexit; /* no mail, return error */
110 if (isedit)
111 if (stbuf.st_size == 0L)
112 fprintf(stderr, gettext("%s: empty file\n"),
113 name);
114 else
115 fprintf(stderr,
116 gettext("%s: not a regular file\n"), name);
117 else if (!Hflag) {
118 if (strrchr(name, '/') == NOSTR)
119 fprintf(stderr, gettext("No mail.\n"));
120 else
121 fprintf(stderr, gettext("No mail for %s\n"),
122 strrchr(name, '/') + 1);
123 }
124 fclose(ibuf);
125 goto doret;
126 }
127
128 if (fgets(fortest, sizeof (fortest), ibuf) == NULL) {
129 perror(gettext("mailx: Unable to read from mail file"));
130 goto doexit;
131 }
132
133 fseek(ibuf, (long)(BUFSIZ+1), 0); /* flush input buffer */
134 fseek(ibuf, 0L, 0);
135 if (strncmp(fortest, "Forward to ", 11) == 0) {
136 if (exitflg)
137 goto doexit; /* no mail, return error */
138 fprintf(stderr, gettext("Your mail is being forwarded to %s"),
139 fortest+11);
140 fclose(ibuf);
141 goto doret;
142 }
143 if (exitflg) {
144 exrc = 0;
145 goto doexit; /* there is mail, return success */
146 }
147
148 /*
149 * Looks like all will be well. Must hold signals
150 * while we are reading the new file, else we will ruin
151 * the message[] data structure.
152 * Copy the messages into /tmp and set pointers.
153 */
154
155 holdsigs();
156 if (shudclob) {
157 /*
158 * Now that we know we can switch to the new file
159 * it's safe to close out the current file.
160 *
161 * If we're switching to the file we are currently
162 * editing, don't allow it to be removed as a side
163 * effect of closing it out.
164 */
165 if (edit)
166 edstop(strcmp(editfile, name) == 0);
167 else {
168 quit(strcmp(editfile, name) == 0);
169 if (issysmbox)
170 Verhogen();
171 }
172 fflush(stdout);
173 fclose(itf);
174 fclose(otf);
175 free(message);
176 space = 0;
177 }
178 readonly = 0;
179 if (!isedit && issysmbox && !Hflag)
180 readonly = Passeren() == -1;
181 lock(ibuf, "r", 1);
182 fstat(fileno(ibuf), &stbuf);
183 utimep->actime = stbuf.st_atime;
184 utimep->modtime = stbuf.st_mtime;
185
186 if (!readonly)
187 if ((i = open(name, O_WRONLY)) < 0)
188 readonly++;
189 else
190 close(i);
191 shudclob = 1;
192 edit = isedit;
193 nstrcpy(efile, PATHSIZE, name);
194 editfile = efile;
195 #ifdef notdef
196 if (name != mailname)
197 nstrcpy(mailname, PATHSIZE, name);
198 #endif
199 mailsize = fsize(ibuf);
200 if ((fd = open(tempMesg, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
201 (otf = fdopen(fd, "w")) == NULL) {
202 perror(tempMesg);
203 if (!edit && issysmbox)
204 Verhogen();
205 goto doexit;
206 }
207 if ((itf = fopen(tempMesg, "r")) == NULL) {
208 perror(tempMesg);
209 if (!edit && issysmbox)
210 Verhogen();
211 goto doexit;
212 }
213 removefile(tempMesg);
214 setptr(ibuf);
215 setmsize(msgCount);
216 fclose(ibuf);
217 relsesigs();
218 sawcom = 0;
219 rc = 0;
220
221 doret:
222 if (!isedit && issysmbox)
223 unlockmail();
224 return (rc);
225
226 doexit:
227 if (!isedit && issysmbox)
228 unlockmail();
229 exit(exrc ? exrc : rpterr);
230 /* NOTREACHED */
231 }
232
233 /* global to semaphores */
234 static char semfn[128];
235 static FILE *semfp = NULL;
236
237 /*
238 * return -1 if file is already being read, 0 otherwise
239 */
240 static int
Passeren(void)241 Passeren(void)
242 {
243 char *home;
244
245 if ((home = getenv("HOME")) == NULL)
246 return (0);
247 snprintf(semfn, sizeof (semfn), "%s%s", home, "/.Maillock");
248 if ((semfp = fopen(semfn, "w")) == NULL) {
249 fprintf(stderr,
250 gettext("WARNING: Can't open mail lock file (%s).\n"), semfn);
251 fprintf(stderr,
252 gettext("\t Assuming you are not already reading mail.\n"));
253 return (0);
254 }
255 if (lock(semfp, "w", 0) < 0) {
256 if (errno == ENOLCK) {
257 fprintf(stderr,
258 gettext("WARNING: Unable to acquire mail lock, no record locks available.\n"));
259 fprintf(stderr,
260 gettext("\t Assuming you are not already reading mail.\n"));
261 return (0);
262 }
263 fprintf(stderr,
264 gettext("WARNING: You are already reading mail.\n"));
265 fprintf(stderr,
266 gettext("\t This instance of mail is read only.\n"));
267 fclose(semfp);
268 semfp = NULL;
269 return (-1);
270 }
271 return (0);
272 }
273
274 void
Verhogen(void)275 Verhogen(void)
276 {
277 if (semfp != NULL) {
278 unlink(semfn);
279 fclose(semfp);
280 }
281 }
282
283 /*
284 * Interpret user commands one by one. If standard input is not a tty,
285 * print no prompt.
286 */
287
288 static int *msgvec;
289 static int shudprompt;
290
291 void
commands(void)292 commands(void)
293 {
294 int eofloop;
295 register int n;
296 char linebuf[LINESIZE];
297 char line[LINESIZE];
298 struct stat minfo;
299 FILE *ibuf;
300
301 #ifdef SIGCONT
302 sigset(SIGCONT, SIG_DFL);
303 #endif
304 if (rcvmode && !sourcing) {
305 if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
306 sigset(SIGINT, stop);
307 if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
308 sigset(SIGHUP, hangup);
309 }
310 for (;;) {
311 setjmp(srbuf);
312
313 /*
314 * Print the prompt, if needed. Clear out
315 * string space, and flush the output.
316 */
317
318 if (!rcvmode && !sourcing)
319 return;
320 eofloop = 0;
321 top:
322 if ((shudprompt = (intty && !sourcing)) != 0) {
323 if (prompt == NOSTR) {
324 if ((int)value("bsdcompat"))
325 prompt = "& ";
326 else
327 prompt = "";
328 }
329 #ifdef SIGCONT
330 sigset(SIGCONT, contin);
331 #endif
332 if (intty && value("autoinc") &&
333 stat(editfile, &minfo) >= 0 &&
334 minfo.st_size > mailsize) {
335 int OmsgCount, i;
336
337 OmsgCount = msgCount;
338 fseek(otf, 0L, 2);
339 holdsigs();
340 if (!edit && issysmbox)
341 lockmail();
342 if ((ibuf = fopen(editfile, "r")) == NULL) {
343 fprintf(stderr,
344 gettext("Can't reopen %s\n"),
345 editfile);
346 if (!edit && issysmbox)
347 unlockmail();
348 exit(1);
349 /* NOTREACHED */
350 }
351 if (edit || !issysmbox)
352 lock(ibuf, "r", 1);
353 fseek(ibuf, mailsize, 0);
354 mailsize = fsize(ibuf);
355 setptr(ibuf);
356 setmsize(msgCount);
357 fclose(ibuf);
358 if (!edit && issysmbox)
359 unlockmail();
360 if (msgCount-OmsgCount > 0) {
361 printf(gettext(
362 "New mail has arrived.\n"));
363 if (msgCount - OmsgCount == 1)
364 printf(gettext(
365 "Loaded 1 new message\n"));
366 else
367 printf(gettext(
368 "Loaded %d new messages\n"),
369 msgCount-OmsgCount);
370 if (value("header") != NOSTR)
371 for (i = OmsgCount+1;
372 i <= msgCount; i++) {
373 printhead(i);
374 sreset();
375 }
376 }
377 relsesigs();
378 }
379 printf("%s", prompt);
380 }
381 flush();
382 sreset();
383
384 /*
385 * Read a line of commands from the current input
386 * and handle end of file specially.
387 */
388
389 n = 0;
390 linebuf[0] = '\0';
391 for (;;) {
392 if (readline(input, line) <= 0) {
393 if (n != 0)
394 break;
395 if (loading)
396 return;
397 if (sourcing) {
398 unstack();
399 goto more;
400 }
401 if (value("ignoreeof") != NOSTR && shudprompt) {
402 if (++eofloop < 25) {
403 printf(gettext(
404 "Use \"quit\" to quit.\n"));
405 goto top;
406 }
407 }
408 return;
409 }
410 if ((n = strlen(line)) == 0)
411 break;
412 n--;
413 if (line[n] != '\\')
414 break;
415 line[n++] = ' ';
416 if (n > LINESIZE - (int)strlen(linebuf) - 1)
417 break;
418 strcat(linebuf, line);
419 }
420 n = LINESIZE - strlen(linebuf) - 1;
421 if ((int)strlen(line) > n) {
422 printf(gettext(
423 "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"),
424 linebuf, line);
425 if (loading)
426 return;
427 if (sourcing) {
428 unstack();
429 goto more;
430 }
431 return;
432 }
433 strncat(linebuf, line, n);
434 #ifdef SIGCONT
435 sigset(SIGCONT, SIG_DFL);
436 #endif
437 if (execute(linebuf, 0))
438 return;
439 more:;
440 }
441 }
442
443 /*
444 * Execute a single command. If the command executed
445 * is "quit," then return non-zero so that the caller
446 * will know to return back to main, if he cares.
447 * Contxt is non-zero if called while composing mail.
448 */
449
450 int
execute(char linebuf[],int contxt)451 execute(char linebuf[], int contxt)
452 {
453 char word[LINESIZE];
454 char *arglist[MAXARGC];
455 const struct cmd *com;
456 register char *cp, *cp2;
457 register int c, e;
458 int muvec[2];
459
460 /*
461 * Strip the white space away from the beginning
462 * of the command, then scan out a word, which
463 * consists of anything except digits and white space.
464 *
465 * Handle |, ! and # differently to get the correct
466 * lexical conventions.
467 */
468
469 cp = linebuf;
470 while (any(*cp, " \t"))
471 cp++;
472 cp2 = word;
473 if (any(*cp, "!|#"))
474 *cp2++ = *cp++;
475 else
476 while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
477 *cp2++ = *cp++;
478 *cp2 = '\0';
479
480 /*
481 * Look up the command; if not found, complain.
482 * Normally, a blank command would map to the
483 * first command in the table; while sourcing,
484 * however, we ignore blank lines to eliminate
485 * confusion.
486 */
487
488 if (sourcing && equal(word, ""))
489 return (0);
490 com = lex(word);
491 if (com == NONE) {
492 fprintf(stderr, gettext("Unknown command: \"%s\"\n"), word);
493 if (loading) {
494 cond = CANY;
495 return (1);
496 }
497 if (sourcing) {
498 cond = CANY;
499 unstack();
500 }
501 return (0);
502 }
503
504 /*
505 * See if we should execute the command -- if a conditional
506 * we always execute it, otherwise, check the state of cond.
507 */
508
509 if ((com->c_argtype & F) == 0)
510 if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode ||
511 cond == CTTY && !intty || cond == CNOTTY && intty)
512 return (0);
513
514 /*
515 * Special case so that quit causes a return to
516 * main, who will call the quit code directly.
517 * If we are in a source file, just unstack.
518 */
519
520 if (com->c_func == (int (*)(void *))edstop) {
521 if (sourcing) {
522 if (loading)
523 return (1);
524 unstack();
525 return (0);
526 }
527 return (1);
528 }
529
530 /*
531 * Process the arguments to the command, depending
532 * on the type he expects. Default to an error.
533 * If we are sourcing an interactive command, it's
534 * an error.
535 */
536
537 if (!rcvmode && (com->c_argtype & M) == 0) {
538 fprintf(stderr,
539 gettext("May not execute \"%s\" while sending\n"),
540 com->c_name);
541 if (loading)
542 return (1);
543 if (sourcing)
544 unstack();
545 return (0);
546 }
547 if (sourcing && com->c_argtype & I) {
548 fprintf(stderr,
549 gettext("May not execute \"%s\" while sourcing\n"),
550 com->c_name);
551 rpterr = 1;
552 if (loading)
553 return (1);
554 unstack();
555 return (0);
556 }
557 if (readonly && com->c_argtype & W) {
558 fprintf(stderr, gettext(
559 "May not execute \"%s\" -- message file is read only\n"),
560 com->c_name);
561 if (loading)
562 return (1);
563 if (sourcing)
564 unstack();
565 return (0);
566 }
567 if (contxt && com->c_argtype & R) {
568 fprintf(stderr, gettext("Cannot recursively invoke \"%s\"\n"),
569 com->c_name);
570 return (0);
571 }
572 e = 1;
573 switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
574 case MSGLIST:
575 /*
576 * A message list defaulting to nearest forward
577 * legal message.
578 */
579 if (msgvec == 0) {
580 fprintf(stderr,
581 gettext("Illegal use of \"message list\"\n"));
582 return (-1);
583 }
584 if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
585 break;
586 if (c == 0)
587 if (msgCount == 0)
588 *msgvec = NULL;
589 else {
590 *msgvec = first(com->c_msgflag,
591 com->c_msgmask);
592 msgvec[1] = NULL;
593 }
594 if (*msgvec == NULL) {
595 fprintf(stderr, gettext("No applicable messages\n"));
596 break;
597 }
598 e = (*com->c_func)(msgvec);
599 break;
600
601 case NDMLIST:
602 /*
603 * A message operand with no defaults, but no error
604 * if none exists. There will be an error if the
605 * msgvec pointer is of zero value.
606 */
607 if (msgvec == 0) {
608 fprintf(stderr,
609 gettext("Illegal use of \"message operand\"\n"));
610 return (-1);
611 }
612 if (getmessage(cp, msgvec, com->c_msgflag) < 0)
613 break;
614 e = (*com->c_func)(msgvec);
615 break;
616
617 case STRLIST:
618 /*
619 * Just the straight string, with
620 * leading blanks removed.
621 */
622 while (any(*cp, " \t"))
623 cp++;
624 e = (*com->c_func)(cp);
625 break;
626
627 case RAWLIST:
628 /*
629 * A vector of strings, in shell style.
630 */
631 if ((c = getrawlist(cp, arglist,
632 sizeof (arglist) / sizeof (*arglist))) < 0)
633 break;
634 if (c < com->c_minargs) {
635 fprintf(stderr,
636 gettext("%s requires at least %d arg(s)\n"),
637 com->c_name, com->c_minargs);
638 break;
639 }
640 if (c > com->c_maxargs) {
641 fprintf(stderr,
642 gettext("%s takes no more than %d arg(s)\n"),
643 com->c_name, com->c_maxargs);
644 break;
645 }
646 e = (*com->c_func)(arglist);
647 break;
648
649 case NOLIST:
650 /*
651 * Just the constant zero, for exiting,
652 * eg.
653 */
654 e = (*com->c_func)(0);
655 break;
656
657 default:
658 panic("Unknown argtype");
659 }
660
661 /*
662 * Exit the current source file on
663 * error.
664 */
665
666 if (e && loading)
667 return (1);
668 if (e && sourcing)
669 unstack();
670 if (com->c_func == (int (*)(void *))edstop)
671 return (1);
672 if (value("autoprint") != NOSTR && com->c_argtype & P)
673 if ((dot->m_flag & MDELETED) == 0) {
674 muvec[0] = dot - &message[0] + 1;
675 muvec[1] = 0;
676 type(muvec);
677 }
678 if (!sourcing && (com->c_argtype & T) == 0)
679 sawcom = 1;
680 return (0);
681 }
682
683 #ifdef SIGCONT
684 /*
685 * When we wake up after ^Z, reprint the prompt.
686 */
687 static void
688 #ifdef __cplusplus
contin(int)689 contin(int)
690 #else
691 /* ARGSUSED */
692 contin(int s)
693 #endif
694 {
695 if (shudprompt)
696 printf("%s", prompt);
697 fflush(stdout);
698 }
699 #endif
700
701 /*
702 * Branch here on hangup signal and simulate quit.
703 */
704 void
705 #ifdef __cplusplus
hangup(int)706 hangup(int)
707 #else
708 /* ARGSUSED */
709 hangup(int s)
710 #endif
711 {
712
713 holdsigs();
714 #ifdef OLD_BSD_SIGS
715 sigignore(SIGHUP);
716 #endif
717 if (edit) {
718 if (setjmp(srbuf))
719 exit(rpterr);
720 edstop(0);
721 } else {
722 if (issysmbox)
723 Verhogen();
724 if (value("exit") != NOSTR)
725 exit(1);
726 else
727 quit(0);
728 }
729 exit(rpterr);
730 }
731
732 /*
733 * Set the size of the message vector used to construct argument
734 * lists to message list functions.
735 */
736
737 static void
setmsize(int sz)738 setmsize(int sz)
739 {
740
741 if (msgvec != (int *)0)
742 free(msgvec);
743 if (sz < 1)
744 sz = 1; /* need at least one cell for terminating 0 */
745 if ((msgvec = (int *)
746 calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL)
747 panic("Failed to allocate memory for message vector");
748 }
749
750 /*
751 * Find the correct command in the command table corresponding
752 * to the passed command "word"
753 */
754
755 static const struct cmd *
lex(char word[])756 lex(char word[])
757 {
758 register const struct cmd *cp;
759
760 for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
761 if (isprefix(word, cp->c_name))
762 return (cp);
763 return (NONE);
764 }
765
766 /*
767 * Determine if as1 is a valid prefix of as2.
768 */
769 static int
isprefix(char * as1,char * as2)770 isprefix(char *as1, char *as2)
771 {
772 register char *s1, *s2;
773
774 s1 = as1;
775 s2 = as2;
776 while (*s1++ == *s2)
777 if (*s2++ == '\0')
778 return (1);
779 return (*--s1 == '\0');
780 }
781
782 /*
783 * The following gets called on receipt of a rubout. This is
784 * to abort printout of a command, mainly.
785 * Dispatching here when command() is inactive crashes rcv.
786 * Close all open files except 0, 1, 2, and the temporary.
787 * The special call to getuserid() is needed so it won't get
788 * annoyed about losing its open file.
789 * Also, unstack all source files.
790 */
791
792 static int inithdr; /* am printing startup headers */
793
794 void
stop(int s)795 stop(int s)
796 {
797 register NODE *head;
798
799 noreset = 0;
800 if (!inithdr)
801 sawcom++;
802 inithdr = 0;
803 while (sourcing)
804 unstack();
805 (void) getuserid((char *)0);
806 for (head = fplist; head != (NODE *)NULL; head = head->next) {
807 if (head->fp == stdin || head->fp == stdout)
808 continue;
809 if (head->fp == itf || head->fp == otf)
810 continue;
811 if (head->fp == stderr)
812 continue;
813 if (head->fp == semfp)
814 continue;
815 if (head->fp == pipef) {
816 npclose(pipef);
817 pipef = NULL;
818 continue;
819 }
820 fclose(head->fp);
821 }
822 if (image >= 0) {
823 close(image);
824 image = -1;
825 }
826 if (s) {
827 fprintf(stderr, gettext("Interrupt\n"));
828 fflush(stderr);
829 #ifdef OLD_BSD_SIGS
830 sigrelse(s);
831 #endif
832 }
833 longjmp(srbuf, 1);
834 }
835
836 /*
837 * Announce the presence of the current mailx version,
838 * give the message count, and print a header listing.
839 */
840
841 #define GREETING "%s Type ? for help.\n"
842
843 void
announce(void)844 announce(void)
845 {
846 int vec[2], mdot;
847 extern const char *const version;
848
849 if (!Hflag && value("quiet") == NOSTR)
850 printf(gettext(GREETING), version);
851 mdot = newfileinfo(1);
852 vec[0] = mdot;
853 vec[1] = 0;
854 dot = &message[mdot - 1];
855 if (msgCount > 0 && !noheader) {
856 inithdr++;
857 headers(vec);
858 inithdr = 0;
859 }
860 }
861
862 /*
863 * Announce information about the file we are editing.
864 * Return a likely place to set dot.
865 */
866 int
newfileinfo(int start)867 newfileinfo(int start)
868 {
869 register struct message *mp;
870 register int u, n, mdot, d, s;
871 char fname[BUFSIZ], zname[BUFSIZ], *ename;
872
873 if (Hflag)
874 return (1); /* fake it--return message 1 */
875 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
876 if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
877 break;
878 if (mp >= &message[msgCount])
879 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
880 if ((mp->m_flag & MREAD) == 0)
881 break;
882 if (mp < &message[msgCount])
883 mdot = mp - &message[0] + 1;
884 else
885 mdot = 1;
886 n = u = d = s = 0;
887 for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) {
888 if (mp->m_flag & MNEW)
889 n++;
890 if ((mp->m_flag & MREAD) == 0)
891 u++;
892 if (mp->m_flag & MDELETED)
893 d++;
894 if (mp->m_flag & MSAVED)
895 s++;
896 }
897 ename = origname;
898 if (getfold(fname) >= 0) {
899 nstrcat(fname, sizeof (fname), "/");
900 if (strncmp(fname, editfile, strlen(fname)) == 0) {
901 snprintf(zname, sizeof (zname),
902 "+%s", editfile + strlen(fname));
903 ename = zname;
904 }
905 }
906 printf("\"%s\": ", ename);
907 if (msgCount == 1)
908 printf(gettext("1 message"));
909 else
910 printf(gettext("%d messages"), msgCount);
911 if (n > 0)
912 printf(gettext(" %d new"), n);
913 if (u-n > 0)
914 printf(gettext(" %d unread"), u);
915 if (d > 0)
916 printf(gettext(" %d deleted"), d);
917 if (s > 0)
918 printf(gettext(" %d saved"), s);
919 if (readonly)
920 printf(gettext(" [Read only]"));
921 printf("\n");
922 return (mdot);
923 }
924
925 /*
926 * Print the current version number.
927 */
928
929 int
930 #ifdef __cplusplus
pversion(char *)931 pversion(char *)
932 #else
933 /* ARGSUSED */
934 pversion(char *s)
935 #endif
936 {
937 printf("%s\n", version);
938 return (0);
939 }
940
941 /*
942 * Load a file of user definitions.
943 */
944 void
load(char * name)945 load(char *name)
946 {
947 register FILE *in, *oldin;
948
949 if ((in = fopen(name, "r")) == NULL)
950 return;
951 oldin = input;
952 input = in;
953 loading = 1;
954 sourcing = 1;
955 commands();
956 loading = 0;
957 sourcing = 0;
958 input = oldin;
959 fclose(in);
960 }
961
962 /*
963 * Incorporate any new mail into the current session.
964 *
965 * XXX - Since autoinc works on "edited" files as well as the
966 * system mailbox, this probably ought to as well.
967 */
968
969 int
inc(void)970 inc(void)
971 {
972 FILE *ibuf;
973 int mdot;
974 struct stat stbuf;
975 int firstnewmsg = msgCount + 1;
976
977 if (edit) {
978 fprintf(stderr, gettext("Not in system mailbox\n"));
979 return (-1);
980 }
981 if (((ibuf = fopen(mailname, "r")) == NULL) ||
982 (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L ||
983 stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) {
984 if (strrchr(mailname, '/') == NOSTR)
985 fprintf(stderr, gettext("No new mail.\n"));
986 else
987 fprintf(stderr, gettext("No new mail for %s\n"),
988 strrchr(mailname, '/')+1);
989 if (ibuf != NULL)
990 fclose(ibuf);
991 return (-1);
992 }
993
994 fseek(otf, 0L, 2);
995 holdsigs();
996 if (issysmbox)
997 lockmail();
998 fseek(ibuf, mailsize, 0);
999 mailsize = fsize(ibuf);
1000 setptr(ibuf);
1001 setmsize(msgCount);
1002 fclose(ibuf);
1003 if (issysmbox)
1004 unlockmail();
1005 relsesigs();
1006 mdot = newfileinfo(firstnewmsg);
1007 dot = &message[mdot - 1];
1008 sawcom = 0;
1009 return (0);
1010 }
1011