diff options
Diffstat (limited to 'src/kmk/kmkbuiltin/printf.c')
-rw-r--r-- | src/kmk/kmkbuiltin/printf.c | 954 |
1 files changed, 954 insertions, 0 deletions
diff --git a/src/kmk/kmkbuiltin/printf.c b/src/kmk/kmkbuiltin/printf.c new file mode 100644 index 0000000..9dc5956 --- /dev/null +++ b/src/kmk/kmkbuiltin/printf.c @@ -0,0 +1,954 @@ +/* $NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. + * 3. 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. + */ + +/*#include <sys/cdefs.h> +#ifndef lint +#if !defined(BUILTIN) && !defined(SHELL) +__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif +#endif + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95"; +#else +__RCSID("$NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl Exp $"); +#endif +#endif*/ /* not lint */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define FAKES_NO_GETOPT_H /* bird */ +#if !defined(KMK_BUILTIN_STANDALONE) && !defined(BUILTIN) && !defined(SHELL) +# include "../makeint.h" +# include "../filedef.h" +# include "../variable.h" +#else +# include "config.h" +#endif +#include <sys/types.h> + +#include <ctype.h> +#include "err.h" +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "getopt_r.h" +#ifdef __sun__ +# include "solfakes.h" +#endif +#ifdef _MSC_VER +# include "mscfakes.h" +#endif + +#include "../kmkbuiltin.h" + +#ifdef KBUILD_OS_WINDOWS +/* This is a trick to speed up console output on windows. */ +# include "console.h" +# undef fwrite +# define fwrite maybe_con_fwrite +#endif + +#if 0 +#ifdef BUILTIN /* csh builtin */ +#define kmk_builtin_printf progprintf +#endif + +#ifdef SHELL /* sh (aka ash) builtin */ +#define kmk_builtin_printf printfcmd +#include "../../bin/sh/bltin/bltin.h" +#endif /* SHELL */ +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#if 0 /*def __GNUC__ - bird: gcc complains about non-ISO-standard escape. */ +#define ESCAPE '\e' +#else +#define ESCAPE 033 +#endif + +#define PF(f, func) { \ + if (fieldwidth != -1) { \ + if (precision != -1) \ + (void)wrap_printf(pThis, f, fieldwidth, precision, func); \ + else \ + (void)wrap_printf(pThis, f, fieldwidth, func); \ + } else if (precision != -1) \ + (void)wrap_printf(pThis, f, precision, func); \ + else \ + (void)wrap_printf(pThis, f, func); \ +} + +#define APF(cpp, f, func) { \ + if (fieldwidth != -1) { \ + if (precision != -1) \ + (void)asprintf(cpp, f, fieldwidth, precision, func); \ + else \ + (void)asprintf(cpp, f, fieldwidth, func); \ + } else if (precision != -1) \ + (void)asprintf(cpp, f, precision, func); \ + else \ + (void)asprintf(cpp, f, func); \ +} + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct PRINTFINSTANCE +{ + PKMKBUILTINCTX pCtx; + /* former globals */ + size_t b_length; + char *b_fmt; + int rval; + char **gargv; +#ifndef KMK_BUILTIN_STANDALONE + char *g_o; +#endif + /* former function level statics in common_printf(); both need freeing. */ + char *a, *t; + + /* former function level statics in conv_expand(); needs freeing. */ + char *conv_str; + + /* Buffer the output because windows doesn't do line buffering of stdout. */ + size_t g_cchBuf; + char g_achBuf[256]; +} PRINTFINSTANCE; +typedef PRINTFINSTANCE *PPRINTFINSTANCE; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static struct option long_options[] = +{ + { "help", no_argument, 0, 261 }, + { "version", no_argument, 0, 262 }, + { 0, 0, 0, 0 }, +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int common_printf(PPRINTFINSTANCE pThis, char *argv[], PKMKBUILTINCTX pCtx); +static int common_printf_inner(PPRINTFINSTANCE pThis, char *argv[]); +static void conv_escape_str(PPRINTFINSTANCE, char *, void (*)(PPRINTFINSTANCE, int)); +static char *conv_escape(PPRINTFINSTANCE, char *, char *); +static const char *conv_expand(PPRINTFINSTANCE, const char *); +static int getchr(PPRINTFINSTANCE); +static double getdouble(PPRINTFINSTANCE); +static int getwidth(PPRINTFINSTANCE); +static intmax_t getintmax(PPRINTFINSTANCE); +static uintmax_t getuintmax(PPRINTFINSTANCE); +static char *getstr(PPRINTFINSTANCE); +static char *mklong(PPRINTFINSTANCE, const char *, int, char[64]); +static void check_conversion(PPRINTFINSTANCE, const char *, const char *); +static int usage(PKMKBUILTINCTX, int); + +static int flush_buffer(PPRINTFINSTANCE); +static void b_count(PPRINTFINSTANCE, int); +static void b_output(PPRINTFINSTANCE, int); +static int wrap_putchar(PPRINTFINSTANCE, int ch); +static int wrap_printf(PPRINTFINSTANCE, const char *, ...); + + + +int kmk_builtin_printf(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx) +{ + PRINTFINSTANCE This; + struct getopt_state_r gos; + int ch; + + getopt_initialize_r(&gos, argc, argv, "", long_options, envp, pCtx); + while ((ch = getopt_long_r(&gos, NULL)) != -1) { + switch (ch) { + case 261: + usage(pCtx, 0); + return 0; + case 262: + return kbuild_version(argv[0]); + case '?': + default: + return usage(pCtx, 1); + } + } + argc -= gos.optind; + argv += gos.optind; + + if (argc < 1) + return usage(pCtx, 1); + +#ifndef KMK_BUILTIN_STANDALONE + This.g_o = NULL; +#endif + return common_printf(&This, argv, pCtx); +} + +#ifdef KMK_BUILTIN_STANDALONE +int main(int argc, char **argv, char **envp) +{ + KMKBUILTINCTX Ctx = { "kmk_printf", NULL }; + setlocale(LC_ALL, ""); + return kmk_builtin_printf(argc, argv, envp, &Ctx); +} +#else /* KMK_BUILTIN_STANDALONE */ +/* entry point used by function.c $(printf ..,..). */ +char *kmk_builtin_func_printf(char *o, char **argv, const char *funcname) +{ + PRINTFINSTANCE This; + int rc; + int argc; + + for (argc = 0; argv[argc] != NULL; argc++) + /* nothing */; + if (argc == 0) + fatal(NILF, strlen(funcname) + INTSTR_LENGTH, _("$(%s): no format string\n"), funcname); + + This.g_o = o; + rc = common_printf(&This, argv, NULL); + o = This.g_o; + + if (rc != 0) + fatal(NILF, strlen(funcname) + INTSTR_LENGTH, _("$(%s): failure rc=%d\n"), funcname, rc); + return o; +} +#endif /* KMK_BUILTIN_STANDALONE */ + +static int common_printf(PPRINTFINSTANCE pThis, char *argv[], PKMKBUILTINCTX pCtx) +{ + int rc; + + /* Init all but g_o. */ + pThis->pCtx = pCtx; + pThis->b_length = 0; + pThis->b_fmt = NULL; + pThis->rval = 0; + pThis->gargv = NULL; + pThis->g_cchBuf = 0; + pThis->a = NULL; + pThis->t = NULL; + pThis->conv_str = NULL; + + rc = common_printf_inner(pThis, argv); + + /* Cleanup allocations. */ + if (pThis->a) { + free(pThis->a); + pThis->a = NULL; + } + if (pThis->t) { + free(pThis->t); + pThis->t = NULL; + } + if (pThis->conv_str) { + free(pThis->conv_str); + pThis->conv_str = NULL; + } + return rc; +} + +static int common_printf_inner(PPRINTFINSTANCE pThis, char *argv[]) +{ + char *fmt, *start; + int fieldwidth, precision; + char nextch; + char *format; + int ch; + char longbuf[64]; + + format = *argv; + pThis->gargv = ++argv; + +#define SKIP1 "#-+ 0" +#define SKIP2 "*0123456789" + do { + /* + * Basic algorithm is to scan the format string for conversion + * specifications -- once one is found, find out if the field + * width or precision is a '*'; if it is, gather up value. + * Note, format strings are reused as necessary to use up the + * provided arguments, arguments of zero/null string are + * provided to use up the format string. + */ + + /* find next format specification */ + for (fmt = format; (ch = *fmt++) != '\0';) { + if (ch == '\\') { + char c_ch; + fmt = conv_escape(pThis, fmt, &c_ch); + wrap_putchar(pThis, c_ch); + continue; + } + if (ch != '%' || (*fmt == '%' && ++fmt)) { + (void)wrap_putchar(pThis, ch); + continue; + } + + /* Ok - we've found a format specification, + Save its address for a later printf(). */ + start = fmt - 1; + + /* skip to field width */ + fmt += strspn(fmt, SKIP1); + fieldwidth = *fmt == '*' ? getwidth(pThis) : -1; + + /* skip to possible '.', get following precision */ + fmt += strspn(fmt, SKIP2); + if (*fmt == '.') + ++fmt; + precision = *fmt == '*' ? getwidth(pThis) : -1; + + fmt += strspn(fmt, SKIP2); + + ch = *fmt; + if (!ch) { + flush_buffer(pThis); + warnx(pThis->pCtx, "missing format character"); + return (1); + } + /* null terminate format string to we can use it + as an argument to printf. */ + nextch = fmt[1]; + fmt[1] = 0; + switch (ch) { + + case 'B': { + const char *p = conv_expand(pThis, getstr(pThis)); + *fmt = 's'; + PF(start, p); + break; + } + case 'b': { + /* There has to be a better way to do this, + * but the string we generate might have + * embedded nulls. */ + char *cp = getstr(pThis); + /* Free on entry in case shell longjumped out */ + if (pThis->a != NULL) { + free(pThis->a); + pThis->a = NULL; + } + if (pThis->t != NULL) { + free(pThis->t); + pThis->t = NULL; + } + /* Count number of bytes we want to output */ + pThis->b_length = 0; + conv_escape_str(pThis, cp, b_count); + pThis->t = malloc(pThis->b_length + 1); + if (pThis->t == NULL) + break; + memset(pThis->t, 'x', pThis->b_length); + pThis->t[pThis->b_length] = 0; + /* Get printf to calculate the lengths */ + *fmt = 's'; + APF(&pThis->a, start, pThis->t); + pThis->b_fmt = pThis->a; + /* Output leading spaces and data bytes */ + conv_escape_str(pThis, cp, b_output); + /* Add any trailing spaces */ + wrap_printf(pThis, "%s", pThis->b_fmt); + break; + } + case 'c': { + char p = getchr(pThis); + PF(start, p); + break; + } + case 's': { + char *p = getstr(pThis); + PF(start, p); + break; + } + case 'd': + case 'i': { + intmax_t p = getintmax(pThis); + char *f = mklong(pThis, start, ch, longbuf); + PF(f, p); + break; + } + case 'o': + case 'u': + case 'x': + case 'X': { + uintmax_t p = getuintmax(pThis); + char *f = mklong(pThis, start, ch, longbuf); + PF(f, p); + break; + } + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + double p = getdouble(pThis); + PF(start, p); + break; + } + default: + flush_buffer(pThis); + warnx(pThis->pCtx, "%s: invalid directive", start); + return 1; + } + *fmt++ = ch; + *fmt = nextch; + /* escape if a \c was encountered */ + if (pThis->rval & 0x100) { + flush_buffer(pThis); + return pThis->rval & ~0x100; + } + } + } while (pThis->gargv != argv && *pThis->gargv); + + flush_buffer(pThis); + return pThis->rval; +} + + +/* helper functions for conv_escape_str */ + +static void +/*ARGSUSED*/ +b_count(PPRINTFINSTANCE pThis, int ch) +{ + pThis->b_length++; + (void)ch; +} + +/* Output one converted character for every 'x' in the 'format' */ + +static void +b_output(PPRINTFINSTANCE pThis, int ch) +{ + for (;;) { + switch (*pThis->b_fmt++) { + case 0: + pThis->b_fmt--; + return; + case ' ': + wrap_putchar(pThis, ' '); + break; + default: + wrap_putchar(pThis, ch); + return; + } + } +} + +static int wrap_putchar(PPRINTFINSTANCE pThis, int ch) +{ +#ifndef KMK_BUILTIN_STANDALONE + if (pThis->g_o) { + char sz[2]; + sz[0] = ch; sz[1] = '\0'; + pThis->g_o = variable_buffer_output(pThis->g_o, sz, 1); + } + else +#endif + /* Buffered output. */ + if (pThis->g_cchBuf + 1 < sizeof(pThis->g_achBuf)) { + pThis->g_achBuf[pThis->g_cchBuf++] = ch; + } else { + int rc = flush_buffer(pThis); + pThis->g_achBuf[pThis->g_cchBuf++] = ch; + if (rc) + return -1; + } + return 0; +} + +static int wrap_printf(PPRINTFINSTANCE pThis, const char * fmt, ...) +{ + ssize_t cchRet; + va_list va; + char *pszTmp; + + va_start(va, fmt); + cchRet = vasprintf(&pszTmp, fmt, va); + va_end(va); + if (cchRet >= 0) { +#ifndef KMK_BUILTIN_STANDALONE + if (pThis->g_o) { + pThis->g_o = variable_buffer_output(pThis->g_o, pszTmp, cchRet); + } else +#endif + { + if (cchRet + pThis->g_cchBuf <= sizeof(pThis->g_achBuf)) { + /* We've got space in the buffer. */ + memcpy(&pThis->g_achBuf[pThis->g_cchBuf], pszTmp, cchRet); + pThis->g_cchBuf += cchRet; + } else { + /* Try write out complete lines. */ + const char *pszLeft = pszTmp; + ssize_t cchLeft = cchRet; + + while (cchLeft > 0) { + const char *pchNewLine = strchr(pszLeft, '\n'); + ssize_t cchLine = pchNewLine ? pchNewLine - pszLeft + 1 : cchLeft; + if (pThis->g_cchBuf + cchLine <= sizeof(pThis->g_achBuf)) { + memcpy(&pThis->g_achBuf[pThis->g_cchBuf], pszLeft, cchLine); + pThis->g_cchBuf += cchLine; + } else { + if (flush_buffer(pThis) < 0) { + return -1; + } +#ifndef KMK_BUILTIN_STANDALONE + if (output_write_text(pThis->pCtx->pOut, 0,pszLeft, cchLine) < 1) +#else + if (fwrite(pszLeft, cchLine, 1, stdout) < 1) +#endif + + return -1; + } + pszLeft += cchLine; + cchLeft -= cchLine; + } + } + } + free(pszTmp); + } + return (int)cchRet; +} + +/** + * Flushes the g_abBuf/g_cchBuf. + */ +static int flush_buffer(PPRINTFINSTANCE pThis) +{ + ssize_t cchToWrite = pThis->g_cchBuf; + if (cchToWrite > 0) { +#ifndef KMK_BUILTIN_STANDALONE + ssize_t cchWritten = output_write_text(pThis->pCtx->pOut, 0, pThis->g_achBuf, cchToWrite); +#else + ssize_t cchWritten = fwrite(pThis->g_achBuf, 1, cchToWrite, stdout); +#endif + pThis->g_cchBuf = 0; + if (cchWritten >= cchToWrite) { + /* likely */ + } else { + ssize_t off = cchWritten; + if (cchWritten >= 0) { + off = cchWritten; + } else if (errno == EINTR) { + cchWritten = 0; + } else { + return -1; + } + + while (off < cchToWrite) { +#ifndef KMK_BUILTIN_STANDALONE + cchWritten = output_write_text(pThis->pCtx->pOut, 0, &pThis->g_achBuf[off], cchToWrite - off); +#else + cchWritten = fwrite(&pThis->g_achBuf[off], 1, cchToWrite - off, stdout); +#endif + if (cchWritten > 0) { + off += cchWritten; + } else if (errno == EINTR) { + /* nothing */ + } else { + return -1; + } + } + } + } + return 0; +} + + + +/* + * Print SysV echo(1) style escape string + * Halts processing string if a \c escape is encountered. + */ +static void +conv_escape_str(PPRINTFINSTANCE pThis, char *str, void (*do_putchar)(PPRINTFINSTANCE, int)) +{ + int value; + int ch; + char c; + + while ((ch = *str++) != '\0') { + if (ch != '\\') { + do_putchar(pThis, ch); + continue; + } + + ch = *str++; + if (ch == 'c') { + /* \c as in SYSV echo - abort all processing.... */ + pThis->rval |= 0x100; + break; + } + + /* + * %b string octal constants are not like those in C. + * They start with a \0, and are followed by 0, 1, 2, + * or 3 octal digits. + */ + if (ch == '0') { + int octnum = 0, i; + for (i = 0; i < 3; i++) { + if (!isdigit((unsigned char)*str) || *str > '7') + break; + octnum = (octnum << 3) | (*str++ - '0'); + } + do_putchar(pThis, octnum); + continue; + } + + /* \[M][^|-]C as defined by vis(3) */ + if (ch == 'M' && *str == '-') { + do_putchar(pThis, 0200 | str[1]); + str += 2; + continue; + } + if (ch == 'M' && *str == '^') { + str++; + value = 0200; + ch = '^'; + } else + value = 0; + if (ch == '^') { + ch = *str++; + if (ch == '?') + value |= 0177; + else + value |= ch & 037; + do_putchar(pThis, value); + continue; + } + + /* Finally test for sequences valid in the format string */ + str = conv_escape(pThis, str - 1, &c); + do_putchar(pThis, c); + } +} + +/* + * Print "standard" escape characters + */ +static char * +conv_escape(PPRINTFINSTANCE pThis, char *str, char *conv_ch) +{ + int value; + int ch; + char num_buf[4], *num_end; + + ch = *str++; + + switch (ch) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + num_buf[0] = ch; + ch = str[0]; + num_buf[1] = ch; + num_buf[2] = ch ? str[1] : 0; + num_buf[3] = 0; + value = strtoul(num_buf, &num_end, 8); + str += num_end - (num_buf + 1); + break; + + case 'x': + /* Hexadecimal character constants are not required to be + supported (by SuS v1) because there is no consistent + way to detect the end of the constant. + Supporting 2 byte constants is a compromise. */ + ch = str[0]; + num_buf[0] = ch; + num_buf[1] = ch ? str[1] : 0; + num_buf[2] = 0; + value = strtoul(num_buf, &num_end, 16); + str += num_end - num_buf; + break; + + case '\\': value = '\\'; break; /* backslash */ + case '\'': value = '\''; break; /* single quote */ + case '"': value = '"'; break; /* double quote */ + case 'a': value = '\a'; break; /* alert */ + case 'b': value = '\b'; break; /* backspace */ + case 'e': value = ESCAPE; break; /* escape */ + case 'f': value = '\f'; break; /* form-feed */ + case 'n': value = '\n'; break; /* newline */ + case 'r': value = '\r'; break; /* carriage-return */ + case 't': value = '\t'; break; /* tab */ + case 'v': value = '\v'; break; /* vertical-tab */ + + default: + warnx(pThis->pCtx, "unknown escape sequence `\\%c'", ch); + pThis->rval = 1; + value = ch; + break; + } + + *conv_ch = value; + return str; +} + +/* expand a string so that everything is printable */ + +static const char * +conv_expand(PPRINTFINSTANCE pThis, const char *str) +{ + static const char no_memory[] = "<no memory>"; + char *cp; + int ch; + + if (pThis->conv_str) + free(pThis->conv_str); + /* get a buffer that is definitely large enough.... */ + pThis->conv_str = cp = malloc(4 * strlen(str) + 1); + if (!cp) + return no_memory; + + while ((ch = *(const unsigned char *)str++) != '\0') { + switch (ch) { + /* Use C escapes for expected control characters */ + case '\\': ch = '\\'; break; /* backslash */ + case '\'': ch = '\''; break; /* single quote */ + case '"': ch = '"'; break; /* double quote */ + case '\a': ch = 'a'; break; /* alert */ + case '\b': ch = 'b'; break; /* backspace */ + case ESCAPE: ch = 'e'; break; /* escape */ + case '\f': ch = 'f'; break; /* form-feed */ + case '\n': ch = 'n'; break; /* newline */ + case '\r': ch = 'r'; break; /* carriage-return */ + case '\t': ch = 't'; break; /* tab */ + case '\v': ch = 'v'; break; /* vertical-tab */ + default: + /* Copy anything printable */ + if (isprint(ch)) { + *cp++ = ch; + continue; + } + /* Use vis(3) encodings for the rest */ + *cp++ = '\\'; + if (ch & 0200) { + *cp++ = 'M'; + ch &= ~0200; + } + if (ch == 0177) { + *cp++ = '^'; + *cp++ = '?'; + continue; + } + if (ch < 040) { + *cp++ = '^'; + *cp++ = ch | 0100; + continue; + } + *cp++ = '-'; + *cp++ = ch; + continue; + } + *cp++ = '\\'; + *cp++ = ch; + } + + *cp = 0; + return pThis->conv_str; +} + +static char * +mklong(PPRINTFINSTANCE pThis, const char *str, int ch, char copy[64]) +{ + size_t len; + + len = strlen(str) - 1; + if (len > 64 - 5) { + warnx(pThis->pCtx, "format %s too complex\n", str); + len = 4; + } + (void)memmove(copy, str, len); +#ifndef _MSC_VER + copy[len++] = 'j'; +#else + copy[len++] = 'I'; + copy[len++] = '6'; + copy[len++] = '4'; +#endif + copy[len++] = ch; + copy[len] = '\0'; + return copy; +} + +static int +getchr(PPRINTFINSTANCE pThis) +{ + if (!*pThis->gargv) + return 0; + return (int)**pThis->gargv++; +} + +static char * +getstr(PPRINTFINSTANCE pThis) +{ + static char empty[] = ""; + if (!*pThis->gargv) + return empty; + return *pThis->gargv++; +} + +static int +getwidth(PPRINTFINSTANCE pThis) +{ + long val; + char *s, *ep; + + s = *pThis->gargv; + if (!s) + return (0); + pThis->gargv++; + + errno = 0; + val = strtoul(s, &ep, 0); + check_conversion(pThis, s, ep); + + /* Arbitrarily 'restrict' field widths to 1Mbyte */ + if (val < 0 || val > 1 << 20) { + warnx(pThis->pCtx, "%s: invalid field width", s); + return 0; + } + + return val; +} + +static intmax_t +getintmax(PPRINTFINSTANCE pThis) +{ + intmax_t val; + char *cp, *ep; + + cp = *pThis->gargv; + if (cp == NULL) + return 0; + pThis->gargv++; + + if (*cp == '\"' || *cp == '\'') + return *(cp+1); + + errno = 0; + val = strtoimax(cp, &ep, 0); + check_conversion(pThis, cp, ep); + return val; +} + +static uintmax_t +getuintmax(PPRINTFINSTANCE pThis) +{ + uintmax_t val; + char *cp, *ep; + + cp = *pThis->gargv; + if (cp == NULL) + return 0; + pThis->gargv++; + + if (*cp == '\"' || *cp == '\'') + return *(cp + 1); + + /* strtoumax won't error -ve values */ + while (isspace(*(unsigned char *)cp)) + cp++; + if (*cp == '-') { + warnx(pThis->pCtx, "%s: expected positive numeric value", cp); + pThis->rval = 1; + return 0; + } + + errno = 0; + val = strtoumax(cp, &ep, 0); + check_conversion(pThis, cp, ep); + return val; +} + +static double +getdouble(PPRINTFINSTANCE pThis) +{ + double val; + char *ep; + char *s; + + s = *pThis->gargv; + if (!s) + return (0.0); + pThis->gargv++; + + if (*s == '\"' || *s == '\'') + return (double) s[1]; + + errno = 0; + val = strtod(s, &ep); + check_conversion(pThis, s, ep); + return val; +} + +static void +check_conversion(PPRINTFINSTANCE pThis, const char *s, const char *ep) +{ + if (*ep) { + if (ep == s) + warnx(pThis->pCtx, "%s: expected numeric value", s); + else + warnx(pThis->pCtx, "%s: not completely converted", s); + pThis->rval = 1; + } else if (errno == ERANGE) { + warnx(pThis->pCtx, "%s: %s", s, strerror(ERANGE)); + pThis->rval = 1; + } +} + +static int +usage(PKMKBUILTINCTX pCtx, int fIsErr) +{ + kmk_builtin_ctx_printf(pCtx, fIsErr, + "usage: %s format [arg ...]\n" + " or: %s --help\n" + " or: %s --version\n", + pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName); + return 1; +} + |