Index: head/lib/libstand/libstand.3
===================================================================
--- head/lib/libstand/libstand.3 (revision 277559)
+++ head/lib/libstand/libstand.3 (revision 277560)
@@ -1,676 +1,676 @@
.\" Copyright (c) Michael Smith
.\" 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 6, 2004
.Dt LIBSTAND 3
.Os
.Sh NAME
.Nm libstand
.Nd support library for standalone executables
.Sh SYNOPSIS
.In stand.h
.Sh DESCRIPTION
The
.Nm
library provides a set of supporting functions for standalone
applications, mimicking where possible the standard
.Bx
programming
environment.
The following sections group these functions by kind.
Unless specifically described here, see the corresponding section 3
manpages for the given functions.
.Sh STRING FUNCTIONS
String functions are available as documented in
.Xr string 3
and
.Xr bstring 3 .
.Sh MEMORY ALLOCATION
.Bl -hang -width 10n
.It Xo
.Ft "void *"
.Fn malloc "size_t size"
.Xc
.Pp
Allocate
.Fa size
bytes of memory from the heap using a best-fit algorithm.
.It Xo
.Ft void
.Fn free "void *ptr"
.Xc
.Pp
Free the allocated object at
.Fa ptr .
.It Xo
.Ft void
.Fn setheap "void *start" "void *limit"
.Xc
.Pp
Initialise the heap.
This function must be called before calling
.Fn alloc
for the first time.
The region between
.Fa start
and
.Fa limit
will be used for the heap; attempting to allocate beyond this will result
in a panic.
.It Xo
.Ft "char *"
.Fn sbrk "int junk"
.Xc
.Pp
Provides the behaviour of
.Fn sbrk 0 ,
i.e., returns the highest point that the heap has reached.
This value can
be used during testing to determine the actual heap usage.
The
.Fa junk
argument is ignored.
.El
.Sh ENVIRONMENT
A set of functions are provided for manipulating a flat variable space similar
to the traditional shell-supported environment.
Major enhancements are support
for set/unset hook functions.
.Bl -hang -width 10n
.It Xo
.Ft "char *"
.Fn getenv "const char *name"
.Xc
.It Xo
.Ft int
.Fn setenv "const char *name" "const char *value" "int overwrite"
.Xc
.It Xo
.Ft int
.Fn putenv "const char *string"
.Xc
.It Xo
.Ft int
.Fn unsetenv "const char *name"
.Xc
.Pp
These functions behave similarly to their standard library counterparts.
.It Xo
.Ft "struct env_var *"
.Fn env_getenv "const char *name"
.Xc
.Pp
Looks up a variable in the environment and returns its entire
data structure.
.It Xo
.Ft int
.Fn env_setenv "const char *name" "int flags" "const void *value" "ev_sethook_t sethook" "ev_unsethook_t unsethook"
.Xc
.Pp
Creates a new or sets an existing environment variable called
.Fa name .
If creating a new variable, the
.Fa sethook
and
.Fa unsethook
arguments may be specified.
.Pp
The set hook is invoked whenever an attempt
is made to set the variable, unless the EV_NOHOOK flag is set.
Typically
a set hook will validate the
.Fa value
argument, and then call
.Fn env_setenv
again with EV_NOHOOK set to actually save the value.
The predefined function
.Fn env_noset
may be specified to refuse all attempts to set a variable.
.Pp
The unset hook is invoked when an attempt is made to unset a variable.
If it
returns zero, the variable will be unset.
The predefined function
.Fa env_nounset
may be used to prevent a variable being unset.
.El
.Sh STANDARD LIBRARY SUPPORT
.Bl -hang -width 10n
.It Xo
.Ft int
.Fn getopt "int argc" "char * const *argv" "const char *optstring"
.Xc
.It Xo
.Ft long
.Fn strtol "const char *nptr" "char **endptr" "int base"
.Xc
.It Xo
.Ft void
.Fn srandom "unsigned long seed"
.Xc
.It Xo
.Ft "unsigned long"
.Fn random void
.Xc
.It Xo
.Ft "char *"
.Fn strerror "int error"
.Xc
.Pp
Returns error messages for the subset of errno values supported by
.Nm .
.It Fn assert expression
.Pp
Requires
.In assert.h .
.It Xo
.Ft int
.Fn setjmp "jmp_buf env"
.Xc
.It Xo
.Ft void
.Fn longjmp "jmp_buf env" "int val"
.Xc
.Pp
Defined as
.Fn _setjmp
and
.Fn _longjmp
respectively as there is no signal state to manipulate.
Requires
.In setjmp.h .
.El
.Sh CHARACTER I/O
.Bl -hang -width 10n
.It Xo
.Ft void
.Fn gets "char *buf"
.Xc
.Pp
Read characters from the console into
.Fa buf .
All of the standard cautions apply to this function.
.It Xo
.Ft void
.Fn ngets "char *buf" "int size"
.Xc
.Pp
Read at most
.Fa size
- 1 characters from the console into
.Fa buf .
If
.Fa size
is less than 1, the function's behaviour is as for
.Fn gets .
.It Xo
.Ft int
.Fn fgetstr "char *buf" "int size" "int fd"
.Xc
.Pp
Read a line of at most
.Fa size
characters into
.Fa buf .
Line terminating characters are stripped, and the buffer is always
.Dv NUL
terminated.
Returns the number of characters in
.Fa buf
if successful, or -1 if a read error occurs.
.It Xo
.Ft int
.Fn printf "const char *fmt" "..."
.Xc
.It Xo
.Ft void
.Fn vprintf "const char *fmt" "va_list ap"
.Xc
.It Xo
.Ft int
.Fn sprintf "char *buf" "const char *fmt" "..."
.Xc
.It Xo
.Ft void
.Fn vsprintf "char *buf" "const char *fmt" "va_list ap"
.Xc
.Pp
The *printf functions implement a subset of the standard
.Fn printf
family functionality and some extensions.
The following standard conversions
are supported: c,d,n,o,p,s,u,x.
The following modifiers are supported:
+,-,#,*,0,field width,precision,l.
.Pp
The
.Li b
conversion is provided to decode error registers.
Its usage is:
.Bd -ragged -offset indent
printf(
.Qq reg=%b\en ,
regval,
.Qq *
);
.Ed
.Pp
where is the output expressed as a control character, e.g.\& \e10 gives
octal, \e20 gives hex.
Each is a sequence of characters, the first of
which gives the bit number to be inspected (origin 1) and the next characters
(up to a character less than 32) give the text to be displayed if the bit is set.
Thus
.Bd -ragged -offset indent
printf(
.Qq reg=%b\en ,
3,
-.Qq \e10\e2BITTWO\e1BITONE\en
+.Qq \e10\e2BITTWO\e1BITONE
);
.Ed
.Pp
would give the output
.Bd -ragged -offset indent
reg=3
.Ed
.Pp
The
.Li D
conversion provides a hexdump facility, e.g.
.Bd -ragged -offset indent
printf(
.Qq %6D ,
ptr,
.Qq \&:
); gives
.Qq XX:XX:XX:XX:XX:XX
.Ed
.Bd -ragged -offset indent
printf(
.Qq %*D ,
len,
ptr,
.Qq "\ "
); gives
.Qq XX XX XX ...
.Ed
.El
.Sh CHARACTER TESTS AND CONVERSIONS
.Bl -hang -width 10n
.It Xo
.Ft int
.Fn isupper "int c"
.Xc
.It Xo
.Ft int
.Fn islower "int c"
.Xc
.It Xo
.Ft int
.Fn isspace "int c"
.Xc
.It Xo
.Ft int
.Fn isdigit "int c"
.Xc
.It Xo
.Ft int
.Fn isxdigit "int c"
.Xc
.It Xo
.Ft int
.Fn isascii "int c"
.Xc
.It Xo
.Ft int
.Fn isalpha "int c"
.Xc
.It Xo
.Ft int
.Fn toupper "int c"
.Xc
.It Xo
.Ft int
.Fn tolower "int c"
.Xc
.El
.Sh FILE I/O
.Bl -hang -width 10n
.It Xo
.Ft int
.Fn open "const char *path" "int flags"
.Xc
.Pp
Similar to the behaviour as specified in
.Xr open 2 ,
except that file creation is not supported, so the mode parameter is not
required.
The
.Fa flags
argument may be one of O_RDONLY, O_WRONLY and O_RDWR (although no file systems
currently support writing).
.It Xo
.Ft int
.Fn close "int fd"
.Xc
.It Xo
.Ft void
.Fn closeall void
.Xc
.Pp
Close all open files.
.It Xo
.Ft ssize_t
.Fn read "int fd" "void *buf" "size_t len"
.Xc
.It Xo
.Ft ssize_t
.Fn write "int fd" "void *buf" "size_t len"
.Xc
.Pp
(No file systems currently support writing.)
.It Xo
.Ft off_t
.Fn lseek "int fd" "off_t offset" "int whence"
.Xc
.Pp
Files being automatically uncompressed during reading cannot seek backwards
from the current point.
.It Xo
.Ft int
.Fn stat "const char *path" "struct stat *sb"
.Xc
.It Xo
.Ft int
.Fn fstat "int fd" "struct stat *sb"
.Xc
.Pp
The
.Fn stat
and
.Fn fstat
functions only fill out the following fields in the
.Fa sb
structure: st_mode,st_nlink,st_uid,st_gid,st_size.
The
.Nm tftp
file system cannot provide meaningful values for this call, and the
.Nm cd9660
file system always reports files having uid/gid of zero.
.El
.Sh PAGER
The
.Nm
library supplies a simple internal pager to ease reading the output of large
commands.
.Bl -hang -width 10n
.It Xo
.Ft void
.Fn pager_open
.Xc
.Pp
Initialises the pager and tells it that the next line output will be the top of the
display.
The environment variable LINES is consulted to determine the number of
lines to be displayed before pausing.
.It Xo
.Ft void
.Fn pager_close void
.Xc
.Pp
Closes the pager.
.It Xo
.Ft int
.Fn pager_output "const char *lines"
.Xc
.Pp
Sends the lines in the
.Dv NUL Ns
-terminated buffer at
.Fa lines
to the pager.
Newline characters are counted in order to determine the number
of lines being output (wrapped lines are not accounted for).
The
.Fn pager_output
function will return zero when all of the lines have been output, or nonzero
if the display was paused and the user elected to quit.
.It Xo
.Ft int
.Fn pager_file "const char *fname"
.Xc
.Pp
Attempts to open and display the file
.Fa fname .
Returns -1 on error, 0 at EOF, or 1 if the user elects to quit while reading.
.El
.Sh MISC
.Bl -hang -width 10n
.It Xo
.Ft void
.Fn twiddle void
.Xc
.Pp
Successive calls emit the characters in the sequence |,/,-,\\ followed by a
backspace in order to provide reassurance to the user.
.El
.Sh REQUIRED LOW-LEVEL SUPPORT
The following resources are consumed by
.Nm
- stack, heap, console and devices.
.Pp
The stack must be established before
.Nm
functions can be invoked.
Stack requirements vary depending on the functions
and file systems used by the consumer and the support layer functions detailed
below.
.Pp
The heap must be established before calling
.Fn alloc
or
.Fn open
by calling
.Fn setheap .
Heap usage will vary depending on the number of simultaneously open files,
as well as client behaviour.
Automatic decompression will allocate more
than 64K of data per open file.
.Pp
Console access is performed via the
.Fn getchar ,
.Fn putchar
and
.Fn ischar
functions detailed below.
.Pp
Device access is initiated via
.Fn devopen
and is performed through the
.Fn dv_strategy ,
.Fn dv_ioctl
and
.Fn dv_close
functions in the device switch structure that
.Fn devopen
returns.
.Pp
The consumer must provide the following support functions:
.Bl -hang -width 10n
.It Xo
.Ft int
.Fn getchar void
.Xc
.Pp
Return a character from the console, used by
.Fn gets ,
.Fn ngets
and pager functions.
.It Xo
.Ft int
.Fn ischar void
.Xc
.Pp
Returns nonzero if a character is waiting from the console.
.It Xo
.Ft void
.Fn putchar int
.Xc
.Pp
Write a character to the console, used by
.Fn gets ,
.Fn ngets ,
.Fn *printf ,
.Fn panic
and
.Fn twiddle
and thus by many other functions for debugging and informational output.
.It Xo
.Ft int
.Fn devopen "struct open_file *of" "const char *name" "const char **file"
.Xc
.Pp
Open the appropriate device for the file named in
.Fa name ,
returning in
.Fa file
a pointer to the remaining body of
.Fa name
which does not refer to the device.
The
.Va f_dev
field in
.Fa of
will be set to point to the
.Vt devsw
structure for the opened device if successful.
Device identifiers must
always precede the path component, but may otherwise be arbitrarily formatted.
Used by
.Fn open
and thus for all device-related I/O.
.It Xo
.Ft int
.Fn devclose "struct open_file *of"
.Xc
.Pp
Close the device allocated for
.Fa of .
The device driver itself will already have been called for the close; this call
should clean up any allocation made by devopen only.
.It Xo
.Ft void
.Fn panic "const char *msg" "..."
.Xc
.Pp
Signal a fatal and unrecoverable error condition.
The
.Fa msg ...
arguments are as for
.Fn printf .
.El
.Sh INTERNAL FILE SYSTEMS
Internal file systems are enabled by the consumer exporting the array
.Vt struct fs_ops *file_system[] ,
which should be initialised with pointers
to
.Vt struct fs_ops
structures.
The following file system handlers are supplied by
.Nm ,
the consumer may supply other file systems of their own:
.Bl -hang -width ".Va cd9660_fsops"
.It Va ufs_fsops
The
.Bx
UFS.
.It Va ext2fs_fsops
Linux ext2fs file system.
.It Va tftp_fsops
File access via TFTP.
.It Va nfs_fsops
File access via NFS.
.It Va cd9660_fsops
ISO 9660 (CD-ROM) file system.
.It Va gzipfs_fsops
Stacked file system supporting gzipped files.
When trying the gzipfs file system,
.Nm
appends
.Li .gz
to the end of the filename, and then tries to locate the file using the other
file systems.
Placement of this file system in the
.Va file_system[]
array determines whether gzipped files will be opened in preference to non-gzipped
files.
It is only possible to seek a gzipped file forwards, and
.Fn stat
and
.Fn fstat
on gzipped files will report an invalid length.
.It Va bzipfs_fsops
The same as
.Va gzipfs_fsops ,
but for
.Xr bzip2 1 Ns -compressed
files.
.El
.Pp
The array of
.Vt struct fs_ops
pointers should be terminated with a NULL.
.Sh DEVICES
Devices are exported by the supporting code via the array
.Vt struct devsw *devsw[]
which is a NULL terminated array of pointers to device switch structures.
.Sh HISTORY
The
.Nm
library contains contributions from many sources, including:
.Bl -bullet -compact
.It
.Nm libsa
from
.Nx
.It
.Nm libc
and
.Nm libkern
from
.Fx 3.0 .
.It
.Nm zalloc
from
.An Matthew Dillon Aq Mt dillon@backplane.com
.El
.Pp
The reorganisation and port to
.Fx 3.0 ,
the environment functions and this manpage were written by
.An Mike Smith Aq Mt msmith@FreeBSD.org .
.Sh BUGS
The lack of detailed memory usage data is unhelpful.
Index: head/lib/libstand/printf.c
===================================================================
--- head/lib/libstand/printf.c (revision 277559)
+++ head/lib/libstand/printf.c (revision 277560)
@@ -1,513 +1,513 @@
/*-
* Copyright (c) 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
*/
#include
__FBSDID("$FreeBSD$");
/*
* Standaloneified version of the FreeBSD kernel printf family.
*/
#include
#include
#include
#include
#include
#include "stand.h"
/*
* Note that stdarg.h and the ANSI style va_start macro is used for both
* ANSI and traditional C compilers.
*/
#include
#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
typedef void (kvprintf_fn_t)(int, void *);
static char *ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
static int kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap);
static void
putchar_wrapper(int cc, void *arg)
{
putchar(cc);
}
int
printf(const char *fmt, ...)
{
va_list ap;
int retval;
va_start(ap, fmt);
retval = kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
va_end(ap);
return retval;
}
void
vprintf(const char *fmt, va_list ap)
{
kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
}
int
sprintf(char *buf, const char *cfmt, ...)
{
int retval;
va_list ap;
va_start(ap, cfmt);
retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
buf[retval] = '\0';
va_end(ap);
return retval;
}
struct print_buf {
char *buf;
size_t size;
};
static void
snprint_func(int ch, void *arg)
{
struct print_buf *pbuf = arg;
if (pbuf->size < 2) {
/*
* Reserve last buffer position for the terminating
* character:
*/
return;
}
*(pbuf->buf)++ = ch;
pbuf->size--;
}
int
snprintf(char *buf, size_t size, const char *cfmt, ...)
{
int retval;
va_list ap;
struct print_buf arg;
arg.buf = buf;
arg.size = size;
va_start(ap, cfmt);
retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
va_end(ap);
if (arg.size >= 1)
*(arg.buf)++ = 0;
return retval;
}
void
vsprintf(char *buf, const char *cfmt, va_list ap)
{
int retval;
retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
buf[retval] = '\0';
}
/*
* Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
* order; return an optional length and a pointer to the last character
* written in the buffer (i.e., the first character of the string).
* The buffer pointed to by `nbuf' must have length >= MAXNBUF.
*/
static char *
ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
{
char *p, c;
p = nbuf;
*p = '\0';
do {
c = hex2ascii(num % base);
*++p = upper ? toupper(c) : c;
} while (num /= base);
if (lenp)
*lenp = p - nbuf;
return (p);
}
/*
* Scaled down version of printf(3).
*
* Two additional formats:
*
* The format %b is supported to decode error registers.
* Its usage is:
*
* printf("reg=%b\n", regval, "*");
*
* where is the output base expressed as a control character, e.g.
* \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
* the first of which gives the bit number to be inspected (origin 1), and
* the next characters (up to a control character, i.e. a character <= 32),
* give the name of the register. Thus:
*
- * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
+ * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
*
* would produce output:
*
* reg=3
*
* XXX: %D -- Hexdump, takes pointer and separator string:
* ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
* ("%*D", len, ptr, " " -> XX XX XX XX ...
*/
static int
kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
{
#define PCHAR(c) {int cc=(c); if (func) (*func)(cc, arg); else *d++ = cc; retval++; }
char nbuf[MAXNBUF];
char *d;
const char *p, *percent, *q;
u_char *up;
int ch, n;
uintmax_t num;
int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
int cflag, hflag, jflag, tflag, zflag;
int dwidth, upper;
char padc;
int stop = 0, retval = 0;
num = 0;
if (!func)
d = (char *) arg;
else
d = NULL;
if (fmt == NULL)
fmt = "(fmt null)\n";
if (radix < 2 || radix > 36)
radix = 10;
for (;;) {
padc = ' ';
width = 0;
while ((ch = (u_char)*fmt++) != '%' || stop) {
if (ch == '\0')
return (retval);
PCHAR(ch);
}
percent = fmt - 1;
qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
sign = 0; dot = 0; dwidth = 0; upper = 0;
cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
reswitch: switch (ch = (u_char)*fmt++) {
case '.':
dot = 1;
goto reswitch;
case '#':
sharpflag = 1;
goto reswitch;
case '+':
sign = 1;
goto reswitch;
case '-':
ladjust = 1;
goto reswitch;
case '%':
PCHAR(ch);
break;
case '*':
if (!dot) {
width = va_arg(ap, int);
if (width < 0) {
ladjust = !ladjust;
width = -width;
}
} else {
dwidth = va_arg(ap, int);
}
goto reswitch;
case '0':
if (!dot) {
padc = '0';
goto reswitch;
}
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (n = 0;; ++fmt) {
n = n * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
if (dot)
dwidth = n;
else
width = n;
goto reswitch;
case 'b':
num = (u_int)va_arg(ap, int);
p = va_arg(ap, char *);
for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
PCHAR(*q--);
if (num == 0)
break;
for (tmp = 0; *p;) {
n = *p++;
if (num & (1 << (n - 1))) {
PCHAR(tmp ? ',' : '<');
for (; (n = *p) > ' '; ++p)
PCHAR(n);
tmp = 1;
} else
for (; *p > ' '; ++p)
continue;
}
if (tmp)
PCHAR('>');
break;
case 'c':
PCHAR(va_arg(ap, int));
break;
case 'D':
up = va_arg(ap, u_char *);
p = va_arg(ap, char *);
if (!width)
width = 16;
while(width--) {
PCHAR(hex2ascii(*up >> 4));
PCHAR(hex2ascii(*up & 0x0f));
up++;
if (width)
for (q=p;*q;q++)
PCHAR(*q);
}
break;
case 'd':
case 'i':
base = 10;
sign = 1;
goto handle_sign;
case 'h':
if (hflag) {
hflag = 0;
cflag = 1;
} else
hflag = 1;
goto reswitch;
case 'j':
jflag = 1;
goto reswitch;
case 'l':
if (lflag) {
lflag = 0;
qflag = 1;
} else
lflag = 1;
goto reswitch;
case 'n':
if (jflag)
*(va_arg(ap, intmax_t *)) = retval;
else if (qflag)
*(va_arg(ap, quad_t *)) = retval;
else if (lflag)
*(va_arg(ap, long *)) = retval;
else if (zflag)
*(va_arg(ap, size_t *)) = retval;
else if (hflag)
*(va_arg(ap, short *)) = retval;
else if (cflag)
*(va_arg(ap, char *)) = retval;
else
*(va_arg(ap, int *)) = retval;
break;
case 'o':
base = 8;
goto handle_nosign;
case 'p':
base = 16;
sharpflag = (width == 0);
sign = 0;
num = (uintptr_t)va_arg(ap, void *);
goto number;
case 'q':
qflag = 1;
goto reswitch;
case 'r':
base = radix;
if (sign)
goto handle_sign;
goto handle_nosign;
case 's':
p = va_arg(ap, char *);
if (p == NULL)
p = "(null)";
if (!dot)
n = strlen (p);
else
for (n = 0; n < dwidth && p[n]; n++)
continue;
width -= n;
if (!ladjust && width > 0)
while (width--)
PCHAR(padc);
while (n--)
PCHAR(*p++);
if (ladjust && width > 0)
while (width--)
PCHAR(padc);
break;
case 't':
tflag = 1;
goto reswitch;
case 'u':
base = 10;
goto handle_nosign;
case 'X':
upper = 1;
case 'x':
base = 16;
goto handle_nosign;
case 'y':
base = 16;
sign = 1;
goto handle_sign;
case 'z':
zflag = 1;
goto reswitch;
handle_nosign:
sign = 0;
if (jflag)
num = va_arg(ap, uintmax_t);
else if (qflag)
num = va_arg(ap, u_quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, u_long);
else if (zflag)
num = va_arg(ap, size_t);
else if (hflag)
num = (u_short)va_arg(ap, int);
else if (cflag)
num = (u_char)va_arg(ap, int);
else
num = va_arg(ap, u_int);
goto number;
handle_sign:
if (jflag)
num = va_arg(ap, intmax_t);
else if (qflag)
num = va_arg(ap, quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, long);
else if (zflag)
num = va_arg(ap, ssize_t);
else if (hflag)
num = (short)va_arg(ap, int);
else if (cflag)
num = (char)va_arg(ap, int);
else
num = va_arg(ap, int);
number:
if (sign && (intmax_t)num < 0) {
neg = 1;
num = -(intmax_t)num;
}
p = ksprintn(nbuf, num, base, &n, upper);
tmp = 0;
if (sharpflag && num != 0) {
if (base == 8)
tmp++;
else if (base == 16)
tmp += 2;
}
if (neg)
tmp++;
if (!ladjust && padc == '0')
dwidth = width - tmp;
width -= tmp + imax(dwidth, n);
dwidth -= n;
if (!ladjust)
while (width-- > 0)
PCHAR(' ');
if (neg)
PCHAR('-');
if (sharpflag && num != 0) {
if (base == 8) {
PCHAR('0');
} else if (base == 16) {
PCHAR('0');
PCHAR('x');
}
}
while (dwidth-- > 0)
PCHAR('0');
while (*p)
PCHAR(*p--);
if (ladjust)
while (width-- > 0)
PCHAR(' ');
break;
default:
while (percent < fmt)
PCHAR(*percent++);
/*
- * Since we ignore an formatting argument it is no
+ * Since we ignore a formatting argument it is no
* longer safe to obey the remaining formatting
* arguments as the arguments will no longer match
* the format specs.
*/
stop = 1;
break;
}
}
#undef PCHAR
}
Index: head/share/man/man9/printf.9
===================================================================
--- head/share/man/man9/printf.9 (revision 277559)
+++ head/share/man/man9/printf.9 (revision 277560)
@@ -1,175 +1,175 @@
.\"
.\" Copyright (c) 2001 Andrew R. Reiter
.\" Copyright (c) 2004 Joerg Wunsch
.\" 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 ``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 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 September 8, 2006
.Dt PRINTF 9
.Os
.Sh NAME
.Nm printf , uprintf , tprintf, log
.Nd formatted output conversion
.Sh SYNOPSIS
.In sys/types.h
.In sys/systm.h
.Ft int
.Fn printf "const char *fmt" ...
.Ft void
.Fn tprintf "struct proc *p" "int pri" "const char *fmt" ...
.Ft int
.Fn uprintf "const char *fmt" ...
.In sys/syslog.h
.Ft void
.Fn log "int pri" "const char *fmt" ...
.Sh DESCRIPTION
The
.Xr printf 9
family of functions are similar to the
.Xr printf 3
family of functions.
The different functions each use a different output stream.
The
.Fn uprintf
function outputs to the current process' controlling tty, while
.Fn printf
writes to the console as well as to the logging facility.
The
.Fn tprintf
function outputs to the tty associated with the process
.Fa p
and the logging facility if
.Fa pri
is not \-1.
The
.Fn log
function sends the message to the kernel logging facility, using
the log level as indicated by
.Fa pri .
.Pp
Each of these related functions use the
.Fa fmt
parameter in the same manner as
.Xr printf 3 .
However,
.Xr printf 9
adds two other conversion specifiers.
.Pp
The
.Cm \&%b
identifier expects two arguments: an
.Vt int
and a
.Vt "char *" .
These are used as a register value and a print mask for decoding bitmasks.
The print mask is made up of two parts: the base and the
arguments.
The base value is the output base expressed as an integer value;
for example, \e10 gives octal and \e20 gives hexadecimal.
The arguments are made up of a sequence of bit identifiers.
Each bit identifier begins with an integer value which is the number of the
bit (starting from 1) this identifier describes.
The rest of the identifier is a string of characters containing the name of
the bit.
The string is terminated by either the bit number at the start of the next
bit identifier or
.Dv NUL
for the last bit identifier.
.Pp
The
.Cm \&%D
identifier is meant to assist in hexdumps.
It requires two arguments: a
.Vt "u_char *"
pointer and a
.Vt "char *"
string.
The memory pointed to be the pointer is output in hexadecimal one byte at
a time.
The string is used as a delimiter between individual bytes.
If present, a width directive will specify the number of bytes to display.
By default, 16 bytes of data are output.
.Pp
The
.Fn log
function uses
.Xr syslog 3
level values
.Dv LOG_DEBUG
through
.Dv LOG_EMERG
for its
.Fa pri
parameter (mistakenly called
.Sq priority
here).
Alternatively, if a
.Fa pri
of \-1 is given, the message will be appended to the last log message
started by a previous call to
.Fn log .
As these messages are generated by the kernel itself, the facility will
always be
.Dv LOG_KERN .
.Sh RETURN VALUES
The
.Fn printf
and the
.Fn uprintf
functions return the number of characters displayed.
.Sh EXAMPLES
This example demonstrates the use of the
.Cm \&%b
and
.Cm \&%D
conversion specifiers.
The function
.Bd -literal -offset indent
void
printf_test(void)
{
- printf("reg=%b\en", 3, "\e10\e2BITTWO\e1BITONE\en");
+ printf("reg=%b\en", 3, "\e10\e2BITTWO\e1BITONE");
printf("out: %4D\en", "AAAA", ":");
}
.Ed
.Pp
will produce the following output:
.Bd -literal -offset indent
reg=3
out: 41:41:41:41
.Ed
.Pp
The call
.Bd -literal -offset indent
log(LOG_DEBUG, "%s%d: been there.\en", sc->sc_name, sc->sc_unit);
.Ed
.Pp
will add the appropriate debug message at priority
.Dq Li kern.debug
to the system log.
.Sh SEE ALSO
.Xr printf 3 ,
.Xr syslog 3
Index: head/sys/kern/subr_prf.c
===================================================================
--- head/sys/kern/subr_prf.c (revision 277559)
+++ head/sys/kern/subr_prf.c (revision 277560)
@@ -1,1121 +1,1121 @@
/*-
* Copyright (c) 1986, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
#include "opt_printf.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DDB
#include
#endif
/*
* Note that stdarg.h and the ANSI style va_start macro is used for both
* ANSI and traditional C compilers.
*/
#include
#define TOCONS 0x01
#define TOTTY 0x02
#define TOLOG 0x04
/* Max number conversion buffer length: a u_quad_t in base 2, plus NUL byte. */
#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
struct putchar_arg {
int flags;
int pri;
struct tty *tty;
char *p_bufr;
size_t n_bufr;
char *p_next;
size_t remain;
};
struct snprintf_arg {
char *str;
size_t remain;
};
extern int log_open;
static void msglogchar(int c, int pri);
static void msglogstr(char *str, int pri, int filter_cr);
static void putchar(int ch, void *arg);
static char *ksprintn(char *nbuf, uintmax_t num, int base, int *len, int upper);
static void snprintf_func(int ch, void *arg);
static int msgbufmapped; /* Set when safe to use msgbuf */
int msgbuftrigger;
static int log_console_output = 1;
SYSCTL_INT(_kern, OID_AUTO, log_console_output, CTLFLAG_RWTUN,
&log_console_output, 0, "Duplicate console output to the syslog");
/*
* See the comment in log_console() below for more explanation of this.
*/
static int log_console_add_linefeed;
SYSCTL_INT(_kern, OID_AUTO, log_console_add_linefeed, CTLFLAG_RWTUN,
&log_console_add_linefeed, 0, "log_console() adds extra newlines");
static int always_console_output;
SYSCTL_INT(_kern, OID_AUTO, always_console_output, CTLFLAG_RWTUN,
&always_console_output, 0, "Always output to console despite TIOCCONS");
/*
* Warn that a system table is full.
*/
void
tablefull(const char *tab)
{
log(LOG_ERR, "%s: table is full\n", tab);
}
/*
* Uprintf prints to the controlling terminal for the current process.
*/
int
uprintf(const char *fmt, ...)
{
va_list ap;
struct putchar_arg pca;
struct proc *p;
struct thread *td;
int retval;
td = curthread;
if (TD_IS_IDLETHREAD(td))
return (0);
sx_slock(&proctree_lock);
p = td->td_proc;
PROC_LOCK(p);
if ((p->p_flag & P_CONTROLT) == 0) {
PROC_UNLOCK(p);
sx_sunlock(&proctree_lock);
return (0);
}
SESS_LOCK(p->p_session);
pca.tty = p->p_session->s_ttyp;
SESS_UNLOCK(p->p_session);
PROC_UNLOCK(p);
if (pca.tty == NULL) {
sx_sunlock(&proctree_lock);
return (0);
}
pca.flags = TOTTY;
pca.p_bufr = NULL;
va_start(ap, fmt);
tty_lock(pca.tty);
sx_sunlock(&proctree_lock);
retval = kvprintf(fmt, putchar, &pca, 10, ap);
tty_unlock(pca.tty);
va_end(ap);
return (retval);
}
/*
* tprintf and vtprintf print on the controlling terminal associated with the
* given session, possibly to the log as well.
*/
void
tprintf(struct proc *p, int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vtprintf(p, pri, fmt, ap);
va_end(ap);
}
void
vtprintf(struct proc *p, int pri, const char *fmt, va_list ap)
{
struct tty *tp = NULL;
int flags = 0;
struct putchar_arg pca;
struct session *sess = NULL;
sx_slock(&proctree_lock);
if (pri != -1)
flags |= TOLOG;
if (p != NULL) {
PROC_LOCK(p);
if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) {
sess = p->p_session;
sess_hold(sess);
PROC_UNLOCK(p);
tp = sess->s_ttyp;
if (tp != NULL && tty_checkoutq(tp))
flags |= TOTTY;
else
tp = NULL;
} else
PROC_UNLOCK(p);
}
pca.pri = pri;
pca.tty = tp;
pca.flags = flags;
pca.p_bufr = NULL;
if (pca.tty != NULL)
tty_lock(pca.tty);
sx_sunlock(&proctree_lock);
kvprintf(fmt, putchar, &pca, 10, ap);
if (pca.tty != NULL)
tty_unlock(pca.tty);
if (sess != NULL)
sess_release(sess);
msgbuftrigger = 1;
}
/*
* Ttyprintf displays a message on a tty; it should be used only by
* the tty driver, or anything that knows the underlying tty will not
* be revoke(2)'d away. Other callers should use tprintf.
*/
int
ttyprintf(struct tty *tp, const char *fmt, ...)
{
va_list ap;
struct putchar_arg pca;
int retval;
va_start(ap, fmt);
pca.tty = tp;
pca.flags = TOTTY;
pca.p_bufr = NULL;
retval = kvprintf(fmt, putchar, &pca, 10, ap);
va_end(ap);
return (retval);
}
static int
_vprintf(int level, int flags, const char *fmt, va_list ap)
{
struct putchar_arg pca;
int retval;
#ifdef PRINTF_BUFR_SIZE
char bufr[PRINTF_BUFR_SIZE];
#endif
pca.tty = NULL;
pca.pri = level;
pca.flags = flags;
#ifdef PRINTF_BUFR_SIZE
pca.p_bufr = bufr;
pca.p_next = pca.p_bufr;
pca.n_bufr = sizeof(bufr);
pca.remain = sizeof(bufr);
*pca.p_next = '\0';
#else
/* Don't buffer console output. */
pca.p_bufr = NULL;
#endif
retval = kvprintf(fmt, putchar, &pca, 10, ap);
#ifdef PRINTF_BUFR_SIZE
/* Write any buffered console/log output: */
if (*pca.p_bufr != '\0') {
if (pca.flags & TOLOG)
msglogstr(pca.p_bufr, level, /*filter_cr*/1);
if (pca.flags & TOCONS)
cnputs(pca.p_bufr);
}
#endif
return (retval);
}
/*
* Log writes to the log buffer, and guarantees not to sleep (so can be
* called by interrupt routines). If there is no process reading the
* log yet, it writes to the console also.
*/
void
log(int level, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void)_vprintf(level, log_open ? TOLOG : TOCONS, fmt, ap);
va_end(ap);
msgbuftrigger = 1;
}
#define CONSCHUNK 128
void
log_console(struct uio *uio)
{
int c, error, nl;
char *consbuffer;
int pri;
if (!log_console_output)
return;
pri = LOG_INFO | LOG_CONSOLE;
uio = cloneuio(uio);
consbuffer = malloc(CONSCHUNK, M_TEMP, M_WAITOK);
nl = 0;
while (uio->uio_resid > 0) {
c = imin(uio->uio_resid, CONSCHUNK - 1);
error = uiomove(consbuffer, c, uio);
if (error != 0)
break;
/* Make sure we're NUL-terminated */
consbuffer[c] = '\0';
if (consbuffer[c - 1] == '\n')
nl = 1;
else
nl = 0;
msglogstr(consbuffer, pri, /*filter_cr*/ 1);
}
/*
* The previous behavior in log_console() is preserved when
* log_console_add_linefeed is non-zero. For that behavior, if an
* individual console write came in that was not terminated with a
* line feed, it would add a line feed.
*
* This results in different data in the message buffer than
* appears on the system console (which doesn't add extra line feed
* characters).
*
* A number of programs and rc scripts write a line feed, or a period
* and a line feed when they have completed their operation. On
* the console, this looks seamless, but when displayed with
* 'dmesg -a', you wind up with output that looks like this:
*
* Updating motd:
* .
*
* On the console, it looks like this:
* Updating motd:.
*
* We could add logic to detect that situation, or just not insert
* the extra newlines. Set the kern.log_console_add_linefeed
* sysctl/tunable variable to get the old behavior.
*/
if (!nl && log_console_add_linefeed) {
consbuffer[0] = '\n';
consbuffer[1] = '\0';
msglogstr(consbuffer, pri, /*filter_cr*/ 1);
}
msgbuftrigger = 1;
free(uio, M_IOV);
free(consbuffer, M_TEMP);
return;
}
int
printf(const char *fmt, ...)
{
va_list ap;
int retval;
va_start(ap, fmt);
retval = vprintf(fmt, ap);
va_end(ap);
return (retval);
}
int
vprintf(const char *fmt, va_list ap)
{
int retval;
retval = _vprintf(-1, TOCONS | TOLOG, fmt, ap);
if (!panicstr)
msgbuftrigger = 1;
return (retval);
}
static void
putbuf(int c, struct putchar_arg *ap)
{
/* Check if no console output buffer was provided. */
if (ap->p_bufr == NULL) {
/* Output direct to the console. */
if (ap->flags & TOCONS)
cnputc(c);
if (ap->flags & TOLOG)
msglogchar(c, ap->pri);
} else {
/* Buffer the character: */
*ap->p_next++ = c;
ap->remain--;
/* Always leave the buffer zero terminated. */
*ap->p_next = '\0';
/* Check if the buffer needs to be flushed. */
if (ap->remain == 2 || c == '\n') {
if (ap->flags & TOLOG)
msglogstr(ap->p_bufr, ap->pri, /*filter_cr*/1);
if (ap->flags & TOCONS) {
if ((panicstr == NULL) && (constty != NULL))
msgbuf_addstr(&consmsgbuf, -1,
ap->p_bufr, /*filter_cr*/ 0);
if ((constty == NULL) ||(always_console_output))
cnputs(ap->p_bufr);
}
ap->p_next = ap->p_bufr;
ap->remain = ap->n_bufr;
*ap->p_next = '\0';
}
/*
* Since we fill the buffer up one character at a time,
* this should not happen. We should always catch it when
* ap->remain == 2 (if not sooner due to a newline), flush
* the buffer and move on. One way this could happen is
* if someone sets PRINTF_BUFR_SIZE to 1 or something
* similarly silly.
*/
KASSERT(ap->remain > 2, ("Bad buffer logic, remain = %zd",
ap->remain));
}
}
/*
* Print a character on console or users terminal. If destination is
* the console then the last bunch of characters are saved in msgbuf for
* inspection later.
*/
static void
putchar(int c, void *arg)
{
struct putchar_arg *ap = (struct putchar_arg*) arg;
struct tty *tp = ap->tty;
int flags = ap->flags;
/* Don't use the tty code after a panic or while in ddb. */
if (kdb_active) {
if (c != '\0')
cnputc(c);
return;
}
if ((flags & TOTTY) && tp != NULL && panicstr == NULL)
tty_putchar(tp, c);
if ((flags & (TOCONS | TOLOG)) && c != '\0')
putbuf(c, ap);
}
/*
* Scaled down version of sprintf(3).
*/
int
sprintf(char *buf, const char *cfmt, ...)
{
int retval;
va_list ap;
va_start(ap, cfmt);
retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
buf[retval] = '\0';
va_end(ap);
return (retval);
}
/*
* Scaled down version of vsprintf(3).
*/
int
vsprintf(char *buf, const char *cfmt, va_list ap)
{
int retval;
retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
buf[retval] = '\0';
return (retval);
}
/*
* Scaled down version of snprintf(3).
*/
int
snprintf(char *str, size_t size, const char *format, ...)
{
int retval;
va_list ap;
va_start(ap, format);
retval = vsnprintf(str, size, format, ap);
va_end(ap);
return(retval);
}
/*
* Scaled down version of vsnprintf(3).
*/
int
vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
struct snprintf_arg info;
int retval;
info.str = str;
info.remain = size;
retval = kvprintf(format, snprintf_func, &info, 10, ap);
if (info.remain >= 1)
*info.str++ = '\0';
return (retval);
}
/*
* Kernel version which takes radix argument vsnprintf(3).
*/
int
vsnrprintf(char *str, size_t size, int radix, const char *format, va_list ap)
{
struct snprintf_arg info;
int retval;
info.str = str;
info.remain = size;
retval = kvprintf(format, snprintf_func, &info, radix, ap);
if (info.remain >= 1)
*info.str++ = '\0';
return (retval);
}
static void
snprintf_func(int ch, void *arg)
{
struct snprintf_arg *const info = arg;
if (info->remain >= 2) {
*info->str++ = ch;
info->remain--;
}
}
/*
* Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
* order; return an optional length and a pointer to the last character
* written in the buffer (i.e., the first character of the string).
* The buffer pointed to by `nbuf' must have length >= MAXNBUF.
*/
static char *
ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
{
char *p, c;
p = nbuf;
*p = '\0';
do {
c = hex2ascii(num % base);
*++p = upper ? toupper(c) : c;
} while (num /= base);
if (lenp)
*lenp = p - nbuf;
return (p);
}
/*
* Scaled down version of printf(3).
*
* Two additional formats:
*
* The format %b is supported to decode error registers.
* Its usage is:
*
* printf("reg=%b\n", regval, "*");
*
* where is the output base expressed as a control character, e.g.
* \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
* the first of which gives the bit number to be inspected (origin 1), and
* the next characters (up to a control character, i.e. a character <= 32),
* give the name of the register. Thus:
*
- * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
+ * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
*
* would produce output:
*
* reg=3
*
* XXX: %D -- Hexdump, takes pointer and separator string:
* ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
* ("%*D", len, ptr, " " -> XX XX XX XX ...
*/
int
kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap)
{
#define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; }
char nbuf[MAXNBUF];
char *d;
const char *p, *percent, *q;
u_char *up;
int ch, n;
uintmax_t num;
int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
int cflag, hflag, jflag, tflag, zflag;
int dwidth, upper;
char padc;
int stop = 0, retval = 0;
num = 0;
if (!func)
d = (char *) arg;
else
d = NULL;
if (fmt == NULL)
fmt = "(fmt null)\n";
if (radix < 2 || radix > 36)
radix = 10;
for (;;) {
padc = ' ';
width = 0;
while ((ch = (u_char)*fmt++) != '%' || stop) {
if (ch == '\0')
return (retval);
PCHAR(ch);
}
percent = fmt - 1;
qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
sign = 0; dot = 0; dwidth = 0; upper = 0;
cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
reswitch: switch (ch = (u_char)*fmt++) {
case '.':
dot = 1;
goto reswitch;
case '#':
sharpflag = 1;
goto reswitch;
case '+':
sign = 1;
goto reswitch;
case '-':
ladjust = 1;
goto reswitch;
case '%':
PCHAR(ch);
break;
case '*':
if (!dot) {
width = va_arg(ap, int);
if (width < 0) {
ladjust = !ladjust;
width = -width;
}
} else {
dwidth = va_arg(ap, int);
}
goto reswitch;
case '0':
if (!dot) {
padc = '0';
goto reswitch;
}
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
for (n = 0;; ++fmt) {
n = n * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
if (dot)
dwidth = n;
else
width = n;
goto reswitch;
case 'b':
num = (u_int)va_arg(ap, int);
p = va_arg(ap, char *);
for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
PCHAR(*q--);
if (num == 0)
break;
for (tmp = 0; *p;) {
n = *p++;
if (num & (1 << (n - 1))) {
PCHAR(tmp ? ',' : '<');
for (; (n = *p) > ' '; ++p)
PCHAR(n);
tmp = 1;
} else
for (; *p > ' '; ++p)
continue;
}
if (tmp)
PCHAR('>');
break;
case 'c':
PCHAR(va_arg(ap, int));
break;
case 'D':
up = va_arg(ap, u_char *);
p = va_arg(ap, char *);
if (!width)
width = 16;
while(width--) {
PCHAR(hex2ascii(*up >> 4));
PCHAR(hex2ascii(*up & 0x0f));
up++;
if (width)
for (q=p;*q;q++)
PCHAR(*q);
}
break;
case 'd':
case 'i':
base = 10;
sign = 1;
goto handle_sign;
case 'h':
if (hflag) {
hflag = 0;
cflag = 1;
} else
hflag = 1;
goto reswitch;
case 'j':
jflag = 1;
goto reswitch;
case 'l':
if (lflag) {
lflag = 0;
qflag = 1;
} else
lflag = 1;
goto reswitch;
case 'n':
if (jflag)
*(va_arg(ap, intmax_t *)) = retval;
else if (qflag)
*(va_arg(ap, quad_t *)) = retval;
else if (lflag)
*(va_arg(ap, long *)) = retval;
else if (zflag)
*(va_arg(ap, size_t *)) = retval;
else if (hflag)
*(va_arg(ap, short *)) = retval;
else if (cflag)
*(va_arg(ap, char *)) = retval;
else
*(va_arg(ap, int *)) = retval;
break;
case 'o':
base = 8;
goto handle_nosign;
case 'p':
base = 16;
sharpflag = (width == 0);
sign = 0;
num = (uintptr_t)va_arg(ap, void *);
goto number;
case 'q':
qflag = 1;
goto reswitch;
case 'r':
base = radix;
if (sign)
goto handle_sign;
goto handle_nosign;
case 's':
p = va_arg(ap, char *);
if (p == NULL)
p = "(null)";
if (!dot)
n = strlen (p);
else
for (n = 0; n < dwidth && p[n]; n++)
continue;
width -= n;
if (!ladjust && width > 0)
while (width--)
PCHAR(padc);
while (n--)
PCHAR(*p++);
if (ladjust && width > 0)
while (width--)
PCHAR(padc);
break;
case 't':
tflag = 1;
goto reswitch;
case 'u':
base = 10;
goto handle_nosign;
case 'X':
upper = 1;
case 'x':
base = 16;
goto handle_nosign;
case 'y':
base = 16;
sign = 1;
goto handle_sign;
case 'z':
zflag = 1;
goto reswitch;
handle_nosign:
sign = 0;
if (jflag)
num = va_arg(ap, uintmax_t);
else if (qflag)
num = va_arg(ap, u_quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, u_long);
else if (zflag)
num = va_arg(ap, size_t);
else if (hflag)
num = (u_short)va_arg(ap, int);
else if (cflag)
num = (u_char)va_arg(ap, int);
else
num = va_arg(ap, u_int);
goto number;
handle_sign:
if (jflag)
num = va_arg(ap, intmax_t);
else if (qflag)
num = va_arg(ap, quad_t);
else if (tflag)
num = va_arg(ap, ptrdiff_t);
else if (lflag)
num = va_arg(ap, long);
else if (zflag)
num = va_arg(ap, ssize_t);
else if (hflag)
num = (short)va_arg(ap, int);
else if (cflag)
num = (char)va_arg(ap, int);
else
num = va_arg(ap, int);
number:
if (sign && (intmax_t)num < 0) {
neg = 1;
num = -(intmax_t)num;
}
p = ksprintn(nbuf, num, base, &n, upper);
tmp = 0;
if (sharpflag && num != 0) {
if (base == 8)
tmp++;
else if (base == 16)
tmp += 2;
}
if (neg)
tmp++;
if (!ladjust && padc == '0')
dwidth = width - tmp;
width -= tmp + imax(dwidth, n);
dwidth -= n;
if (!ladjust)
while (width-- > 0)
PCHAR(' ');
if (neg)
PCHAR('-');
if (sharpflag && num != 0) {
if (base == 8) {
PCHAR('0');
} else if (base == 16) {
PCHAR('0');
PCHAR('x');
}
}
while (dwidth-- > 0)
PCHAR('0');
while (*p)
PCHAR(*p--);
if (ladjust)
while (width-- > 0)
PCHAR(' ');
break;
default:
while (percent < fmt)
PCHAR(*percent++);
/*
- * Since we ignore a formatting argument it is no
+ * Since we ignore a formatting argument it is no
* longer safe to obey the remaining formatting
* arguments as the arguments will no longer match
* the format specs.
*/
stop = 1;
break;
}
}
#undef PCHAR
}
/*
* Put character in log buffer with a particular priority.
*/
static void
msglogchar(int c, int pri)
{
static int lastpri = -1;
static int dangling;
char nbuf[MAXNBUF];
char *p;
if (!msgbufmapped)
return;
if (c == '\0' || c == '\r')
return;
if (pri != -1 && pri != lastpri) {
if (dangling) {
msgbuf_addchar(msgbufp, '\n');
dangling = 0;
}
msgbuf_addchar(msgbufp, '<');
for (p = ksprintn(nbuf, (uintmax_t)pri, 10, NULL, 0); *p;)
msgbuf_addchar(msgbufp, *p--);
msgbuf_addchar(msgbufp, '>');
lastpri = pri;
}
msgbuf_addchar(msgbufp, c);
if (c == '\n') {
dangling = 0;
lastpri = -1;
} else {
dangling = 1;
}
}
static void
msglogstr(char *str, int pri, int filter_cr)
{
if (!msgbufmapped)
return;
msgbuf_addstr(msgbufp, pri, str, filter_cr);
}
void
msgbufinit(void *ptr, int size)
{
char *cp;
static struct msgbuf *oldp = NULL;
size -= sizeof(*msgbufp);
cp = (char *)ptr;
msgbufp = (struct msgbuf *)(cp + size);
msgbuf_reinit(msgbufp, cp, size);
if (msgbufmapped && oldp != msgbufp)
msgbuf_copy(oldp, msgbufp);
msgbufmapped = 1;
oldp = msgbufp;
}
static int unprivileged_read_msgbuf = 1;
SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf,
CTLFLAG_RW, &unprivileged_read_msgbuf, 0,
"Unprivileged processes may read the kernel message buffer");
/* Sysctls for accessing/clearing the msgbuf */
static int
sysctl_kern_msgbuf(SYSCTL_HANDLER_ARGS)
{
char buf[128];
u_int seq;
int error, len;
if (!unprivileged_read_msgbuf) {
error = priv_check(req->td, PRIV_MSGBUF);
if (error)
return (error);
}
/* Read the whole buffer, one chunk at a time. */
mtx_lock(&msgbuf_lock);
msgbuf_peekbytes(msgbufp, NULL, 0, &seq);
for (;;) {
len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq);
mtx_unlock(&msgbuf_lock);
if (len == 0)
return (0);
error = sysctl_handle_opaque(oidp, buf, len, req);
if (error)
return (error);
mtx_lock(&msgbuf_lock);
}
}
SYSCTL_PROC(_kern, OID_AUTO, msgbuf,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
NULL, 0, sysctl_kern_msgbuf, "A", "Contents of kernel message buffer");
static int msgbuf_clearflag;
static int
sysctl_kern_msgbuf_clear(SYSCTL_HANDLER_ARGS)
{
int error;
error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req);
if (!error && req->newptr) {
mtx_lock(&msgbuf_lock);
msgbuf_clear(msgbufp);
mtx_unlock(&msgbuf_lock);
msgbuf_clearflag = 0;
}
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, msgbuf_clear,
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE | CTLFLAG_MPSAFE,
&msgbuf_clearflag, 0, sysctl_kern_msgbuf_clear, "I",
"Clear kernel message buffer");
#ifdef DDB
DB_SHOW_COMMAND(msgbuf, db_show_msgbuf)
{
int i, j;
if (!msgbufmapped) {
db_printf("msgbuf not mapped yet\n");
return;
}
db_printf("msgbufp = %p\n", msgbufp);
db_printf("magic = %x, size = %d, r= %u, w = %u, ptr = %p, cksum= %u\n",
msgbufp->msg_magic, msgbufp->msg_size, msgbufp->msg_rseq,
msgbufp->msg_wseq, msgbufp->msg_ptr, msgbufp->msg_cksum);
for (i = 0; i < msgbufp->msg_size && !db_pager_quit; i++) {
j = MSGBUF_SEQ_TO_POS(msgbufp, i + msgbufp->msg_rseq);
db_printf("%c", msgbufp->msg_ptr[j]);
}
db_printf("\n");
}
#endif /* DDB */
void
hexdump(const void *ptr, int length, const char *hdr, int flags)
{
int i, j, k;
int cols;
const unsigned char *cp;
char delim;
if ((flags & HD_DELIM_MASK) != 0)
delim = (flags & HD_DELIM_MASK) >> 8;
else
delim = ' ';
if ((flags & HD_COLUMN_MASK) != 0)
cols = flags & HD_COLUMN_MASK;
else
cols = 16;
cp = ptr;
for (i = 0; i < length; i+= cols) {
if (hdr != NULL)
printf("%s", hdr);
if ((flags & HD_OMIT_COUNT) == 0)
printf("%04x ", i);
if ((flags & HD_OMIT_HEX) == 0) {
for (j = 0; j < cols; j++) {
k = i + j;
if (k < length)
printf("%c%02x", delim, cp[k]);
else
printf(" ");
}
}
if ((flags & HD_OMIT_CHARS) == 0) {
printf(" |");
for (j = 0; j < cols; j++) {
k = i + j;
if (k >= length)
printf(" ");
else if (cp[k] >= ' ' && cp[k] <= '~')
printf("%c", cp[k]);
else
printf(".");
}
printf("|");
}
printf("\n");
}
}