lex.c revision 81586
155714Skris/* 255714Skris * Copyright (c) 1980, 1993 355714Skris * The Regents of the University of California. All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 855714Skris * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 3. All advertising materials mentioning features or use of this software 1455714Skris * must display the following acknowledgement: 1555714Skris * This product includes software developed by the University of 1655714Skris * California, Berkeley and its contributors. 1755714Skris * 4. Neither the name of the University nor the names of its contributors 1855714Skris * may be used to endorse or promote products derived from this software 1955714Skris * without specific prior written permission. 2055714Skris * 2155714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2255714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2455714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3155714Skris * SUCH DAMAGE. 3255714Skris */ 3355714Skris 3455714Skris#ifndef lint 3555714Skris#if 0 3655714Skrisstatic char sccsid[] = "@(#)lex.c 8.1 (Berkeley) 6/6/93"; 3755714Skris#endif 3855714Skrisstatic const char rcsid[] = 3955714Skris "$FreeBSD: head/usr.bin/mail/lex.c 81586 2001-08-13 14:06:34Z ru $"; 4055714Skris#endif /* not lint */ 4155714Skris 4255714Skris#include "rcv.h" 4355714Skris#include <errno.h> 4455714Skris#include <fcntl.h> 4555714Skris#include "extern.h" 4655714Skris 4755714Skris/* 4855714Skris * Mail -- a mail program 4955714Skris * 5055714Skris * Lexical processing of commands. 5155714Skris */ 5255714Skris 5355714Skrisconst char *prompt = "& "; 5455714Skris 5555714Skrisextern const struct cmd cmdtab[]; 5655714Skrisextern const char *version; 5755714Skris 5872613Skris/* 59238405Sjkim * Set up editing on the given file name. 6072613Skris * If the first character of name is %, we are considered to be 6172613Skris * editing the file, otherwise we are reading our mail which has 6272613Skris * signficance for mbox and so forth. 6372613Skris */ 6472613Skrisint 6572613Skrissetfile(name) 6672613Skris char *name; 6772613Skris{ 6872613Skris FILE *ibuf; 6972613Skris int i, fd; 7072613Skris struct stat stb; 7172613Skris char isedit = *name != '%'; 7272613Skris char *who = name[1] ? name + 1 : myname; 7372613Skris char tempname[PATHSIZE]; 7472613Skris static int shudclob; 7572613Skris 7672613Skris if ((name = expand(name)) == NULL) 7772613Skris return (-1); 7872613Skris 7972613Skris if ((ibuf = Fopen(name, "r")) == NULL) { 8072613Skris if (!isedit && errno == ENOENT) 8172613Skris goto nomail; 8272613Skris warn("%s", name); 8372613Skris return (-1); 8472613Skris } 8572613Skris 8672613Skris if (fstat(fileno(ibuf), &stb) < 0) { 8772613Skris warn("fstat"); 8872613Skris (void)Fclose(ibuf); 8972613Skris return (-1); 9072613Skris } 9172613Skris 9272613Skris if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) { 9372613Skris (void)Fclose(ibuf); 9472613Skris errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL; 9572613Skris warn("%s", name); 9672613Skris return (-1); 9772613Skris } 9872613Skris 9972613Skris /* 10072613Skris * Looks like all will be well. We must now relinquish our 10172613Skris * hold on the current set of stuff. Must hold signals 10272613Skris * while we are reading the new file, else we will ruin 10372613Skris * the message[] data structure. 10472613Skris */ 10572613Skris 10672613Skris holdsigs(); 10772613Skris if (shudclob) 10872613Skris quit(); 10972613Skris 11072613Skris /* 111160814Ssimon * Copy the messages into /tmp 112160814Ssimon * and set pointers. 113160814Ssimon */ 114160814Ssimon 115160814Ssimon readonly = 0; 116160814Ssimon if ((i = open(name, 1)) < 0) 117160814Ssimon readonly++; 118160814Ssimon else 119160814Ssimon (void)close(i); 120160814Ssimon if (shudclob) { 121160814Ssimon (void)fclose(itf); 122160814Ssimon (void)fclose(otf); 123160814Ssimon } 124238405Sjkim shudclob = 1; 125238405Sjkim edit = isedit; 126238405Sjkim strlcpy(prevfile, mailname, sizeof(prevfile)); 127238405Sjkim if (name != mailname) 128238405Sjkim strlcpy(mailname, name, sizeof(mailname)); 129238405Sjkim mailsize = fsize(ibuf); 130238405Sjkim (void)snprintf(tempname, sizeof(tempname), 131238405Sjkim "%s/mail.RxXXXXXXXXXX", tmpdir); 132238405Sjkim if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL) 133238405Sjkim err(1, "%s", tempname); 134238405Sjkim (void)fcntl(fileno(otf), F_SETFD, 1); 135238405Sjkim if ((itf = fopen(tempname, "r")) == NULL) 136238405Sjkim err(1, "%s", tempname); 137238405Sjkim (void)fcntl(fileno(itf), F_SETFD, 1); 138238405Sjkim (void)rm(tempname); 139238405Sjkim setptr(ibuf); 140238405Sjkim setmsize(msgCount); 141238405Sjkim (void)Fclose(ibuf); 142238405Sjkim relsesigs(); 143238405Sjkim sawcom = 0; 144238405Sjkim if (!edit && msgCount == 0) { 145238405Sjkimnomail: 146238405Sjkim fprintf(stderr, "No mail for %s\n", who); 147238405Sjkim return (-1); 148238405Sjkim } 149238405Sjkim return (0); 15055714Skris} 15155714Skris 15255714Skrisint *msgvec; 15355714Skrisint reset_on_stop; /* do a reset() if stopped */ 154109998Smarkm 155238405Sjkim/* 156238405Sjkim * Interpret user commands one by one. If standard input is not a tty, 157238405Sjkim * print no prompt. 158238405Sjkim */ 159238405Sjkimvoid 160109998Smarkmcommands() 161160814Ssimon{ 162160814Ssimon int n, eofloop = 0; 163160814Ssimon char linebuf[LINESIZE]; 16455714Skris 165167612Ssimon if (!sourcing) { 16655714Skris if (signal(SIGINT, SIG_IGN) != SIG_IGN) 16755714Skris (void)signal(SIGINT, intr); 16855714Skris if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 169160814Ssimon (void)signal(SIGHUP, hangup); 17055714Skris (void)signal(SIGTSTP, stop); 171238405Sjkim (void)signal(SIGTTOU, stop); 17255714Skris (void)signal(SIGTTIN, stop); 17355714Skris } 17455714Skris setexit(); 17555714Skris for (;;) { 17655714Skris /* 17755714Skris * Print the prompt, if needed. Clear out 178238405Sjkim * string space, and flush the output. 179238405Sjkim */ 180238405Sjkim if (!sourcing && value("interactive") != NULL) { 181238405Sjkim reset_on_stop = 1; 182238405Sjkim printf("%s", prompt); 183100936Snectar } 184238405Sjkim (void)fflush(stdout); 18555714Skris sreset(); 18659191Skris /* 18755714Skris * Read a line of commands from the current input 188238405Sjkim * and handle end of file specially. 18955714Skris */ 19055714Skris n = 0; 19155714Skris for (;;) { 19255714Skris if (readline(input, &linebuf[n], LINESIZE - n) < 0) { 19355714Skris if (n == 0) 194238405Sjkim n = -1; 195238405Sjkim break; 196238405Sjkim } 197238405Sjkim if ((n = strlen(linebuf)) == 0) 198238405Sjkim break; 199194206Ssimon n--; 200238405Sjkim if (linebuf[n] != '\\') 20155714Skris break; 20259191Skris linebuf[n++] = ' '; 20355714Skris } 204238405Sjkim reset_on_stop = 0; 20555714Skris if (n < 0) { 20655714Skris /* eof */ 20755714Skris if (loading) 20855714Skris break; 20955714Skris if (sourcing) { 210238405Sjkim unstack(); 211238405Sjkim continue; 212238405Sjkim } 213238405Sjkim if (value("interactive") != NULL && 214238405Sjkim value("ignoreeof") != NULL && 21559191Skris ++eofloop < 25) { 216238405Sjkim printf("Use \"quit\" to quit.\n"); 21759191Skris continue; 21859191Skris } 21955714Skris break; 220238405Sjkim } 22155714Skris eofloop = 0; 22255714Skris if (execute(linebuf, 0)) 22355714Skris break; 22455714Skris } 22555714Skris} 226238405Sjkim 227238405Sjkim/* 228238405Sjkim * Execute a single command. 229238405Sjkim * Command functions return 0 for success, 1 for error, and -1 230238405Sjkim * for abort. A 1 or -1 aborts a load or source. A -1 aborts 23159191Skris * the interactive command loop. 232238405Sjkim * Contxt is non-zero if called while composing mail. 23359191Skris */ 23459191Skrisint 23555714Skrisexecute(linebuf, contxt) 236238405Sjkim char linebuf[]; 23755714Skris int contxt; 23855714Skris{ 23955714Skris char word[LINESIZE]; 24055714Skris char *arglist[MAXARGC]; 24155714Skris const struct cmd *com; 242238405Sjkim char *cp, *cp2; 243238405Sjkim int c, muvec[2]; 244238405Sjkim int e = 1; 245238405Sjkim 246238405Sjkim /* 24759191Skris * Strip the white space away from the beginning 248238405Sjkim * of the command, then scan out a word, which 24959191Skris * consists of anything except digits and white space. 25059191Skris * 25155714Skris * Handle ! escapes differently to get the correct 252238405Sjkim * lexical conventions. 25355714Skris */ 25455714Skris 25555714Skris for (cp = linebuf; isspace(*cp); cp++) 25655714Skris ; 25755714Skris if (*cp == '!') { 258238405Sjkim if (sourcing) { 259238405Sjkim printf("Can't \"!\" while sourcing\n"); 260238405Sjkim goto out; 261238405Sjkim } 262238405Sjkim shell(cp+1); 26359191Skris return (0); 264238405Sjkim } 26559191Skris cp2 = word; 26659191Skris while (*cp != '\0' && strchr(" \t0123456789$^.:/-+*'\"", *cp) == NULL) 26755714Skris *cp2++ = *cp++; 268238405Sjkim *cp2 = '\0'; 26955714Skris 270127128Snectar /* 27155714Skris * Look up the command; if not found, bitch. 27255714Skris * Normally, a blank command would map to the 27355714Skris * first command in the table; while sourcing, 27455714Skris * however, we ignore blank lines to eliminate 275238405Sjkim * confusion. 276238405Sjkim */ 277238405Sjkim 278238405Sjkim if (sourcing && *word == '\0') 279238405Sjkim return (0); 28059191Skris com = lex(word); 281238405Sjkim if (com == NULL) { 28259191Skris printf("Unknown command: \"%s\"\n", word); 28359191Skris goto out; 28455714Skris } 285127128Snectar 286238405Sjkim /* 28755714Skris * See if we should execute the command -- if a conditional 28855714Skris * we always execute it, otherwise, check the state of cond. 28955714Skris */ 29055714Skris 29155714Skris if ((com->c_argtype & F) == 0) 292238405Sjkim if ((cond == CRCV && !rcvmode) || (cond == CSEND && rcvmode)) 293238405Sjkim return (0); 294238405Sjkim 295238405Sjkim /* 296238405Sjkim * Process the arguments to the command, depending 297160814Ssimon * on the type he expects. Default to an error. 298238405Sjkim * If we are sourcing an interactive command, it's 29959191Skris * an error. 30059191Skris */ 30155714Skris 302238405Sjkim if (!rcvmode && (com->c_argtype & M) == 0) { 30355714Skris printf("May not execute \"%s\" while sending\n", 30455714Skris com->c_name); 30555714Skris goto out; 30655714Skris } 30755714Skris if (sourcing && com->c_argtype & I) { 308238405Sjkim printf("May not execute \"%s\" while sourcing\n", 309238405Sjkim com->c_name); 310238405Sjkim goto out; 311238405Sjkim } 312238405Sjkim if (readonly && com->c_argtype & W) { 313160814Ssimon printf("May not execute \"%s\" -- message file is read only\n", 314238405Sjkim com->c_name); 31559191Skris goto out; 31659191Skris } 31755714Skris if (contxt && com->c_argtype & R) { 318238405Sjkim printf("Cannot recursively invoke \"%s\"\n", com->c_name); 31955714Skris goto out; 32055714Skris } 32155714Skris switch (com->c_argtype & ~(F|P|I|M|T|W|R)) { 32255714Skris case MSGLIST: 32355714Skris /* 324238405Sjkim * A message list defaulting to nearest forward 325238405Sjkim * legal message. 326238405Sjkim */ 327238405Sjkim if (msgvec == 0) { 328238405Sjkim printf("Illegal use of \"message list\"\n"); 329194206Ssimon break; 330238405Sjkim } 331279264Sdelphij if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0) 33259191Skris break; 33355714Skris if (c == 0) { 334238405Sjkim *msgvec = first(com->c_msgflag, com->c_msgmask); 335160814Ssimon msgvec[1] = 0; 33655714Skris } 33755714Skris if (*msgvec == 0) { 33855714Skris printf("No applicable messages\n"); 33955714Skris break; 34055714Skris } 341238405Sjkim e = (*com->c_func)(msgvec); 342238405Sjkim break; 343238405Sjkim 344238405Sjkim case NDMLIST: 345238405Sjkim /* 346160814Ssimon * A message list with no defaults, but no error 347238405Sjkim * if none exist. 34859191Skris */ 34959191Skris if (msgvec == 0) { 35055714Skris printf("Illegal use of \"message list\"\n"); 351238405Sjkim break; 35255714Skris } 35355714Skris if (getmsglist(cp, msgvec, com->c_msgflag) < 0) 354238405Sjkim break; 35555714Skris e = (*com->c_func)(msgvec); 35655714Skris break; 357238405Sjkim 358238405Sjkim case STRLIST: 359238405Sjkim /* 360238405Sjkim * Just the straight string, with 361238405Sjkim * leading blanks removed. 362160814Ssimon */ 363238405Sjkim while (isspace(*cp)) 36459191Skris cp++; 36559191Skris e = (*com->c_func)(cp); 36655714Skris break; 367238405Sjkim 36855714Skris case RAWLIST: 36955714Skris /* 370238405Sjkim * A vector of strings, in shell style. 37155714Skris */ 37255714Skris if ((c = getrawlist(cp, arglist, 373238405Sjkim sizeof(arglist) / sizeof(*arglist))) < 0) 374238405Sjkim break; 375238405Sjkim if (c < com->c_minargs) { 376238405Sjkim printf("%s requires at least %d arg(s)\n", 377238405Sjkim com->c_name, com->c_minargs); 378194206Ssimon break; 379238405Sjkim } 380279264Sdelphij if (c > com->c_maxargs) { 38159191Skris printf("%s takes no more than %d arg(s)\n", 38255714Skris com->c_name, com->c_maxargs); 383238405Sjkim break; 38455714Skris } 38555714Skris e = (*com->c_func)(arglist); 386238405Sjkim break; 38755714Skris 38855714Skris case NOLIST: 389238405Sjkim /* 390238405Sjkim * Just the constant zero, for exiting, 391238405Sjkim * eg. 392238405Sjkim */ 393238405Sjkim e = (*com->c_func)(0); 394160814Ssimon break; 395238405Sjkim 39659191Skris default: 39759191Skris errx(1, "Unknown argtype"); 39855714Skris } 399238405Sjkim 40055714Skrisout: 40155714Skris /* 402238405Sjkim * Exit the current source file on 40355714Skris * error. 40455714Skris */ 405238405Sjkim if (e) { 406238405Sjkim if (e < 0) 407238405Sjkim return (1); 408238405Sjkim if (loading) 409238405Sjkim return (1); 410160814Ssimon if (sourcing) 411238405Sjkim unstack(); 41259191Skris return (0); 41359191Skris } 41455714Skris if (value("autoprint") != NULL && com->c_argtype & P) 415238405Sjkim if ((dot->m_flag & MDELETED) == 0) { 41655714Skris muvec[0] = dot - &message[0] + 1; 41755714Skris muvec[1] = 0; 418238405Sjkim type(muvec); 41955714Skris } 42055714Skris if (!sourcing && (com->c_argtype & T) == 0) 421238405Sjkim sawcom = 1; 422238405Sjkim return (0); 423238405Sjkim} 424238405Sjkim 425238405Sjkim/* 426194206Ssimon * Set the size of the message vector used to construct argument 427238405Sjkim * lists to message list functions. 428279264Sdelphij */ 42959191Skrisvoid 43055714Skrissetmsize(sz) 43155714Skris int sz; 43255714Skris{ 43355714Skris 43455714Skris if (msgvec != 0) 43555714Skris (void)free(msgvec); 43655714Skris msgvec = calloc((unsigned)(sz + 1), sizeof(*msgvec)); 43755714Skris} 438238405Sjkim 439238405Sjkim/* 440238405Sjkim * Find the correct command in the command table corresponding 441238405Sjkim * to the passed command "word" 442238405Sjkim */ 443160814Ssimon 444238405Sjkim__const struct cmd * 44559191Skrislex(word) 44659191Skris char word[]; 44755714Skris{ 448238405Sjkim const struct cmd *cp; 44955714Skris 45055714Skris /* 45155714Skris * ignore trailing chars after `#' 45255714Skris * 45355714Skris * lines with beginning `#' are comments 454238405Sjkim * spaces before `#' are ignored in execute() 455238405Sjkim */ 456238405Sjkim 457238405Sjkim if (*word == '#') 458238405Sjkim *(word+1) = '\0'; 459160814Ssimon 460238405Sjkim 46159191Skris for (cp = &cmdtab[0]; cp->c_name != NULL; cp++) 46259191Skris if (isprefix(word, cp->c_name)) 46355714Skris return (cp); 464238405Sjkim return (NULL); 46555714Skris} 46655714Skris 46755714Skris/* 46855714Skris * Determine if as1 is a valid prefix of as2. 46955714Skris * Return true if yep. 470238405Sjkim */ 471238405Sjkimint 472238405Sjkimisprefix(as1, as2) 473238405Sjkim const char *as1, *as2; 474238405Sjkim{ 475194206Ssimon const char *s1, *s2; 476238405Sjkim 477279264Sdelphij s1 = as1; 47859191Skris s2 = as2; 47955714Skris while (*s1++ == *s2) 480238405Sjkim if (*s2++ == '\0') 48155714Skris return (1); 48255714Skris return (*--s1 == '\0'); 48355714Skris} 48455714Skris 48555714Skris/* 486238405Sjkim * The following gets called on receipt of an interrupt. This is 487238405Sjkim * to abort printout of a command, mainly. 488238405Sjkim * Dispatching here when command() is inactive crashes rcv. 489238405Sjkim * Close all open files except 0, 1, 2, and the temporary. 490238405Sjkim * Also, unstack all source files. 491160814Ssimon */ 492238405Sjkim 49359191Skrisint inithdr; /* am printing startup headers */ 49459191Skris 49555714Skris/*ARGSUSED*/ 496238405Sjkimvoid 49755714Skrisintr(s) 49855714Skris int s; 49955714Skris{ 50055714Skris 50155714Skris noreset = 0; 502238405Sjkim if (!inithdr) 503238405Sjkim sawcom++; 504238405Sjkim inithdr = 0; 505238405Sjkim while (sourcing) 506238405Sjkim unstack(); 507160814Ssimon 508238405Sjkim close_all_files(); 50959191Skris 51059191Skris if (image >= 0) { 51155714Skris (void)close(image); 512238405Sjkim image = -1; 51355714Skris } 51455714Skris fprintf(stderr, "Interrupt\n"); 51555714Skris reset(0); 51655714Skris} 51755714Skris 518238405Sjkim/* 519238405Sjkim * When we wake up after ^Z, reprint the prompt. 520238405Sjkim */ 521238405Sjkimvoid 522238405Sjkimstop(s) 523194206Ssimon int s; 524238405Sjkim{ 525279264Sdelphij sig_t old_action = signal(s, SIG_DFL); 52659191Skris 52755714Skris (void)sigsetmask(sigblock(0) & ~sigmask(s)); 528238405Sjkim (void)kill(0, s); 529160814Ssimon (void)sigblock(sigmask(s)); 530160814Ssimon (void)signal(s, old_action); 531160814Ssimon if (reset_on_stop) { 532160814Ssimon reset_on_stop = 0; 533160814Ssimon reset(0); 534238405Sjkim } 535238405Sjkim} 536238405Sjkim 537238405Sjkim/* 538238405Sjkim * Branch here on hangup signal and simulate "exit". 539160814Ssimon */ 540238405Sjkim/*ARGSUSED*/ 541160814Ssimonvoid 542160814Ssimonhangup(s) 543160814Ssimon int s; 544238405Sjkim{ 545160814Ssimon 546160814Ssimon /* nothing to do? */ 547160814Ssimon exit(1); 548160814Ssimon} 549160814Ssimon 550238405Sjkim/* 551238405Sjkim * Announce the presence of the current Mail version, 552238405Sjkim * give the message count, and print a header listing. 553238405Sjkim */ 554238405Sjkimvoid 555160814Ssimonannounce() 556238405Sjkim{ 557160814Ssimon int vec[2], mdot; 558160814Ssimon 559160814Ssimon mdot = newfileinfo(); 560238405Sjkim vec[0] = mdot; 561160814Ssimon vec[1] = 0; 562160814Ssimon dot = &message[mdot - 1]; 563160814Ssimon if (msgCount > 0 && value("noheader") == NULL) { 564160814Ssimon inithdr++; 565160814Ssimon headers(vec); 566238405Sjkim inithdr = 0; 567238405Sjkim } 568238405Sjkim} 569238405Sjkim 570238405Sjkim/* 571160814Ssimon * Announce information about the file we are editing. 572238405Sjkim * Return a likely place to set dot. 573160814Ssimon */ 574160814Ssimonint 575160814Ssimonnewfileinfo() 576238405Sjkim{ 577160814Ssimon struct message *mp; 578160814Ssimon int u, n, mdot, d, s; 579160814Ssimon char fname[PATHSIZE+1], zname[PATHSIZE+1], *ename; 580160814Ssimon 581160814Ssimon for (mp = &message[0]; mp < &message[msgCount]; mp++) 582238405Sjkim if (mp->m_flag & MNEW) 583238405Sjkim break; 584238405Sjkim if (mp >= &message[msgCount]) 585238405Sjkim for (mp = &message[0]; mp < &message[msgCount]; mp++) 586238405Sjkim if ((mp->m_flag & MREAD) == 0) 587160814Ssimon break; 588238405Sjkim if (mp < &message[msgCount]) 589160814Ssimon mdot = mp - &message[0] + 1; 590160814Ssimon else 591160814Ssimon mdot = 1; 592238405Sjkim s = d = 0; 593160814Ssimon for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) { 594160814Ssimon if (mp->m_flag & MNEW) 595160814Ssimon n++; 596160814Ssimon if ((mp->m_flag & MREAD) == 0) 597160814Ssimon u++; 598238405Sjkim if (mp->m_flag & MDELETED) 599238405Sjkim d++; 600238405Sjkim if (mp->m_flag & MSAVED) 601238405Sjkim s++; 602238405Sjkim } 603194206Ssimon ename = mailname; 604238405Sjkim if (getfold(fname, sizeof(fname) - 1) >= 0) { 605279264Sdelphij strcat(fname, "/"); 606160814Ssimon if (strncmp(fname, mailname, strlen(fname)) == 0) { 607160814Ssimon (void)snprintf(zname, sizeof(zname), "+%s", 60855714Skris mailname + strlen(fname)); 609238405Sjkim ename = zname; 610238405Sjkim } 61155714Skris } 61255714Skris printf("\"%s\": ", ename); 61355714Skris if (msgCount == 1) 61455714Skris printf("1 message"); 61555714Skris else 616238405Sjkim printf("%d messages", msgCount); 617238405Sjkim if (n > 0) 618238405Sjkim printf(" %d new", n); 619238405Sjkim if (u-n > 0) 620238405Sjkim printf(" %d unread", u); 621100936Snectar if (d > 0) 622238405Sjkim printf(" %d deleted", d); 62355714Skris if (s > 0) 62459191Skris printf(" %d saved", s); 62555714Skris if (readonly) 62655714Skris printf(" [Read only]"); 62755714Skris printf("\n"); 62855714Skris return (mdot); 62955714Skris} 63055714Skris 63155714Skris/* 632238405Sjkim * Print the current version number. 633238405Sjkim */ 634238405Sjkim 635238405Sjkim/*ARGSUSED*/ 636238405Sjkimint 637100936Snectarpversion(e) 638238405Sjkim int e; 63955714Skris{ 64059191Skris 64155714Skris printf("Version %s\n", version); 64255714Skris return (0); 64355714Skris} 64455714Skris 64555714Skris/* 64655714Skris * Load a file of user definitions. 64755714Skris */ 648238405Sjkimvoid 649238405Sjkimload(name) 650238405Sjkim char *name; 651238405Sjkim{ 652238405Sjkim FILE *in, *oldin; 653100928Snectar 654238405Sjkim if ((in = Fopen(name, "r")) == NULL) 65559191Skris return; 65659191Skris oldin = input; 65755714Skris input = in; 658109998Smarkm loading = 1; 65955714Skris sourcing = 1; 660109998Smarkm commands(); 661238405Sjkim loading = 0; 662194206Ssimon sourcing = 0; 663109998Smarkm input = oldin; 664109998Smarkm (void)Fclose(in); 665109998Smarkm} 666109998Smarkm