From owner-freebsd-standards@FreeBSD.ORG Sun Feb 8 22:00:28 2004 Return-Path: Delivered-To: freebsd-standards@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 12B2C16A4CE for ; Sun, 8 Feb 2004 22:00:28 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 046C543D1D for ; Sun, 8 Feb 2004 22:00:28 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) i1960Rbv042079 for ; Sun, 8 Feb 2004 22:00:27 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.10/8.12.10/Submit) id i1960Rj2042078; Sun, 8 Feb 2004 22:00:27 -0800 (PST) (envelope-from gnats) Date: Sun, 8 Feb 2004 22:00:27 -0800 (PST) Message-Id: <200402090600.i1960Rj2042078@freefall.freebsd.org> To: freebsd-standards@FreeBSD.org From: Mike Heffner Subject: Re: standards/61934: [PATCH] FreeBSD's mailx not completely SUSv3-compliant X-BeenThere: freebsd-standards@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list Reply-To: Mike Heffner List-Id: Standards compliance List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 09 Feb 2004 06:00:28 -0000 The following reply was made to PR standards/61934; it has been noted by GNATS. From: Mike Heffner To: Wartan Hachaturow Cc: FreeBSD-gnats-submit@FreeBSD.org Subject: Re: standards/61934: [PATCH] FreeBSD's mailx not completely SUSv3-compliant Date: Mon, 09 Feb 2004 00:59:59 -0500 (EST) I'm glad you picked up this stuff up, it is part of the few remaining things to implement for mailx standards compliance. :-D I've done a quick first pass, comments are inlined. | |>Fix: | | 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. | This sounds like a good choice. | | | 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. | + */ I would really rather not have the setfile() function duplicated here. Since the setfile()'s return code is only checked for error in two places, your modified setfile() should work in those cases as well. So, can you modify setfile to return a more descriptive error code like in your checkmail and use it instead for the -e option? Also, the fprintf that displays "No mail for ..." should not be printed when using the -e option. SuSv3 states that when using the -e option, nothing should be printed. | +int | +checkmail(name) | + char *name; | +{ | +} | + | +/* | * 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 The -H option can be used in the receive modes and doesn't have to be separate. You can add it to the second and third usage lines. | @@ -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++; A break is needed here. | 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); Same thing for -H in the usage summary. | } | } | @@ -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); Can you replace this exit with a /*NOTREACHED*/. | + } | + | + 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); | + } | + | + Instead of duplicating this code from below, can you add something like: if (header_sum_mode) exit(0); before the call to commands? | /* | * 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 Cheers, Mike -- Mike Heffner