/* print.c -- formatted printing routines (Paul Haahr, 12/91) */
#define PRINT_ALLOCSIZE ((size_t)64)
#define SPRINT_BUFSIZ ((size_t)1024)
* true return -> flag changes only, not a conversion
#define Flag(name, flag) \
static bool name(Format *format, int c) { \
Flag(uconv
, FMT_unsigned
)
Flag(altconv
, FMT_altform
)
Flag(leftconv
, FMT_leftside
)
static bool digitconv(Format
*format
, int c
) {
if (format
->flags
& FMT_f2set
)
format
->f2
= 10 * format
->f2
+ c
- '0';
format
->flags
|= FMT_f1set
;
format
->f1
= 10 * format
->f1
+ c
- '0';
static bool zeroconv(Format
*format
, int c
) {
if (format
->flags
& (FMT_f1set
| FMT_f2set
))
return digitconv(format
, '0');
format
->flags
|= FMT_zeropad
;
static void pad(Format
*format
, size_t len
, int c
) {
static bool sconv(Format
*format
, int c
) {
char *s
= va_arg(format
->args
, char *);
if ((format
->flags
& FMT_f1set
) == 0)
size_t len
= strlen(s
), width
= format
->f1
- len
;
if (format
->flags
& FMT_leftside
) {
fmtappend(format
, s
, len
);
fmtappend(format
, s
, len
);
static char *utoa(unsigned long u
, char *t
, unsigned int radix
, const char *digit
) {
t
= utoa(u
/ radix
, t
, radix
, digit
);
static void intconv(Format
*format
, unsigned int radix
, int upper
, const char *altform
) {
static const char * const table
[] = {
"0123456789abcdefghijklmnopqrstuvwxyz",
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
size_t len
, pre
, zeroes
, padding
, width
;
char number
[64], prefix
[20];
n
= va_arg(format
->args
, long);
else if (flags
& FMT_short
)
n
= va_arg(format
->args
, short);
n
= va_arg(format
->args
, int);
if ((flags
& FMT_unsigned
) || n
>= 0)
prefix
[pre
++] = *altform
++;
len
= utoa(u
, number
, radix
, table
[upper
]) - number
;
if ((flags
& FMT_f2set
) && (size_t) format
->f2
> len
)
zeroes
= format
->f2
- len
;
width
= pre
+ zeroes
+ len
;
if ((flags
& FMT_f1set
) && (size_t) format
->f1
> width
) {
padding
= format
->f1
- width
;
if (padding
> 0 && flags
& FMT_zeropad
) {
if ((flags
& FMT_leftside
) == 0) {
if ((flags
& FMT_leftside
) == 0)
pad(format
, padding
, padchar
);
fmtappend(format
, prefix
, pre
);
pad(format
, zeroes
, '0');
fmtappend(format
, number
, len
);
if (flags
& FMT_leftside
)
pad(format
, padding
, padchar
);
static bool cconv(Format
*format
, int c
) {
fmtputc(format
, va_arg(format
->args
, int));
static bool dconv(Format
*format
, int c
) {
intconv(format
, 10, 0, "");
static bool oconv(Format
*format
, int c
) {
intconv(format
, 8, 0, "0");
static bool xconv(Format
*format
, int c
) {
intconv(format
, 16, 0, "0x");
static bool pctconv(Format
*format
, int c
) {
static bool badconv(Format
*format
, int c
) {
panic("bad conversion character in printfmt");
return FALSE
; /* hush up gcc -Wall */
* conversion table management
static Conv fmttab
[MAXCONV
];
static void inittab(void) {
for (i
= 0; i
< MAXCONV
; i
++)
for (i
= '1'; i
<= '9'; i
++)
extern bool (*fmtinstall(int c
, bool (*f
)(Format
*, int)))(Format
*, int) {
/*Conv fmtinstall(int c, Conv f) {*/
* functions for inserting strings in the format buffer
extern void fmtappend(Format
*format
, const char *s
, size_t len
) {
while (format
->buf
+ len
> format
->bufend
) {
size_t split
= format
->bufend
- format
->buf
;
memcpy(format
->buf
, s
, split
);
(*format
->grow
)(format
, len
);
memcpy(format
->buf
, s
, len
);
extern void fmtcat(Format
*format
, const char *s
) {
fmtappend(format
, s
, strlen(s
));
* printfmt -- the driver routine
extern int printfmt(Format
*format
, const char *fmt
) {
unsigned const char *s
= (unsigned const char *) fmt
;
format
->flags
= format
->f1
= format
->f2
= 0;
while ((*fmttab
[c
])(format
, c
));
return format
->buf
- format
->bufbegin
+ format
->flushed
;
* the public entry points
extern int fmtprint(Format
*format
, const char *fmt
,...) {
int n
= -format
->flushed
;
va_list saveargs
= format
->args
;
va_start(format
->args
, fmt
);
n
+= printfmt(format
, fmt
);
return n
+ format
->flushed
;
static void fprint_flush(Format
*format
, size_t more
) {
size_t n
= format
->buf
- format
->bufbegin
;
char *buf
= format
->bufbegin
;
format
->buf
= format
->bufbegin
;
writeall(format
->u
.n
, buf
, n
);
extern int fprint(int fd
, const char *fmt
,...) {
format
.bufend
= buf
+ sizeof buf
;
format
.grow
= fprint_flush
;
va_start(format
.args
, fmt
);
fprint_flush(&format
, (size_t) 0);
static void memprint_grow(Format
*format
, size_t more
) {
size_t len
= format
->bufend
- format
->bufbegin
+ 1;
: ((len
+ more
) + PRINT_ALLOCSIZE
) &~ (PRINT_ALLOCSIZE
- 1);
buf
= erealloc(format
->bufbegin
, len
);
size_t used
= format
->buf
- format
->bufbegin
;
memcpy(buf
, format
->bufbegin
, used
);
format
->buf
= buf
+ (format
->buf
- format
->bufbegin
);
format
->bufend
= buf
+ len
- 1;
static char *memprint(Format
*format
, const char *fmt
, char *buf
, size_t len
) {
format
->bufend
= buf
+ len
- 1;
format
->grow
= memprint_grow
;
extern char *mprint(const char *fmt
,...) {
va_start(format
.args
, fmt
);
result
= memprint(&format
, fmt
, ealloc(PRINT_ALLOCSIZE
), PRINT_ALLOCSIZE
);
extern char *nprint(const char *fmt
,...) {
va_start(format
.args
, fmt
);
result
= memprint(&format
, fmt
, nalloc(PRINT_ALLOCSIZE
), PRINT_ALLOCSIZE
);