standards/61934: [PATCH] FreeBSD's mailx not completely
SUSv3-compliant
Wartan Hachaturow
wart at tepkom.ru
Mon Jan 26 04:30:14 PST 2004
>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 at 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 at daemon:/usr/home/wart$ mail -e
mail: illegal option -- e
wart at daemon:/usr/home/wart$ mail -H
mail: illegal option -- H
wart at 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:
More information about the freebsd-standards
mailing list