]> git.saurik.com Git - apple/shell_cmds.git/blobdiff - printf/printf.c
shell_cmds-203.tar.gz
[apple/shell_cmds.git] / printf / printf.c
index 9ac5fbd40f40e70f01a009f5bf2988458cf9531d..438b85ece61f18d90bbe68f01446a783386be8e3 100644 (file)
@@ -1,6 +1,6 @@
-/*     $NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $        */
-
-/*
+/*-
+ * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1989, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * 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. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 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.
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+/*
+ * Important: This file is used both as a standalone program /usr/bin/printf
+ * and as a builtin for /bin/sh (#define SHELL).
+ */
 
-#include <sys/cdefs.h>
+#ifndef SHELL
 #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
+static char const copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
 #endif
 
 #ifndef lint
 #if 0
-static char sccsid[] = "@(#)printf.c   8.2 (Berkeley) 3/22/95";
-#else
-__RCSID("$NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $");
+static char const sccsid[] = "@(#)printf.c     8.1 (Berkeley) 7/20/93";
 #endif
+static const char rcsid[] =
+  "$FreeBSD: head/usr.bin/printf/printf.c 279503 2015-03-01 21:46:55Z jilles $";
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -54,497 +54,633 @@ __RCSID("$NetBSD: printf.c,v 1.19 1998/02/03 03:10:15 perry Exp $");
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
 #include <unistd.h>
-
-static int      print_escape_str __P((const char *));
-static size_t   print_escape __P((const char *));
-
-static int      getchr __P((void));
-static double   getdouble __P((void));
-static int      getint __P((void));
-static long     getlong __P((void));
-static unsigned long getulong __P ((void));
-static char    *getstr __P((void));
-static char    *mklong __P((const char *, int)); 
-static void      check_conversion __P((const char *, const char *));
-static void     usage __P((void)); 
-     
-static int     rval;
-static char  **gargv;
-
-#ifdef BUILTIN
-int progprintf __P((int, char **));
-#else
-int main __P((int, char **));
-#endif
-
-#define isodigit(c)    ((c) >= '0' && (c) <= '7')
-#define octtobin(c)    ((c) - '0')
-#define hextobin(c)    ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
+#include <wchar.h>
 
 #ifdef SHELL
-#define main printfcmd
-#ifdef __APPLE__
+#define        main printfcmd
 #include "bltin/bltin.h"
-#else
-#include "../../bin/sh/bltin/bltin.h"
-#endif
-
-#ifdef __STDC__
-#include <stdarg.h>
-#else
-#include <vararg.h>
-#endif
-
-static void warnx __P((const char *fmt, ...));
-
-static void 
-#ifdef __STDC__
-warnx(const char *fmt, ...)
-#else
-warnx(fmt, va_alist)
-       const char *fmt;
-       va_dcl
-#endif
-{
-       
-       char buf[64];
-       va_list ap;
-
-#ifdef __STDC__
-       va_start(ap, fmt);
-#else
-       va_start(ap);
+#include "options.h"
 #endif
-       vsprintf(buf, fmt, ap);
-       va_end(ap);
 
-       error(buf);
-}
-#endif /* SHELL */
-
-#define PF(f, func) { \
-       if (fieldwidth) \
-               if (precision) \
-                       (void)printf(f, fieldwidth, precision, func); \
-               else \
-                       (void)printf(f, fieldwidth, func); \
-       else if (precision) \
-               (void)printf(f, precision, func); \
-       else \
-               (void)printf(f, func); \
-}
+#define        PF(f, func) do {                                                \
+       char *b = NULL;                                                 \
+       if (havewidth)                                                  \
+               if (haveprec)                                           \
+                       (void)asprintf(&b, f, fieldwidth, precision, func); \
+               else                                                    \
+                       (void)asprintf(&b, f, fieldwidth, func);        \
+       else if (haveprec)                                              \
+               (void)asprintf(&b, f, precision, func);                 \
+       else                                                            \
+               (void)asprintf(&b, f, func);                            \
+       if (b) {                                                        \
+               (void)fputs(b, stdout);                                 \
+               free(b);                                                \
+       }                                                               \
+} while (0)
+
+static int      asciicode(void);
+static char    *printf_doformat(char *, int *);
+static int      escape(char *, int, size_t *);
+static int      getchr(void);
+static int      getfloating(long double *, int);
+static int      getint(int *);
+static int      getnum(intmax_t *, uintmax_t *, int);
+static const char
+               *getstr(void);
+static char    *mknum(char *, char);
+static void     usage(void);
+
+static const char digits[] = "0123456789";
+
+static char end_fmt[1];
+
+static int  myargc;
+static char **myargv;
+static char **gargv;
+static char **maxargv;
 
 int
-#ifdef BUILTIN
-progprintf(argc, argv)
-#else
-main(argc, argv)
-#endif
-       int argc;
-       char *argv[];
+main(int argc, char *argv[])
 {
-       char *fmt, *start;
-       int fieldwidth, precision;
-       char convch, nextch;
-       char *format;
+       size_t len;
+       int end, rval;
+       char *format, *fmt, *start;
+#ifndef SHELL
        int ch;
 
-#if !defined(SHELL) && !defined(BUILTIN)
-       (void)setlocale (LC_ALL, "");
+       (void) setlocale(LC_ALL, "");
 #endif
 
-       while ((ch = getopt(argc, argv, "")) != -1) {
+#ifdef SHELL
+       nextopt("");
+       argc -= argptr - argv;
+       argv = argptr;
+#else
+       while ((ch = getopt(argc, argv, "")) != -1)
                switch (ch) {
                case '?':
                default:
                        usage();
                        return (1);
                }
-       }
        argc -= optind;
        argv += optind;
+#endif
 
        if (argc < 1) {
                usage();
                return (1);
        }
 
-       format = *argv;
+#ifdef SHELL
+       INTOFF;
+#endif
+       /*
+        * 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.
+        */
+       fmt = format = *argv;
+       escape(fmt, 1, &len);           /* backslash interpretation */
+       rval = end = 0;
        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; *fmt; fmt++) {
-                       switch (*fmt) {
-                       case '%':
-                               start = fmt++;
-
-                               if (*fmt == '%') {
-                                       (void)putchar('%');
-                                       break;
-                               } else if (*fmt == 'b') {
-                                       char *p = getstr();
-                                       if (print_escape_str(p)) {
-                                               return (rval);
+       for (;;) {
+               maxargv = gargv;
+
+               myargv = gargv;
+               for (myargc = 0; gargv[myargc]; myargc++)
+                       /* nop */;
+               start = fmt;
+               while (fmt < format + len) {
+                       if (fmt[0] == '%') {
+                               fwrite(start, 1, fmt - start, stdout);
+                               if (fmt[1] == '%') {
+                                       /* %% prints a % */
+                                       putchar('%');
+                                       fmt += 2;
+                               } else {
+                                       fmt = printf_doformat(fmt, &rval);
+                                       if (fmt == NULL || fmt == end_fmt) {
+#ifdef SHELL
+                                               INTON;
+#endif
+                                               return (fmt == NULL ? 1 : rval);
                                        }
-                                       break;
-                               }
-
-                               /* skip to field width */
-                               for (; strchr(SKIP1, *fmt); ++fmt) ;
-                               fieldwidth = *fmt == '*' ? getint() : 0;
-
-                               /* skip to possible '.', get following precision */
-                               for (; strchr(SKIP2, *fmt); ++fmt) ;
-                               if (*fmt == '.')
-                                       ++fmt;
-                               precision = *fmt == '*' ? getint() : 0;
-
-                               for (; strchr(SKIP2, *fmt); ++fmt) ;
-                               if (!*fmt) {
-                                       warnx ("missing format character");
-                                       return(1);
-                               }
-
-                               convch = *fmt;
-                               nextch = *(fmt + 1);
-                               *(fmt + 1) = '\0';
-                               switch(convch) {
-                               case 'c': {
-                                       char p = getchr();
-                                       PF(start, p);
-                                       break;
-                               }
-                               case 's': {
-                                       char *p = getstr();
-                                       PF(start, p);
-                                       break;
-                               }
-                               case 'd':
-                               case 'i': {
-                                       char *f = mklong(start, convch);
-                                       long p = getlong();
-                                       PF(f, p);
-                                       break;
-                               }
-                               case 'o':
-                               case 'u':
-                               case 'x':
-                               case 'X': {
-                                       char *f = mklong(start, convch);
-                                       unsigned long p = getulong();
-                                       PF(f, p);
-                                       break;
+                                       end = 0;
                                }
-                               case 'e':
-                               case 'E':
-                               case 'f':
-                               case 'g':
-                               case 'G': {
-                                       double p = getdouble();
-                                       PF(start, p);
-                                       break;
-                               }
-                               default:
-                                       warnx ("%s: invalid directive", start);
-                                       return(1);
-                               }
-                               *(fmt + 1) = nextch;
-                               break;
-
-                       case '\\':
-                               fmt += print_escape(fmt);
-                               break;
-
-                       default:
-                               (void)putchar(*fmt);
-                               break;
-                       }
+                               start = fmt;
+                       } else
+                               fmt++;
+                       if (gargv > maxargv)
+                               maxargv = gargv;
                }
-       } while (gargv > argv && *gargv);
+               gargv = maxargv;
 
-       return (rval);
+               if (end == 1) {
+                       warnx("missing format character");
+#ifdef SHELL
+                       INTON;
+#endif
+                       return (1);
+               }
+               fwrite(start, 1, fmt - start, stdout);
+               if (!*gargv) {
+#ifdef SHELL
+                       INTON;
+#endif
+                       return (rval);
+               }
+               /* Restart at the beginning of the format string. */
+               fmt = format;
+               end = 1;
+       }
+       /* NOTREACHED */
 }
 
 
-/*
- * Print SysV echo(1) style escape string 
- *     Halts processing string and returns 1 if a \c escape is encountered.
- */
-static int
-print_escape_str(str)
-       const char *str;
+static char *
+printf_doformat(char *fmt, int *rval)
 {
-       int value;
-       int c;
-
-       while (*str) {
-               if (*str == '\\') {
-                       str++;
-                       /* 
-                        * %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 (*str == '0') {
-                               str++;
-                               for (c = 3, value = 0; c-- && isodigit(*str); str++) {
-                                       value <<= 3;
-                                       value += octtobin(*str);
-                               }
-                               (void)putchar(value);
-                               str--;
-                       } else if (*str == 'c') {
-                               return 1;
-                       } else {
-                               str--;                  
-                               str += print_escape(str);
-                       }
+       static const char skip1[] = "#'-+ 0";
+       int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
+       char convch, nextch;
+       char start[strlen(fmt) + 1];
+       char **fargv;
+       char *dptr;
+       int l;
+
+       dptr = start;
+       *dptr++ = '%';
+       *dptr = 0;
+
+       fmt++;
+
+       /* look for "n$" field index specifier */
+       l = strspn(fmt, digits);
+       if ((l > 0) && (fmt[l] == '$')) {
+               int idx = atoi(fmt);
+               if (idx <= myargc) {
+                       gargv = &myargv[idx - 1];
                } else {
-                       (void)putchar(*str);
+                       gargv = &myargv[myargc];
                }
-               str++;
+               if (gargv > maxargv)
+                       maxargv = gargv;
+               fmt += l + 1;
+
+               /* save format argument */
+               fargv = gargv;
+       } else {
+               fargv = NULL;
        }
 
-       return 0;
-}
-
-/*
- * Print "standard" escape characters 
- */
-static size_t
-print_escape(str)
-       const char *str;
-{
-       const char *start = str;
-       int value;
-       int c;
+       /* skip to field width */
+       while (*fmt && strchr(skip1, *fmt) != NULL) {
+               *dptr++ = *fmt++;
+               *dptr = 0;
+       }
 
-       str++;
+       if (*fmt == '*') {
 
-       switch (*str) {
-       case '0': case '1': case '2': case '3':
-       case '4': case '5': case '6': case '7':
-               for (c = 3, value = 0; c-- && isodigit(*str); str++) {
-                       value <<= 3;
-                       value += octtobin(*str);
-               }
-               (void)putchar(value);
-               return str - start - 1;
-               /* NOTREACHED */
-
-       case 'x':
-               str++;
-               for (value = 0; isxdigit(*str); str++) {
-                       value <<= 4;
-                       value += hextobin(*str);
+               fmt++;
+               l = strspn(fmt, digits);
+               if ((l > 0) && (fmt[l] == '$')) {
+                       int idx = atoi(fmt);
+                       if (fargv == NULL) {
+                               warnx("incomplete use of n$");
+                               return (NULL);
+                       }
+                       if (idx <= myargc) {
+                               gargv = &myargv[idx - 1];
+                       } else {
+                               gargv = &myargv[myargc];
+                       }
+                       fmt += l + 1;
+               } else if (fargv != NULL) {
+                       warnx("incomplete use of n$");
+                       return (NULL);
                }
-               if (value > UCHAR_MAX) {
-                       warnx ("escape sequence out of range for character");
-                       rval = 1;
+
+               if (getint(&fieldwidth))
+                       return (NULL);
+               if (gargv > maxargv)
+                       maxargv = gargv;
+               havewidth = 1;
+
+               *dptr++ = '*';
+               *dptr = 0;
+       } else {
+               havewidth = 0;
+
+               /* skip to possible '.', get following precision */
+               while (isdigit(*fmt)) {
+                       *dptr++ = *fmt++;
+                       *dptr = 0;
                }
-               (void)putchar (value);
-               return str - start - 1;
-               /* NOTREACHED */
+       }
 
-       case '\\':                      /* backslash */
-               (void)putchar('\\');
-               break;
+       if (*fmt == '.') {
+               /* precision present? */
+               fmt++;
+               *dptr++ = '.';
 
-       case '\'':                      /* single quote */
-               (void)putchar('\'');
-               break;
+               if (*fmt == '*') {
 
-       case '"':                       /* double quote */
-               (void)putchar('"');
-               break;
+                       fmt++;
+                       l = strspn(fmt, digits);
+                       if ((l > 0) && (fmt[l] == '$')) {
+                               int idx = atoi(fmt);
+                               if (fargv == NULL) {
+                                       warnx("incomplete use of n$");
+                                       return (NULL);
+                               }
+                               if (idx <= myargc) {
+                                       gargv = &myargv[idx - 1];
+                               } else {
+                                       gargv = &myargv[myargc];
+                               }
+                               fmt += l + 1;
+                       } else if (fargv != NULL) {
+                               warnx("incomplete use of n$");
+                               return (NULL);
+                       }
 
-       case 'a':                       /* alert */
-#ifdef __STDC__
-               (void)putchar('\a');
-#else
-               (void)putchar(007);
-#endif
-               break;
+                       if (getint(&precision))
+                               return (NULL);
+                       if (gargv > maxargv)
+                               maxargv = gargv;
+                       haveprec = 1;
+                       *dptr++ = '*';
+                       *dptr = 0;
+               } else {
+                       haveprec = 0;
 
-       case 'b':                       /* backspace */
-               (void)putchar('\b');
-               break;
+                       /* skip to conversion char */
+                       while (isdigit(*fmt)) {
+                               *dptr++ = *fmt++;
+                               *dptr = 0;
+                       }
+               }
+       } else
+               haveprec = 0;
+       if (!*fmt) {
+               warnx("missing format character");
+               return (NULL);
+       }
+       *dptr++ = *fmt;
+       *dptr = 0;
+
+       /*
+        * Look for a length modifier.  POSIX doesn't have these, so
+        * we only support them for floating-point conversions, which
+        * are extensions.  This is useful because the L modifier can
+        * be used to gain extra range and precision, while omitting
+        * it is more likely to produce consistent results on different
+        * architectures.  This is not so important for integers
+        * because overflow is the only bad thing that can happen to
+        * them, but consider the command  printf %a 1.1
+        */
+       if (*fmt == 'L') {
+               mod_ldbl = 1;
+               fmt++;
+               if (!strchr("aAeEfFgG", *fmt)) {
+                       warnx("bad modifier L for %%%c", *fmt);
+                       return (NULL);
+               }
+       } else {
+               mod_ldbl = 0;
+       }
 
-       case 'e':                       /* escape */
-#ifdef __GNUC__
-               (void)putchar('\e');
-#else
-               (void)putchar(033);
-#endif
-               break;
+       /* save the current arg offset, and set to the format arg */
+       if (fargv != NULL) {
+               gargv = fargv;
+       }
 
-       case 'f':                       /* form-feed */
-               (void)putchar('\f');
-               break;
+       convch = *fmt;
+       nextch = *++fmt;
 
-       case 'n':                       /* newline */
-               (void)putchar('\n');
-               break;
+       *fmt = '\0';
+       switch (convch) {
+       case 'b': {
+               size_t len;
+               char *p;
+               int getout;
 
-       case 'r':                       /* carriage-return */
-               (void)putchar('\r');
+               p = strdup(getstr());
+               if (p == NULL) {
+                       warnx("%s", strerror(ENOMEM));
+                       return (NULL);
+               }
+               getout = escape(p, 0, &len);
+               fputs(p, stdout);
+               free(p);
+               if (getout)
+                       return (end_fmt);
                break;
+       }
+       case 'c': {
+               char p;
 
-       case 't':                       /* tab */
-               (void)putchar('\t');
+               p = getchr();
+               PF(start, p);
                break;
+       }
+       case 's': {
+               const char *p;
 
-       case 'v':                       /* vertical-tab */
-               (void)putchar('\v');
+               p = getstr();
+               PF(start, p);
                break;
-
-       default:
-               (void)putchar(*str);
-               warnx("unknown escape sequence `\\%c'", *str);
-               rval = 1;
+       }
+       case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
+               char *f;
+               intmax_t val;
+               uintmax_t uval;
+               int signedconv;
+
+               signedconv = (convch == 'd' || convch == 'i');
+               if ((f = mknum(start, convch)) == NULL)
+                       return (NULL);
+               if (getnum(&val, &uval, signedconv))
+                       *rval = 1;
+               if (signedconv)
+                       PF(f, val);
+               else
+                       PF(f, uval);
                break;
        }
-
-       return 1;
+       case 'e': case 'E':
+       case 'f': case 'F':
+       case 'g': case 'G':
+       case 'a': case 'A': {
+               long double p;
+
+               if (getfloating(&p, mod_ldbl))
+                       *rval = 1;
+               if (mod_ldbl)
+                       PF(start, p);
+               else
+                       PF(start, (double)p);
+               break;
+       }
+       default:
+               warnx("illegal format character %c", convch);
+               return (NULL);
+       }
+       *fmt = nextch;
+       /* return the gargv to the next element */
+       return (fmt);
 }
 
 static char *
-mklong(str, ch)
-       const char *str;
-       char ch;
+mknum(char *str, char ch)
 {
-       static char copy[64];
-       size_t len;     
+       static char *copy;
+       static size_t copy_size;
+       char *newcopy;
+       size_t len, newlen;
 
        len = strlen(str) + 2;
-       (void)memmove(copy, str, len - 3);
-       copy[len - 3] = 'l';
+       if (len > copy_size) {
+               newlen = ((len + 1023) >> 10) << 10;
+               if ((newcopy = realloc(copy, newlen)) == NULL) {
+                       warnx("%s", strerror(ENOMEM));
+                       return (NULL);
+               }
+               copy = newcopy;
+               copy_size = newlen;
+       }
+
+       memmove(copy, str, len - 3);
+       copy[len - 3] = 'j';
        copy[len - 2] = ch;
        copy[len - 1] = '\0';
-       return (copy);  
+       return (copy);
 }
 
 static int
-getchr()
+escape(char *fmt, int percent, size_t *len)
+{
+       char *save, *store, c;
+       int value;
+
+       for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
+               if (c != '\\') {
+                       *store = c;
+                       continue;
+               }
+               switch (*++fmt) {
+               case '\0':              /* EOS, user error */
+                       *store = '\\';
+                       *++store = '\0';
+                       *len = store - save;
+                       return (0);
+               case '\\':              /* backslash */
+               case '\'':              /* single quote */
+                       *store = *fmt;
+                       break;
+               case 'a':               /* bell/alert */
+                       *store = '\a';
+                       break;
+               case 'b':               /* backspace */
+                       *store = '\b';
+                       break;
+               case 'c':
+                       if (!percent) {
+                               *store = '\0';
+                               *len = store - save;
+                               return (1);
+                       }
+                       *store = 'c';
+                       break;
+               case 'f':               /* form-feed */
+                       *store = '\f';
+                       break;
+               case 'n':               /* newline */
+                       *store = '\n';
+                       break;
+               case 'r':               /* carriage-return */
+                       *store = '\r';
+                       break;
+               case 't':               /* horizontal tab */
+                       *store = '\t';
+                       break;
+               case 'v':               /* vertical tab */
+                       *store = '\v';
+                       break;
+                                       /* octal constant */
+               case '0': case '1': case '2': case '3':
+               case '4': case '5': case '6': case '7':
+                       c = (!percent && *fmt == '0') ? 4 : 3;
+                       for (value = 0;
+                           c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
+                               value <<= 3;
+                               value += *fmt - '0';
+                       }
+                       --fmt;
+                       if (percent && value == '%') {
+                               *store++ = '%';
+                               *store = '%';
+                       } else
+                               *store = (char)value;
+                       break;
+               default:
+                       *store = *fmt;
+                       break;
+               }
+       }
+       *store = '\0';
+       *len = store - save;
+       return (0);
+}
+
+static int
+getchr(void)
 {
        if (!*gargv)
                return ('\0');
        return ((int)**gargv++);
 }
 
-static char *
-getstr()
+static const char *
+getstr(void)
 {
        if (!*gargv)
                return ("");
        return (*gargv++);
 }
 
-static char *Number = "+-.0123456789";
 static int
-getint()
+getint(int *ip)
 {
-       if (!*gargv)
-               return(0);
+       intmax_t val;
+       uintmax_t uval;
+       int rval;
 
-       if (strchr(Number, **gargv))
-               return(atoi(*gargv++));
-
-       return 0;
-}
-
-static long
-getlong()
-{
-       long val;
-       char *ep;
-
-       if (!*gargv)
-               return(0L);
-
-       if (**gargv == '\"' || **gargv == '\'')
-               return (long) *((*gargv++)+1);
-
-       errno = 0;
-       val = strtol (*gargv, &ep, 0);
-       check_conversion(*gargv++, ep);
-       return val;
+       if (getnum(&val, &uval, 1))
+               return (1);
+       rval = 0;
+       if (val < INT_MIN || val > INT_MAX) {
+               warnx("%s: %s", *gargv, strerror(ERANGE));
+               rval = 1;
+       }
+       *ip = (int)val;
+       return (rval);
 }
 
-static unsigned long
-getulong()
+static int
+getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
 {
-       unsigned long val;
        char *ep;
+       int rval;
 
-       if (!*gargv)
-               return(0UL);
-
-       if (**gargv == '\"' || **gargv == '\'')
-               return (unsigned long) *((*gargv++)+1);
-
+       if (!*gargv) {
+               *ip = *uip = 0;
+               return (0);
+       }
+       if (**gargv == '"' || **gargv == '\'') {
+               if (signedconv)
+                       *ip = asciicode();
+               else
+                       *uip = asciicode();
+               return (0);
+       }
+       rval = 0;
        errno = 0;
-       val = strtoul (*gargv, &ep, 0);
-       check_conversion(*gargv++, ep);
-       return val;
+       if (signedconv)
+               *ip = strtoimax(*gargv, &ep, 0);
+       else
+               *uip = strtoumax(*gargv, &ep, 0);
+       if (ep == *gargv) {
+               warnx("%s: expected numeric value", *gargv);
+               rval = 1;
+       }
+       else if (*ep != '\0') {
+               warnx("%s: not completely converted", *gargv);
+               rval = 1;
+       }
+       if (errno == ERANGE) {
+               warnx("%s: %s", *gargv, strerror(ERANGE));
+               rval = 1;
+       }
+       ++gargv;
+       return (rval);
 }
 
-static double
-getdouble()
+static int
+getfloating(long double *dp, int mod_ldbl)
 {
-       double val;
        char *ep;
+       int rval;
 
-       if (!*gargv)
-               return(0.0);
-
-       if (**gargv == '\"' || **gargv == '\'')
-               return (double) *((*gargv++)+1);
-
+       if (!*gargv) {
+               *dp = 0.0;
+               return (0);
+       }
+       if (**gargv == '"' || **gargv == '\'') {
+               *dp = asciicode();
+               return (0);
+       }
+       rval = 0;
        errno = 0;
-       val = strtod (*gargv, &ep);
-       check_conversion(*gargv++, ep);
-       return val;
+       if (mod_ldbl)
+               *dp = strtold(*gargv, &ep);
+       else
+               *dp = strtod(*gargv, &ep);
+       if (ep == *gargv) {
+               warnx("%s: expected numeric value", *gargv);
+               rval = 1;
+       } else if (*ep != '\0') {
+               warnx("%s: not completely converted", *gargv);
+               rval = 1;
+       }
+       if (errno == ERANGE) {
+               warnx("%s: %s", *gargv, strerror(ERANGE));
+               rval = 1;
+       }
+       ++gargv;
+       return (rval);
 }
 
-static void
-check_conversion(s, ep)
-       const char *s;
-       const char *ep;
+static int
+asciicode(void)
 {
-       if (*ep) {
-               if (ep == s)
-                       warnx ("%s: expected numeric value", s);
-               else
-                       warnx ("%s: not completely converted", s);
-               rval = 1;
-       } else if (errno == ERANGE) {
-               warnx ("%s: %s", s, strerror(ERANGE));
-               rval = 1;
+       int ch;
+       wchar_t wch;
+       mbstate_t mbs;
+
+       ch = (unsigned char)**gargv;
+       if (ch == '\'' || ch == '"') {
+               memset(&mbs, 0, sizeof(mbs));
+               switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
+               case (size_t)-2:
+               case (size_t)-1:
+                       wch = (unsigned char)gargv[0][1];
+                       break;
+               case 0:
+                       wch = 0;
+                       break;
+               }
+               ch = wch;
        }
+       ++gargv;
+       return (ch);
 }
 
 static void
-usage()
+usage(void)
 {
-       (void)fprintf(stderr, "usage: printf format [arg ...]\n");
+       (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
 }