Date: Mon, 26 Jan 2004 15:24:28 +0300 (MSK) From: Wartan Hachaturow <wart@tepkom.ru> To: FreeBSD-gnats-submit@FreeBSD.org Subject: standards/61934: [PATCH] FreeBSD's mailx not completely SUSv3-compliant Message-ID: <200401261224.i0QCOS4t073378@daemon.tepkom.ru> Resent-Message-ID: <200401261230.i0QCU9Rx011610@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 61934 >Category: standards >Synopsis: [PATCH] FreeBSD's mailx not completely SUSv3-compliant >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-standards >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Jan 26 04:30:09 PST 2004 >Closed-Date: >Last-Modified: >Originator: Wartan Hachaturow >Release: FreeBSD 5.2-RELEASE alpha >Organization: >Environment: System: FreeBSD daemon.tepkom.ru 5.2-RELEASE FreeBSD 5.2-RELEASE #0: Mon Jan 19 15:12:31 MSK 2004 root@daemon.tepkom.ru:/usr/obj/usr/src/sys/DAEMON alpha >Description: SUSv3 requires mailx of the conformant implementations to have options -e, -H, -F. >How-To-Repeat: Easy. wart@daemon:/usr/home/wart$ mail -e mail: illegal option -- e wart@daemon:/usr/home/wart$ mail -H mail: illegal option -- H wart@daemon:/usr/home/wart$ mail -F mail: illegal option -- F >Fix: This is a patch (against HEAD) that implements these three options along with man page update. Hope it's not that ugly :) One little note: SUS is unclear on what does "login-name part of the address" means (in -F case). I've decided to use "the word before @,!,%", but this is, of course, arguable. For example, the only system I know of to support -F, AIX, uses the whole address as a filename. diff -ur ./mail_HEAD.orig/extern.h ./mail_HEAD.patched/extern.h --- ./mail_HEAD.orig/extern.h Tue Jun 25 09:24:29 2002 +++ ./mail_HEAD.patched/extern.h Fri Jan 23 12:57:04 2004 @@ -73,6 +73,7 @@ char *value(const char *); char *vcopy(const char *); char *yankword(char *, char []); +char *yanklogin(char *, char []); int Fclose(FILE *); int More(int *); int Pclose(FILE *); diff -ur ./mail_HEAD.orig/glob.h ./mail_HEAD.patched/glob.h --- ./mail_HEAD.orig/glob.h Sun Mar 25 08:57:04 2001 +++ ./mail_HEAD.patched/glob.h Thu Jan 22 14:37:39 2004 @@ -51,6 +51,7 @@ int sourcing; /* Currently reading variant file */ int loading; /* Loading user definitions */ int cond; /* Current state of conditional exc. */ +int record_recip; /* -F flag set */ FILE *itf; /* Input temp file buffer */ FILE *otf; /* Output temp file buffer */ int image; /* File descriptor for image of msg */ diff -ur ./mail_HEAD.orig/lex.c ./mail_HEAD.patched/lex.c --- ./mail_HEAD.orig/lex.c Sun Jun 30 09:25:06 2002 +++ ./mail_HEAD.patched/lex.c Thu Jan 22 13:03:04 2004 @@ -156,6 +156,105 @@ } /* + * Check the mailbox for mail to read. + * Return -1 in case of error, 0 if there's no mail, 1 if there is. + * This function is almost identical to setfile above, but it has + * meaningful return codes for checkmail task. + */ +int +checkmail(name) + char *name; +{ + FILE *ibuf; + int i, fd; + struct stat stb; + char *who = name[1] ? name + 1 : myname; + char tempname[PATHSIZE]; + static int shudclob; + + if ((name = expand(name)) == NULL) + return (-1); + + if ((ibuf = Fopen(name, "r")) == NULL) { + if (errno == ENOENT) + goto nomail; + warn("%s", name); + return (-1); + } + + if (fstat(fileno(ibuf), &stb) < 0) { + warn("fstat"); + (void)Fclose(ibuf); + return (-1); + } + + if (S_ISDIR(stb.st_mode) || !S_ISREG(stb.st_mode)) { + (void)Fclose(ibuf); + errno = S_ISDIR(stb.st_mode) ? EISDIR : EINVAL; + warn("%s", name); + return (-1); + } + + /* + * Looks like all will be well. Must hold signals + * while we are reading the new file, else we will ruin + * the message[] data structure. + */ + + holdsigs(); + if (shudclob) + quit(); + + /* + * Copy the messages into /tmp + * and set pointers. + */ + + readonly = 0; + if ((i = open(name, 1)) < 0) + readonly++; + else + (void)close(i); + if (shudclob) { + (void)fclose(itf); + (void)fclose(otf); + } + shudclob = 1; + strlcpy(prevfile, mailname, sizeof(prevfile)); + if (name != mailname) + strlcpy(mailname, name, sizeof(mailname)); + mailsize = fsize(ibuf); + (void)snprintf(tempname, sizeof(tempname), + "%s/mail.RxXXXXXXXXXX", tmpdir); + if ((fd = mkstemp(tempname)) == -1 || (otf = fdopen(fd, "w")) == NULL) + err(1, "%s", tempname); + (void)fcntl(fileno(otf), F_SETFD, 1); + if ((itf = fopen(tempname, "r")) == NULL) + err(1, "%s", tempname); + (void)fcntl(fileno(itf), F_SETFD, 1); + (void)rm(tempname); + setptr(ibuf, 0); + setmsize(msgCount); + /* + * New mail may have arrived while we were reading + * the mail file, so reset mailsize to be where + * we really are in the file... + */ + mailsize = ftello(ibuf); + (void)Fclose(ibuf); + relsesigs(); + if (msgCount >= 1) { + return(1); + } else { +nomail: + fprintf(stderr, "No mail for %s\n", who); + return (0); + } + /* Everything else is an error */ + return (-1); +} + +/* * Incorporate any new mail that has arrived since we first * started reading mail. */ diff -ur ./mail_HEAD.orig/mail.1 ./mail_HEAD.patched/mail.1 --- ./mail_HEAD.orig/mail.1 Thu Jan 9 04:08:33 2003 +++ ./mail_HEAD.patched/mail.1 Mon Jan 26 14:43:16 2004 @@ -46,15 +46,22 @@ .Op Fl s Ar subject .Op Fl c Ar cc-addr .Op Fl b Ar bcc-addr +.Op Fl F .Ar to-addr ... .Op Fl Ar sendmail-option ... .Nm .Op Fl EiInNv +.Op Fl F .Fl f .Op Ar name .Nm .Op Fl EiInNv +.Op Fl F .Op Fl u Ar user +.Nm +.Op Fl e +.Nm +.Op Fl H .Sh INTRODUCTION The .Nm @@ -69,6 +76,13 @@ Verbose mode. The details of delivery are displayed on the user's terminal. +.It Fl e +Test for the presence of mail in the system +mailbox. An exit status of 0 is returned if +the user has mail; otherwise, an exit status +of 1 is returned. +.It Fl H +Write a header summary only. .It Fl E Do not send messages with an empty body. This is useful for piping errors from @@ -126,6 +140,15 @@ .Ic quit , .Nm writes undeleted messages back to this file. +.It Fl F +Record the message in a file named after the first +recipient. The name is the login-name portion of the +address found first on the +.Dq Li To: +line in the mail header. +Overrides the +.Va record +variable, if set. .It Fl u Is equivalent to: .Pp diff -ur ./mail_HEAD.orig/main.c ./mail_HEAD.patched/main.c --- ./mail_HEAD.orig/main.c Sun Jun 30 09:25:06 2002 +++ ./mail_HEAD.patched/main.c Mon Jan 26 15:19:00 2004 @@ -65,6 +65,8 @@ char *argv[]; { int i; + int check_mode = 0; + int header_sum_mode = 0; struct name *to, *cc, *bcc, *smopts; char *subject, *replyto; char *ef, *rc; @@ -93,7 +95,7 @@ bcc = NULL; smopts = NULL; subject = NULL; - while ((i = getopt(argc, argv, "EINT:b:c:dfins:u:v")) != -1) { + while ((i = getopt(argc, argv, "FEHINT:b:c:edfins:u:v")) != -1) { switch (i) { case 'T': /* @@ -123,6 +125,24 @@ case 'd': debug++; break; + case 'e': + /* + * User wants to check mail and exit. + */ + check_mode++; + break; + case 'H': + /* + * User wants a header summary only. + */ + header_sum_mode++; + break; + case 'F': + /* + * User wants to record messages to files + * named after first recipient username. + */ + record_recip++; case 's': /* * Give a subject field for sending from @@ -189,11 +209,13 @@ break; case '?': fprintf(stderr, "\ -Usage: %s [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\ +Usage: %s [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] [-F] to-addr ...\n\ %*s [- sendmail-options ...]\n\ - %s [-EiInNv] -f [name]\n\ - %s [-EiInNv] [-u user]\n",__progname, strlen(__progname), "", - __progname, __progname); + %s [-EiInNv] [-F] -f [name]\n\ + %s [-EiInNv] [-F] [-u user]\n\ + %s -e\n\ + %s -H\n",__progname, strlen(__progname), "", + __progname, __progname, __progname, __progname); exit(1); } } @@ -240,6 +262,38 @@ */ exit(senderr); } + + if(check_mode) { + if (ef == NULL) + ef = "%"; + if (checkmail(ef) <= 0) { + exit(1); /* Either an error has occured, or no mail */ + } else { + exit(0); + } + /* Should exit anyway */ + exit(1); + } + + if (header_sum_mode) { + if (ef == NULL) + ef = "%"; + if (setfile(ef) < 0) + exit(1); /* error already reported */ + if (setjmp(hdrjmp) == 0) { + if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN) + (void)signal(SIGINT, hdrstop); + if (value("quiet") == NULL) + printf("Mail version %s. Type ? for help.\n", + version); + announce(); + (void)fflush(stdout); + (void)signal(SIGINT, prevint); + } + exit(0); + } + + /* * Ok, we are reading mail. * Decide whether we are editing a mailbox or reading diff -ur ./mail_HEAD.orig/names.c ./mail_HEAD.patched/names.c --- ./mail_HEAD.orig/names.c Sun Jun 30 09:25:06 2002 +++ ./mail_HEAD.patched/names.c Mon Jan 26 13:51:04 2004 @@ -210,6 +210,79 @@ } /* + * Grab a single login name (liberal word) + * Throw away things between ()'s, take anything between <>, + * and look for words before metacharacters %, @, !. + */ +char * +yanklogin(ap, wbuf) + char *ap, wbuf[]; +{ + char *cp, *cp2, *cp_temp; + int n; + + cp = ap; + for (;;) { + if (*cp == '\0') + return (NULL); + if (*cp == '(') { + int nesting = 0; + + while (*cp != '\0') { + switch (*cp++) { + case '(': + nesting++; + break; + case ')': + --nesting; + break; + } + if (nesting <= 0) + break; + } + } else if (*cp == ' ' || *cp == '\t' || *cp == ',') + cp++; + else + break; + } + + /* + * Now, let's go forward till we meet the needed character, + * and step one word back. + */ + + /* First, remember current point. */ + cp_temp = cp; + n = 0; + + /* + * Note that we look ahead in a cycle. This is safe, since + * non-end of string is checked first. + */ + while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL) + cp++; + + /* + * Now, start stepping back to the first non-word character, + * while counting the number of symbols in a word. + */ + while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) { + n++; + cp--; + } + + /* Finally, grab the word forward. */ + cp2 = wbuf; + while(n >= 0) { + *cp2++=*cp++; + n--; + } + + *cp2 = '\0'; + return (cp); +} + +/* * For each recipient in the passed name list with a / * in the name, append the message to the end of the named file * and remove him from the recipient list. diff -ur ./mail_HEAD.orig/send.c ./mail_HEAD.patched/send.c --- ./mail_HEAD.orig/send.c Sun Jun 30 09:25:06 2002 +++ ./mail_HEAD.patched/send.c Fri Jan 23 12:56:28 2004 @@ -303,9 +303,10 @@ int printheaders; { char *cp; + char *nbuf; int pid; char **namelist; - struct name *to; + struct name *to, *nsto; FILE *mtf; /* @@ -354,6 +355,18 @@ to = elide(to); if (count(to) == 0) goto out; + if (record_recip) { + /* + * Before fixing the header, save old To:. + * We do this because elide above has sorted To: list, and + * we would like to save message in a file named by the first + * recipient the user has entered, not the one being the first + * after sorting happened. + */ + if ((nsto = malloc(sizeof(struct name))) == NULL) + err(1, "Out of memory"); + bcopy(hp->h_to, nsto, sizeof(struct name)); + } fixhead(hp, to); if ((mtf = infix(hp, mtf)) == NULL) { fprintf(stderr, ". . . message lost, sorry.\n"); @@ -369,8 +382,21 @@ printf("\n"); goto out; } - if ((cp = value("record")) != NULL) - (void)savemail(expand(cp), mtf); + if (record_recip) { + /* + * Extract first recipient username from saved To: and use it + * as a filename. + */ + if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL) + err(1, "Out of memory"); + if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL) + (void)savemail(expand(nbuf), mtf); + free(nbuf); + free(nsto); + } else { + if ((cp = value("record")) != NULL) + (void)savemail(expand(cp), mtf); + } /* * Fork, set up the temporary mail file as standard * input for "mail", and exec with the user list we generated >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200401261224.i0QCOS4t073378>