Implement POSIX.1-2001 (XSI)'s fmtmsg(3).

This commit is contained in:
Mike Barcroft 2002-08-05 06:49:58 +00:00
parent c679b309f1
commit 823f68a28a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=101353
6 changed files with 546 additions and 3 deletions

View File

@ -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 \

72
include/fmtmsg.h Normal file
View File

@ -0,0 +1,72 @@
/*-
* Copyright (c) 2002 Mike Barcroft <mike@FreeBSD.org>
* 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_ */

View File

@ -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 \

View File

@ -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

256
lib/libc/gen/fmtmsg.3 Normal file
View File

@ -0,0 +1,256 @@
.\"
.\" Copyright (c) 2002 Mike Barcroft <mike@FreeBSD.org>
.\" 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.

214
lib/libc/gen/fmtmsg.c Normal file
View File

@ -0,0 +1,214 @@
/*-
* Copyright (c) 2002 Mike Barcroft <mike@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <fmtmsg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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);
}