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