From 6173b2ee76bf15494f39f072811060dc30e69561 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 5 Aug 2002 06:49:58 +0000 Subject: Implement POSIX.1-2001 (XSI)'s fmtmsg(3). --- include/Makefile | 2 +- include/fmtmsg.h | 72 +++++++++++++ lib/libc/gen/Makefile.inc | 4 +- lib/libc/gen/err.3 | 1 + lib/libc/gen/fmtmsg.3 | 256 ++++++++++++++++++++++++++++++++++++++++++++++ lib/libc/gen/fmtmsg.c | 214 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 546 insertions(+), 3 deletions(-) create mode 100644 include/fmtmsg.h create mode 100644 lib/libc/gen/fmtmsg.3 create mode 100644 lib/libc/gen/fmtmsg.c diff --git a/include/Makefile b/include/Makefile index 1591211..ae4247f 100644 --- a/include/Makefile +++ b/include/Makefile @@ -10,7 +10,7 @@ CLEANFILES= osreldate.h version vers.c SUBDIR= arpa protocols rpcsvc rpc INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h ctype.h db.h \ dirent.h \ - dlfcn.h elf.h elf-hints.h err.h fnmatch.h fstab.h \ + dlfcn.h elf.h elf-hints.h err.h fnmatch.h fmtmsg.h fstab.h \ fts.h glob.h grp.h \ hesiod.h histedit.h ieeefp.h ifaddrs.h inttypes.h iso646.h kenv.h \ langinfo.h \ diff --git a/include/fmtmsg.h b/include/fmtmsg.h new file mode 100644 index 0000000..0de69c2 --- /dev/null +++ b/include/fmtmsg.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2002 Mike Barcroft + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FMTMSG_H_ +#define _FMTMSG_H_ + +/* Source of condition is... */ +#define MM_HARD 0x0001 /* hardware. */ +#define MM_SOFT 0x0002 /* software. */ +#define MM_FIRM 0x0004 /* fireware. */ + +/* Condition detected by... */ +#define MM_APPL 0x0010 /* application. */ +#define MM_UTIL 0x0020 /* utility. */ +#define MM_OPSYS 0x0040 /* operating system. */ + +/* Display on... */ +#define MM_PRINT 0x0100 /* standard error. */ +#define MM_CONSOLE 0x0200 /* system console. */ + +#define MM_RECOVER 0x1000 /* Recoverable error. */ +#define MM_NRECOV 0x2000 /* Non-recoverable error. */ + +/* Severity levels. */ +#define MM_NOSEV 0 /* No severity level provided. */ +#define MM_HALT 1 /* Error causing application to halt. */ +#define MM_ERROR 2 /* Non-fault fault. */ +#define MM_WARNING 3 /* Unusual non-error condition. */ +#define MM_INFO 4 /* Informative message. */ + +/* Null options. */ +#define MM_NULLLBL (char *)0 +#define MM_NULLSEV 0 +#define MM_NULLMC 0L +#define MM_NULLTXT (char *)0 +#define MM_NULLACT (char *)0 +#define MM_NULLTAG (char *)0 + +/* Return values. */ +#define MM_OK 0 /* Success. */ +#define MM_NOMSG 1 /* Failed to output to stderr. */ +#define MM_NOCON 2 /* Failed to output to console. */ +#define MM_NOTOK 3 /* Failed to output anything. */ + +int fmtmsg(long, const char *, int, const char *, const char *, const char *); + +#endif /* _FMTMSG_H_ */ diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 92a8854..c27ffd1 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -9,7 +9,7 @@ SRCS+= __xuname.c _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ clock.c closedir.c confstr.c \ crypt.c ctermid.c daemon.c devname.c dirname.c disklabel.c \ dlfcn.c dlfunc.c drand48.c erand48.c err.c errlst.c \ - exec.c fmtcheck.c fnmatch.c fstab.c ftok.c fts.c \ + exec.c fmtcheck.c fmtmsg.c fnmatch.c fstab.c ftok.c fts.c \ getbootfile.c getbsize.c \ getcap.c getcwd.c getdomainname.c getgrent.c getgrouplist.c \ gethostname.c getloadavg.c getlogin.c getmntinfo.c getnetgrent.c \ @@ -41,7 +41,7 @@ MAN+= alarm.3 arc4random.3 \ basename.3 clock.3 \ confstr.3 ctermid.3 daemon.3 \ devname.3 directory.3 dirname.3 dladdr.3 dllockinit.3 dlopen.3 \ - err.3 exec.3 fmtcheck.3 fnmatch.3 frexp.3 ftok.3 fts.3 \ + err.3 exec.3 fmtcheck.3 fmtmsg.3 fnmatch.3 frexp.3 ftok.3 fts.3 \ getbootfile.3 getbsize.3 getcap.3 getcwd.3 \ getdiskbyname.3 getdomainname.3 getfsent.3 \ getgrent.3 getgrouplist.3 gethostname.3 getloadavg.3 \ diff --git a/lib/libc/gen/err.3 b/lib/libc/gen/err.3 index b2754f5..753ef15 100644 --- a/lib/libc/gen/err.3 +++ b/lib/libc/gen/err.3 @@ -207,6 +207,7 @@ if (error != 0) .Ed .Sh SEE ALSO .Xr exit 3 , +.Xr fmtmsg 3 , .Xr printf 3 , .Xr strerror 3 .Sh HISTORY diff --git a/lib/libc/gen/fmtmsg.3 b/lib/libc/gen/fmtmsg.3 new file mode 100644 index 0000000..4b7cbfd --- /dev/null +++ b/lib/libc/gen/fmtmsg.3 @@ -0,0 +1,256 @@ +.\" +.\" Copyright (c) 2002 Mike Barcroft +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 5, 2002 +.Dt FMTMSG 3 +.Os +.Sh NAME +.Nm fmtmsg +.Nd display a detailed diagnostic message +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In fmtmsg.h +.Ft int +.Fn fmtmsg "long classification" "const char *label" "int severity" "const char *text" "const char *action" "const char *tag" +.Sh DESCRIPTION +The +.Fn fmtmsg +function displays a detailed diagnostic message, based on +the supplied augments, to +.Em stderr +and/or the system console. +.Pp +The +.Fa classification +argument is the bitwise inclusive +.Tn OR +of zero or one of the manifest constants from +each of the classification groups below. +The Output classification group is an exception since both +.Dv MM_PRINT +and +.Dv MM_CONSOLE +may be specified. +.Pp +Output +.Bl -tag -offset indent -width MM_CONSOLE +.It Dv MM_PRINT +Output should take place on +.Em stderr . +.It Dv MM_CONSOLE +Output should take place on the system console. +.El +.Pp +Source of Condition (Major) +.Bl -tag -offset indent -width MM_CONSOLE +.It Dv MM_HARD +The source of the condition is hardware related. +.It Dv MM_SOFT +The source of the condition is software related. +.It Dv MM_FIRM +The source of the condition is firmware related. +.El +.Pp +Source of Condition (Minor) +.Bl -tag -offset indent -width MM_CONSOLE +.It Dv MM_APPL +The condition was detected at the application level. +.It Dv MM_UTIL +The condition was detected at the utility level. +.It Dv MM_OPSYS +The condition was detected at the operating system level. +.El +.Pp +Status +.Bl -tag -offset indent -width MM_CONSOLE +.It Dv MM_RECOVER +The application can recover from the condition. +.It Dv MM_NRECOV +The application is unable to recover from the condition. +.El +.Pp +Alternatively, the +.Dv MM_NULLMC +manifest constant may be used to specify no classification. +.Pp +The +.Fa label +argument indicates the source of the message. +It is made up of two fields seperated by a colon +.Pq \&: . +The first field can be up to 10 bytes, +and the second field can be up to 14 bytes. +The +.Dv MM_NULLLBL +manifest constant may be used to specify no label. +.Pp +The +.Fa severity +argument identifies the importance of the condition. +One of the following manifest constants should be used for this argument. +.Bl -tag -offset indent -width MM_WARNING +.It Dv MM_HALT +The application has confronted a serious fault and is halting. +.It Dv MM_ERROR +The application has detected a fault. +.It Dv MM_WARNING +The application has detected an unusual condition, +that could be indicative of a problem. +.It Dv MM_INFO +The application is providing information about a non-error condition. +.It Dv MM_NOSEV +No severity level supplied. +.El +.Pp +The +.Fa text +argument details the error condition that caused the message. +There is no limit on the size of this character string. +The +.Dv MM_NULLTXT +manifest constant may be used to specify no text. +.Pp +The +.Fa action +argument details how the error-recovery process should begin. +Upon output, +.Fn fmtmsg +will prefix +.Qq TO FIX: +to the begin of the +.Fa action +argument. +The +.Dv MM_NULLACT +manifest constant may be used to specify no action. +.Pp +The +.Fa tag +argument should reference online documentation for the message. +This usually includes the +.Fa label +and a unique identifying number. +An example tag is +.Qq BSD:ls:168 . +The +.Dv MM_NULLTAG +manifest constant may be used to specify no tag. +.Sh RETURN VALUES +The +.Fn fmtmsg +function returns +.Dv MM_OK +upon success, +.Dv MM_NOMSG +to indicate output to +.Em stderr +failed, +.Dv MM_NOCON +to indicate output to the system console failed, or +.Dv MM_NOTOK +to indicate output to +.Em stderr +and the system console failed. +.Sh ENVIRONMENT +The +.Va MSGVERB +.Pq message verbosity +environment variable specifies which arguments to +.Fn fmtmsg +will be outputted to +.Em stderr , +and in which order. +.Va MSGVERB +should be a colon +.Pq \&: +seperated list of identifiers. +Valid identifiers include: label, severity, text, action, and tag. +If invalid identifiers are specified or incorrectly seperated, +the default message verbosity and ordering will be used. +The default ordering is equivalent to a +.Va MSGVERB +with a value of +.Qq label:severity:text:action:tag . +.Sh EXAMPLES +The code: +.Bd -literal -offset indent +fmtmsg(MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR, + "illegal option -- z", "refer to manual", "BSD:ls:001"); +.Ed +.Pp +will output: +.Bd -literal -offset indent +BSD:ls: ERROR: illegal option -- z +TO FIX: refer to manual BSD:ls:001 +.Ed +.Pp +to +.Em stderr . +.Pp +The same code, with +.Va MSGVERB +set to +.Qq "text:severity:action:tag" , +produces: +.Bd -literal -offset indent +illegal option -- z: ERROR +TO FIX: refer to manual BSD:ls:001 +.Ed +.Sh SEE ALSO +.Xr err 3 , +.Xr exit 3 , +.Xr strerror 3 +.Sh STANDARDS +The +.Fn fmtmsg +function conforms to +.St -p1003.1-2001 . +.Sh HISTORY +The +.Fn fmtmsg +function first appeared in +.Fx 5.0 . +.Sh BUGS +Specifying +.Dv MM_NULLMC +for the +.Fa classification +argument makes little sense, since without an output specified, +.Fn fmtmsg +is unable to do anything useful. +.Pp +In order for +.Fn fmtmsg +to output to the system console, the effective +user must have appropriate permission to write to +.Pa /dev/console . +This means that on most systems +.Fn fmtmsg +will return +.Dv MM_NOCON +unless the effective user is root. diff --git a/lib/libc/gen/fmtmsg.c b/lib/libc/gen/fmtmsg.c new file mode 100644 index 0000000..121a898 --- /dev/null +++ b/lib/libc/gen/fmtmsg.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 2002 Mike Barcroft + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +/* Default value for MSGVERB. */ +#define DFLT_MSGVERB "label:severity:text:action:tag" + +/* Maximum valid size for a MSGVERB. */ +#define MAX_MSGVERB sizeof(DFLT_MSGVERB) + +static char *printfmt(char *, long, const char *, int, const char *, + const char *, const char *); +static char *nextcomp(const char *); +static const char + *sevinfo(int); +static int validmsgverb(const char *); + +static const char *validlist[] = { + "label", "severity", "text", "action", "tag", NULL +}; + +int +fmtmsg(long class, const char *label, int sev, const char *text, + const char *action, const char *tag) +{ + FILE *fp; + char *env, *msgverb, *output; + + if (class & MM_PRINT) { + if ((env = getenv("MSGVERB")) != NULL && *env != '\0' && + strlen(env) <= strlen(DFLT_MSGVERB)) { + if ((msgverb = strdup(env)) == NULL) + return (MM_NOTOK); + else if (validmsgverb(msgverb) == 0) + goto def; + } else { +def: + if ((msgverb = strdup(DFLT_MSGVERB)) == NULL) + return (MM_NOTOK); + } + output = printfmt(msgverb, class, label, sev, text, action, + tag); + if (output == NULL) + return (MM_NOTOK); + if (*output != '\0') + fprintf(stderr, "%s", output); + free(msgverb); + free(output); + } + if (class & MM_CONSOLE) { + output = printfmt(DFLT_MSGVERB, class, label, sev, text, + action, tag); + if (output == NULL) + return (MM_NOCON); + if (*output != '\0') { + if ((fp = fopen("/dev/console", "a")) == NULL) { + free(output); + return (MM_NOCON); + } + fprintf(fp, "%s", output); + fclose(fp); + } + free(output); + } + return (MM_OK); +} + +#define INSERT_COLON \ + if (*output != '\0') \ + strlcat(output, ": ", size) +#define INSERT_NEWLINE \ + if (*output != '\0') \ + strlcat(output, "\n", size) +#define INSERT_SPACE \ + if (*output != '\0') \ + strlcat(output, " ", size) + +/* + * Returns NULL on memory allocation failure, otherwise returns a pointer to + * a newly malloc()'d output buffer. + */ +static char * +printfmt(char *msgverb, long class, const char *label, int sev, + const char *text, const char *act, const char *tag) +{ + size_t size; + char *comp, *output; + const char *sevname; + + size = 32; + if (label != MM_NULLLBL) + size += strlen(label); + if ((sevname = sevinfo(sev)) != NULL) + size += strlen(sevname); + if (text != MM_NULLTXT) + size += strlen(text); + if (text != MM_NULLACT) + size += strlen(act); + if (tag != MM_NULLTAG) + size += strlen(tag); + + if ((output = malloc(size)) == NULL) + return (NULL); + *output = '\0'; + while ((comp = nextcomp(msgverb)) != NULL) { + if (strcmp(comp, "label") == 0 && label != MM_NULLLBL) { + INSERT_COLON; + strlcat(output, label, size); + } else if (strcmp(comp, "severity") == 0 && sevname != NULL) { + INSERT_COLON; + strlcat(output, sevinfo(sev), size); + } else if (strcmp(comp, "text") == 0 && text != MM_NULLTXT) { + INSERT_COLON; + strlcat(output, text, size); + } else if (strcmp(comp, "action") == 0 && act != MM_NULLACT) { + INSERT_NEWLINE; + strlcat(output, "TO FIX: ", size); + strlcat(output, act, size); + } else if (strcmp(comp, "tag") == 0 && tag != MM_NULLTAG) { + INSERT_SPACE; + strlcat(output, tag, size); + } + } + INSERT_NEWLINE; + return (output); +} + +static char * +nextcomp(const char *msgverb) +{ + static char lmsgverb[MAX_MSGVERB], *state; + char *retval; + + if (*lmsgverb == '\0') { + strlcpy(lmsgverb, msgverb, sizeof(lmsgverb)); + retval = strtok_r(lmsgverb, ":", &state); + } else { + retval = strtok_r(NULL, ":", &state); + } + if (retval == NULL) + *lmsgverb = '\0'; + return (retval); +} + +static const char * +sevinfo(int sev) +{ + + switch (sev) { + case MM_HALT: + return ("HALT"); + case MM_ERROR: + return ("ERROR"); + case MM_WARNING: + return ("WARNING"); + case MM_INFO: + return ("INFO"); + default: + return (NULL); + } +} + +/* + * Returns 1 if the msgverb list is valid, otherwise 0. + */ +static int +validmsgverb(const char *msgverb) +{ + char *msgcomp; + const char *validcomp; + + while ((msgcomp = nextcomp(msgverb)) != NULL) { + if (*msgcomp == '\0') + return (0); + for (validcomp = *validlist; + validcomp != NULL; validcomp++) { + if (strcmp(msgcomp, validcomp) == 0) + break; + } + if (validcomp == NULL) + return (0); + } + return (1); +} -- cgit v1.1