1f967d548SGarrett D'Amore /* 2598f4ceeSGarrett D'Amore * Copyright 2014 Garrett D'Amore <garrett@damore.org> 3f967d548SGarrett D'Amore * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 4f967d548SGarrett D'Amore * Copyright (c) 1989, 1993 5f967d548SGarrett D'Amore * The Regents of the University of California. All rights reserved. 6f967d548SGarrett D'Amore * 7f967d548SGarrett D'Amore * Redistribution and use in source and binary forms, with or without 8f967d548SGarrett D'Amore * modification, are permitted provided that the following conditions 9f967d548SGarrett D'Amore * are met: 10f967d548SGarrett D'Amore * 1. Redistributions of source code must retain the above copyright 11f967d548SGarrett D'Amore * notice, this list of conditions and the following disclaimer. 12f967d548SGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright 13f967d548SGarrett D'Amore * notice, this list of conditions and the following disclaimer in the 14f967d548SGarrett D'Amore * documentation and/or other materials provided with the distribution. 15f967d548SGarrett D'Amore * 4. Neither the name of the University nor the names of its contributors 16f967d548SGarrett D'Amore * may be used to endorse or promote products derived from this software 17f967d548SGarrett D'Amore * without specific prior written permission. 18f967d548SGarrett D'Amore * 19f967d548SGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20f967d548SGarrett D'Amore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21f967d548SGarrett D'Amore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22f967d548SGarrett D'Amore * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23f967d548SGarrett D'Amore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24f967d548SGarrett D'Amore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25f967d548SGarrett D'Amore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26f967d548SGarrett D'Amore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27f967d548SGarrett D'Amore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28f967d548SGarrett D'Amore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29f967d548SGarrett D'Amore * SUCH DAMAGE. 30f967d548SGarrett D'Amore */ 31f967d548SGarrett D'Amore 32f967d548SGarrett D'Amore #include <sys/types.h> 33f967d548SGarrett D'Amore 34f967d548SGarrett D'Amore #include <err.h> 35f967d548SGarrett D'Amore #include <errno.h> 36f967d548SGarrett D'Amore #include <inttypes.h> 37f967d548SGarrett D'Amore #include <limits.h> 38f967d548SGarrett D'Amore #include <stdio.h> 39f967d548SGarrett D'Amore #include <stdlib.h> 40f967d548SGarrett D'Amore #include <string.h> 41f967d548SGarrett D'Amore #include <unistd.h> 42598f4ceeSGarrett D'Amore #include <alloca.h> 43598f4ceeSGarrett D'Amore #include <ctype.h> 44f967d548SGarrett D'Amore #include <locale.h> 45f967d548SGarrett D'Amore #include <note.h> 46f967d548SGarrett D'Amore 47f967d548SGarrett D'Amore #define warnx1(a, b, c) warnx(a) 48f967d548SGarrett D'Amore #define warnx2(a, b, c) warnx(a, b) 49f967d548SGarrett D'Amore #define warnx3(a, b, c) warnx(a, b, c) 50f967d548SGarrett D'Amore 51f967d548SGarrett D'Amore #define PTRDIFF(x, y) ((uintptr_t)(x) - (uintptr_t)(y)) 52f967d548SGarrett D'Amore 53f967d548SGarrett D'Amore #define _(x) gettext(x) 54f967d548SGarrett D'Amore 55f967d548SGarrett D'Amore #define PF(f, func) do { \ 56f967d548SGarrett D'Amore char *b = NULL; \ 57f967d548SGarrett D'Amore if (havewidth) \ 58f967d548SGarrett D'Amore if (haveprec) \ 59f967d548SGarrett D'Amore (void) asprintf(&b, f, fieldwidth, precision, func); \ 60f967d548SGarrett D'Amore else \ 61f967d548SGarrett D'Amore (void) asprintf(&b, f, fieldwidth, func); \ 62f967d548SGarrett D'Amore else if (haveprec) \ 63f967d548SGarrett D'Amore (void) asprintf(&b, f, precision, func); \ 64f967d548SGarrett D'Amore else \ 65f967d548SGarrett D'Amore (void) asprintf(&b, f, func); \ 66f967d548SGarrett D'Amore if (b) { \ 67f967d548SGarrett D'Amore (void) fputs(b, stdout); \ 68f967d548SGarrett D'Amore free(b); \ 69f967d548SGarrett D'Amore } \ 70f967d548SGarrett D'Amore _NOTE(CONSTCOND) } while (0) 71f967d548SGarrett D'Amore 72f967d548SGarrett D'Amore static int asciicode(void); 73f967d548SGarrett D'Amore static char *doformat(char *, int *); 74f967d548SGarrett D'Amore static int escape(char *, int, size_t *); 75f967d548SGarrett D'Amore static int getchr(void); 76f967d548SGarrett D'Amore static int getfloating(long double *, int); 77f967d548SGarrett D'Amore static int getint(int *); 78f967d548SGarrett D'Amore static int getnum(intmax_t *, uintmax_t *, int); 79f967d548SGarrett D'Amore static const char 80f967d548SGarrett D'Amore *getstr(void); 81f967d548SGarrett D'Amore static char *mknum(char *, char); 82f967d548SGarrett D'Amore static void usage(void); 83f967d548SGarrett D'Amore 84598f4ceeSGarrett D'Amore static const char digits[] = "0123456789"; 85598f4ceeSGarrett D'Amore 86f967d548SGarrett D'Amore static int myargc; 87f967d548SGarrett D'Amore static char **myargv; 88f967d548SGarrett D'Amore static char **gargv; 89598f4ceeSGarrett D'Amore static char **maxargv; 90f967d548SGarrett D'Amore 91f967d548SGarrett D'Amore int 92f967d548SGarrett D'Amore main(int argc, char *argv[]) 93f967d548SGarrett D'Amore { 94f967d548SGarrett D'Amore size_t len; 95598f4ceeSGarrett D'Amore int end, rval; 96f967d548SGarrett D'Amore char *format, *fmt, *start; 97f967d548SGarrett D'Amore 98f967d548SGarrett D'Amore (void) setlocale(LC_ALL, ""); 99f967d548SGarrett D'Amore 100f967d548SGarrett D'Amore argv++; 101f967d548SGarrett D'Amore argc--; 102f967d548SGarrett D'Amore 103f967d548SGarrett D'Amore /* 104f967d548SGarrett D'Amore * POSIX says: Standard utilities that do not accept options, 105f967d548SGarrett D'Amore * but that do accept operands, shall recognize "--" as a 106f967d548SGarrett D'Amore * first argument to be discarded. 107f967d548SGarrett D'Amore */ 10812d82915SGarrett D'Amore if (argc && strcmp(argv[0], "--") == 0) { 109f967d548SGarrett D'Amore argc--; 110f967d548SGarrett D'Amore argv++; 111f967d548SGarrett D'Amore } 112f967d548SGarrett D'Amore 113f967d548SGarrett D'Amore if (argc < 1) { 114f967d548SGarrett D'Amore usage(); 115f967d548SGarrett D'Amore return (1); 116f967d548SGarrett D'Amore } 117f967d548SGarrett D'Amore 118f967d548SGarrett D'Amore /* 119f967d548SGarrett D'Amore * Basic algorithm is to scan the format string for conversion 120f967d548SGarrett D'Amore * specifications -- once one is found, find out if the field 121f967d548SGarrett D'Amore * width or precision is a '*'; if it is, gather up value. Note, 122f967d548SGarrett D'Amore * format strings are reused as necessary to use up the provided 123f967d548SGarrett D'Amore * arguments, arguments of zero/null string are provided to use 124f967d548SGarrett D'Amore * up the format string. 125f967d548SGarrett D'Amore */ 126f967d548SGarrett D'Amore fmt = format = *argv; 127598f4ceeSGarrett D'Amore (void) escape(fmt, 1, &len); /* backslash interpretation */ 128f967d548SGarrett D'Amore rval = end = 0; 129f967d548SGarrett D'Amore gargv = ++argv; 130f967d548SGarrett D'Amore 131f967d548SGarrett D'Amore for (;;) { 132598f4ceeSGarrett D'Amore maxargv = gargv; 133f967d548SGarrett D'Amore 134f967d548SGarrett D'Amore myargv = gargv; 135f967d548SGarrett D'Amore for (myargc = 0; gargv[myargc]; myargc++) 136f967d548SGarrett D'Amore /* nop */; 137f967d548SGarrett D'Amore start = fmt; 138f967d548SGarrett D'Amore while (fmt < format + len) { 139f967d548SGarrett D'Amore if (fmt[0] == '%') { 140f967d548SGarrett D'Amore (void) fwrite(start, 1, PTRDIFF(fmt, start), 141f967d548SGarrett D'Amore stdout); 142f967d548SGarrett D'Amore if (fmt[1] == '%') { 143f967d548SGarrett D'Amore /* %% prints a % */ 144f967d548SGarrett D'Amore (void) putchar('%'); 145f967d548SGarrett D'Amore fmt += 2; 146f967d548SGarrett D'Amore } else { 147f967d548SGarrett D'Amore fmt = doformat(fmt, &rval); 148f967d548SGarrett D'Amore if (fmt == NULL) 149f967d548SGarrett D'Amore return (1); 150f967d548SGarrett D'Amore end = 0; 151f967d548SGarrett D'Amore } 152f967d548SGarrett D'Amore start = fmt; 153f967d548SGarrett D'Amore } else 154f967d548SGarrett D'Amore fmt++; 155f967d548SGarrett D'Amore if (gargv > maxargv) 156f967d548SGarrett D'Amore maxargv = gargv; 157f967d548SGarrett D'Amore } 158f967d548SGarrett D'Amore gargv = maxargv; 159f967d548SGarrett D'Amore 160f967d548SGarrett D'Amore if (end == 1) { 161f967d548SGarrett D'Amore warnx1(_("missing format character"), NULL, NULL); 162f967d548SGarrett D'Amore return (1); 163f967d548SGarrett D'Amore } 164f967d548SGarrett D'Amore (void) fwrite(start, 1, PTRDIFF(fmt, start), stdout); 165598f4ceeSGarrett D'Amore if (!*gargv) 166f967d548SGarrett D'Amore return (rval); 167f967d548SGarrett D'Amore /* Restart at the beginning of the format string. */ 168f967d548SGarrett D'Amore fmt = format; 169f967d548SGarrett D'Amore end = 1; 170f967d548SGarrett D'Amore } 171f967d548SGarrett D'Amore /* NOTREACHED */ 172f967d548SGarrett D'Amore } 173f967d548SGarrett D'Amore 174f967d548SGarrett D'Amore 175f967d548SGarrett D'Amore static char * 176598f4ceeSGarrett D'Amore doformat(char *fmt, int *rval) 177f967d548SGarrett D'Amore { 178f967d548SGarrett D'Amore static const char skip1[] = "#'-+ 0"; 179f967d548SGarrett D'Amore int fieldwidth, haveprec, havewidth, mod_ldbl, precision; 180f967d548SGarrett D'Amore char convch, nextch; 181598f4ceeSGarrett D'Amore char *start; 182598f4ceeSGarrett D'Amore char **fargv; 183598f4ceeSGarrett D'Amore char *dptr; 184598f4ceeSGarrett D'Amore int l; 185f967d548SGarrett D'Amore 186598f4ceeSGarrett D'Amore start = alloca(strlen(fmt) + 1); 187598f4ceeSGarrett D'Amore 188598f4ceeSGarrett D'Amore dptr = start; 189598f4ceeSGarrett D'Amore *dptr++ = '%'; 190598f4ceeSGarrett D'Amore *dptr = 0; 191598f4ceeSGarrett D'Amore 192598f4ceeSGarrett D'Amore fmt++; 193f967d548SGarrett D'Amore 194f967d548SGarrett D'Amore /* look for "n$" field index specifier */ 195598f4ceeSGarrett D'Amore l = strspn(fmt, digits); 196598f4ceeSGarrett D'Amore if ((l > 0) && (fmt[l] == '$')) { 197598f4ceeSGarrett D'Amore int idx = atoi(fmt); 198f967d548SGarrett D'Amore if (idx <= myargc) { 199f967d548SGarrett D'Amore gargv = &myargv[idx - 1]; 200f967d548SGarrett D'Amore } else { 201f967d548SGarrett D'Amore gargv = &myargv[myargc]; 202f967d548SGarrett D'Amore } 203598f4ceeSGarrett D'Amore if (gargv > maxargv) { 204598f4ceeSGarrett D'Amore maxargv = gargv; 205598f4ceeSGarrett D'Amore } 206598f4ceeSGarrett D'Amore fmt += l + 1; 207598f4ceeSGarrett D'Amore 208598f4ceeSGarrett D'Amore /* save format argument */ 209598f4ceeSGarrett D'Amore fargv = gargv; 210f967d548SGarrett D'Amore } else { 211598f4ceeSGarrett D'Amore fargv = NULL; 212f967d548SGarrett D'Amore } 213f967d548SGarrett D'Amore 214f967d548SGarrett D'Amore /* skip to field width */ 215598f4ceeSGarrett D'Amore while (*fmt && strchr(skip1, *fmt) != NULL) { 216598f4ceeSGarrett D'Amore *dptr++ = *fmt++; 217598f4ceeSGarrett D'Amore *dptr = 0; 218598f4ceeSGarrett D'Amore } 219598f4ceeSGarrett D'Amore 220598f4ceeSGarrett D'Amore 221f967d548SGarrett D'Amore if (*fmt == '*') { 222598f4ceeSGarrett D'Amore 223598f4ceeSGarrett D'Amore fmt++; 224598f4ceeSGarrett D'Amore l = strspn(fmt, digits); 225598f4ceeSGarrett D'Amore if ((l > 0) && (fmt[l] == '$')) { 226598f4ceeSGarrett D'Amore int idx = atoi(fmt); 227598f4ceeSGarrett D'Amore if (fargv == NULL) { 228598f4ceeSGarrett D'Amore warnx1(_("incomplete use of n$"), NULL, NULL); 229598f4ceeSGarrett D'Amore return (NULL); 230598f4ceeSGarrett D'Amore } 231598f4ceeSGarrett D'Amore if (idx <= myargc) { 232598f4ceeSGarrett D'Amore gargv = &myargv[idx - 1]; 233598f4ceeSGarrett D'Amore } else { 234598f4ceeSGarrett D'Amore gargv = &myargv[myargc]; 235598f4ceeSGarrett D'Amore } 236598f4ceeSGarrett D'Amore fmt += l + 1; 237598f4ceeSGarrett D'Amore } else if (fargv != NULL) { 238598f4ceeSGarrett D'Amore warnx1(_("incomplete use of n$"), NULL, NULL); 239598f4ceeSGarrett D'Amore return (NULL); 240598f4ceeSGarrett D'Amore } 241598f4ceeSGarrett D'Amore 242f967d548SGarrett D'Amore if (getint(&fieldwidth)) 243f967d548SGarrett D'Amore return (NULL); 244598f4ceeSGarrett D'Amore if (gargv > maxargv) { 245598f4ceeSGarrett D'Amore maxargv = gargv; 246598f4ceeSGarrett D'Amore } 247f967d548SGarrett D'Amore havewidth = 1; 248598f4ceeSGarrett D'Amore 249598f4ceeSGarrett D'Amore *dptr++ = '*'; 250598f4ceeSGarrett D'Amore *dptr = 0; 251f967d548SGarrett D'Amore } else { 252f967d548SGarrett D'Amore havewidth = 0; 253f967d548SGarrett D'Amore 254f967d548SGarrett D'Amore /* skip to possible '.', get following precision */ 255598f4ceeSGarrett D'Amore while (isdigit(*fmt)) { 256598f4ceeSGarrett D'Amore *dptr++ = *fmt++; 257598f4ceeSGarrett D'Amore *dptr = 0; 258f967d548SGarrett D'Amore } 259598f4ceeSGarrett D'Amore } 260598f4ceeSGarrett D'Amore 261f967d548SGarrett D'Amore if (*fmt == '.') { 262f967d548SGarrett D'Amore /* precision present? */ 263598f4ceeSGarrett D'Amore fmt++; 264598f4ceeSGarrett D'Amore *dptr++ = '.'; 265598f4ceeSGarrett D'Amore 266f967d548SGarrett D'Amore if (*fmt == '*') { 267598f4ceeSGarrett D'Amore 268598f4ceeSGarrett D'Amore fmt++; 269598f4ceeSGarrett D'Amore l = strspn(fmt, digits); 270598f4ceeSGarrett D'Amore if ((l > 0) && (fmt[l] == '$')) { 271598f4ceeSGarrett D'Amore int idx = atoi(fmt); 272598f4ceeSGarrett D'Amore if (fargv == NULL) { 273598f4ceeSGarrett D'Amore warnx1(_("incomplete use of n$"), 274598f4ceeSGarrett D'Amore NULL, NULL); 275598f4ceeSGarrett D'Amore return (NULL); 276598f4ceeSGarrett D'Amore } 277598f4ceeSGarrett D'Amore if (idx <= myargc) { 278598f4ceeSGarrett D'Amore gargv = &myargv[idx - 1]; 279598f4ceeSGarrett D'Amore } else { 280598f4ceeSGarrett D'Amore gargv = &myargv[myargc]; 281598f4ceeSGarrett D'Amore } 282598f4ceeSGarrett D'Amore fmt += l + 1; 283598f4ceeSGarrett D'Amore } else if (fargv != NULL) { 284598f4ceeSGarrett D'Amore warnx1(_("incomplete use of n$"), NULL, NULL); 285598f4ceeSGarrett D'Amore return (NULL); 286598f4ceeSGarrett D'Amore } 287598f4ceeSGarrett D'Amore 288f967d548SGarrett D'Amore if (getint(&precision)) 289f967d548SGarrett D'Amore return (NULL); 290598f4ceeSGarrett D'Amore if (gargv > maxargv) { 291598f4ceeSGarrett D'Amore maxargv = gargv; 292598f4ceeSGarrett D'Amore } 293f967d548SGarrett D'Amore haveprec = 1; 294598f4ceeSGarrett D'Amore *dptr++ = '*'; 295598f4ceeSGarrett D'Amore *dptr = 0; 296f967d548SGarrett D'Amore } else { 297f967d548SGarrett D'Amore haveprec = 0; 298f967d548SGarrett D'Amore 299f967d548SGarrett D'Amore /* skip to conversion char */ 300598f4ceeSGarrett D'Amore while (isdigit(*fmt)) { 301598f4ceeSGarrett D'Amore *dptr++ = *fmt++; 302598f4ceeSGarrett D'Amore *dptr = 0; 303598f4ceeSGarrett D'Amore } 304f967d548SGarrett D'Amore } 305f967d548SGarrett D'Amore } else 306f967d548SGarrett D'Amore haveprec = 0; 307f967d548SGarrett D'Amore if (!*fmt) { 308f967d548SGarrett D'Amore warnx1(_("missing format character"), NULL, NULL); 309f967d548SGarrett D'Amore return (NULL); 310f967d548SGarrett D'Amore } 311598f4ceeSGarrett D'Amore *dptr++ = *fmt; 312598f4ceeSGarrett D'Amore *dptr = 0; 313f967d548SGarrett D'Amore 314f967d548SGarrett D'Amore /* 315f967d548SGarrett D'Amore * Look for a length modifier. POSIX doesn't have these, so 316f967d548SGarrett D'Amore * we only support them for floating-point conversions, which 317f967d548SGarrett D'Amore * are extensions. This is useful because the L modifier can 318f967d548SGarrett D'Amore * be used to gain extra range and precision, while omitting 319f967d548SGarrett D'Amore * it is more likely to produce consistent results on different 320f967d548SGarrett D'Amore * architectures. This is not so important for integers 321f967d548SGarrett D'Amore * because overflow is the only bad thing that can happen to 322f967d548SGarrett D'Amore * them, but consider the command printf %a 1.1 323f967d548SGarrett D'Amore */ 324f967d548SGarrett D'Amore if (*fmt == 'L') { 325f967d548SGarrett D'Amore mod_ldbl = 1; 326f967d548SGarrett D'Amore fmt++; 327f967d548SGarrett D'Amore if (!strchr("aAeEfFgG", *fmt)) { 328f967d548SGarrett D'Amore warnx2(_("bad modifier L for %%%c"), *fmt, NULL); 329f967d548SGarrett D'Amore return (NULL); 330f967d548SGarrett D'Amore } 331f967d548SGarrett D'Amore } else { 332f967d548SGarrett D'Amore mod_ldbl = 0; 333f967d548SGarrett D'Amore } 334f967d548SGarrett D'Amore 335598f4ceeSGarrett D'Amore /* save the current arg offset, and set to the format arg */ 336598f4ceeSGarrett D'Amore if (fargv != NULL) { 337598f4ceeSGarrett D'Amore gargv = fargv; 338598f4ceeSGarrett D'Amore } 339598f4ceeSGarrett D'Amore 340f967d548SGarrett D'Amore convch = *fmt; 341f967d548SGarrett D'Amore nextch = *++fmt; 342598f4ceeSGarrett D'Amore 343f967d548SGarrett D'Amore *fmt = '\0'; 344f967d548SGarrett D'Amore switch (convch) { 345f967d548SGarrett D'Amore case 'b': { 346f967d548SGarrett D'Amore size_t len; 347f967d548SGarrett D'Amore char *p; 348f967d548SGarrett D'Amore int getout; 349f967d548SGarrett D'Amore 350f967d548SGarrett D'Amore p = strdup(getstr()); 351f967d548SGarrett D'Amore if (p == NULL) { 352f967d548SGarrett D'Amore warnx2("%s", strerror(ENOMEM), NULL); 353f967d548SGarrett D'Amore return (NULL); 354f967d548SGarrett D'Amore } 355f967d548SGarrett D'Amore getout = escape(p, 0, &len); 356598f4ceeSGarrett D'Amore (void) fputs(p, stdout); 357f967d548SGarrett D'Amore free(p); 358f967d548SGarrett D'Amore 359f967d548SGarrett D'Amore if (getout) 360598f4ceeSGarrett D'Amore exit(*rval); 361f967d548SGarrett D'Amore break; 362f967d548SGarrett D'Amore } 363f967d548SGarrett D'Amore case 'c': { 364f967d548SGarrett D'Amore char p; 365f967d548SGarrett D'Amore 366f967d548SGarrett D'Amore p = getchr(); 367f967d548SGarrett D'Amore PF(start, p); 368f967d548SGarrett D'Amore break; 369f967d548SGarrett D'Amore } 370f967d548SGarrett D'Amore case 's': { 371f967d548SGarrett D'Amore const char *p; 372f967d548SGarrett D'Amore 373f967d548SGarrett D'Amore p = getstr(); 374f967d548SGarrett D'Amore PF(start, p); 375f967d548SGarrett D'Amore break; 376f967d548SGarrett D'Amore } 377f967d548SGarrett D'Amore case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { 378f967d548SGarrett D'Amore char *f; 379f967d548SGarrett D'Amore intmax_t val; 380f967d548SGarrett D'Amore uintmax_t uval; 381f967d548SGarrett D'Amore int signedconv; 382f967d548SGarrett D'Amore 383f967d548SGarrett D'Amore signedconv = (convch == 'd' || convch == 'i'); 384f967d548SGarrett D'Amore if ((f = mknum(start, convch)) == NULL) 385f967d548SGarrett D'Amore return (NULL); 386f967d548SGarrett D'Amore if (getnum(&val, &uval, signedconv)) 387f967d548SGarrett D'Amore *rval = 1; 388f967d548SGarrett D'Amore if (signedconv) 389f967d548SGarrett D'Amore PF(f, val); 390f967d548SGarrett D'Amore else 391f967d548SGarrett D'Amore PF(f, uval); 392f967d548SGarrett D'Amore break; 393f967d548SGarrett D'Amore } 394f967d548SGarrett D'Amore case 'e': case 'E': 395f967d548SGarrett D'Amore case 'f': case 'F': 396f967d548SGarrett D'Amore case 'g': case 'G': 397f967d548SGarrett D'Amore case 'a': case 'A': { 398f967d548SGarrett D'Amore long double p; 399f967d548SGarrett D'Amore 400f967d548SGarrett D'Amore if (getfloating(&p, mod_ldbl)) 401f967d548SGarrett D'Amore *rval = 1; 402f967d548SGarrett D'Amore if (mod_ldbl) 403f967d548SGarrett D'Amore PF(start, p); 404f967d548SGarrett D'Amore else 405f967d548SGarrett D'Amore PF(start, (double)p); 406f967d548SGarrett D'Amore break; 407f967d548SGarrett D'Amore } 408f967d548SGarrett D'Amore default: 409f967d548SGarrett D'Amore warnx2(_("illegal format character %c"), convch, NULL); 410f967d548SGarrett D'Amore return (NULL); 411f967d548SGarrett D'Amore } 412f967d548SGarrett D'Amore *fmt = nextch; 413598f4ceeSGarrett D'Amore 414598f4ceeSGarrett D'Amore /* return the gargv to the next element */ 415f967d548SGarrett D'Amore return (fmt); 416f967d548SGarrett D'Amore } 417f967d548SGarrett D'Amore 418f967d548SGarrett D'Amore static char * 419f967d548SGarrett D'Amore mknum(char *str, char ch) 420f967d548SGarrett D'Amore { 421f967d548SGarrett D'Amore static char *copy; 422f967d548SGarrett D'Amore static size_t copy_size; 423f967d548SGarrett D'Amore char *newcopy; 424f967d548SGarrett D'Amore size_t len, newlen; 425f967d548SGarrett D'Amore 426f967d548SGarrett D'Amore len = strlen(str) + 2; 427f967d548SGarrett D'Amore if (len > copy_size) { 428f967d548SGarrett D'Amore newlen = ((len + 1023) >> 10) << 10; 429f967d548SGarrett D'Amore if ((newcopy = realloc(copy, newlen)) == NULL) { 430f967d548SGarrett D'Amore warnx2("%s", strerror(ENOMEM), NULL); 431f967d548SGarrett D'Amore return (NULL); 432f967d548SGarrett D'Amore } 433f967d548SGarrett D'Amore copy = newcopy; 434f967d548SGarrett D'Amore copy_size = newlen; 435f967d548SGarrett D'Amore } 436f967d548SGarrett D'Amore 437f967d548SGarrett D'Amore (void) memmove(copy, str, len - 3); 438f967d548SGarrett D'Amore copy[len - 3] = 'j'; 439f967d548SGarrett D'Amore copy[len - 2] = ch; 440f967d548SGarrett D'Amore copy[len - 1] = '\0'; 441f967d548SGarrett D'Amore return (copy); 442f967d548SGarrett D'Amore } 443f967d548SGarrett D'Amore 444f967d548SGarrett D'Amore static int 445f967d548SGarrett D'Amore escape(char *fmt, int percent, size_t *len) 446f967d548SGarrett D'Amore { 447f967d548SGarrett D'Amore char *save, *store, c; 448f967d548SGarrett D'Amore int value; 449f967d548SGarrett D'Amore 450f967d548SGarrett D'Amore for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { 451f967d548SGarrett D'Amore if (c != '\\') { 452f967d548SGarrett D'Amore *store = c; 453f967d548SGarrett D'Amore continue; 454f967d548SGarrett D'Amore } 455f967d548SGarrett D'Amore switch (*++fmt) { 456f967d548SGarrett D'Amore case '\0': /* EOS, user error */ 457f967d548SGarrett D'Amore *store = '\\'; 458f967d548SGarrett D'Amore *++store = '\0'; 459f967d548SGarrett D'Amore *len = PTRDIFF(store, save); 460f967d548SGarrett D'Amore return (0); 461f967d548SGarrett D'Amore case '\\': /* backslash */ 462f967d548SGarrett D'Amore case '\'': /* single quote */ 463f967d548SGarrett D'Amore *store = *fmt; 464f967d548SGarrett D'Amore break; 465f967d548SGarrett D'Amore case 'a': /* bell/alert */ 466f967d548SGarrett D'Amore *store = '\a'; 467f967d548SGarrett D'Amore break; 468f967d548SGarrett D'Amore case 'b': /* backspace */ 469f967d548SGarrett D'Amore *store = '\b'; 470f967d548SGarrett D'Amore break; 471f967d548SGarrett D'Amore case 'c': 472598f4ceeSGarrett D'Amore if (!percent) { 473f967d548SGarrett D'Amore *store = '\0'; 474f967d548SGarrett D'Amore *len = PTRDIFF(store, save); 475f967d548SGarrett D'Amore return (1); 476598f4ceeSGarrett D'Amore } 477598f4ceeSGarrett D'Amore *store = 'c'; 478598f4ceeSGarrett D'Amore break; 479f967d548SGarrett D'Amore case 'f': /* form-feed */ 480f967d548SGarrett D'Amore *store = '\f'; 481f967d548SGarrett D'Amore break; 482f967d548SGarrett D'Amore case 'n': /* newline */ 483f967d548SGarrett D'Amore *store = '\n'; 484f967d548SGarrett D'Amore break; 485f967d548SGarrett D'Amore case 'r': /* carriage-return */ 486f967d548SGarrett D'Amore *store = '\r'; 487f967d548SGarrett D'Amore break; 488f967d548SGarrett D'Amore case 't': /* horizontal tab */ 489f967d548SGarrett D'Amore *store = '\t'; 490f967d548SGarrett D'Amore break; 491f967d548SGarrett D'Amore case 'v': /* vertical tab */ 492f967d548SGarrett D'Amore *store = '\v'; 493f967d548SGarrett D'Amore break; 494f967d548SGarrett D'Amore /* octal constant */ 495f967d548SGarrett D'Amore case '0': case '1': case '2': case '3': 496f967d548SGarrett D'Amore case '4': case '5': case '6': case '7': 497f967d548SGarrett D'Amore c = (!percent && *fmt == '0') ? 4 : 3; 498f967d548SGarrett D'Amore for (value = 0; 499f967d548SGarrett D'Amore c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { 500f967d548SGarrett D'Amore value <<= 3; 501f967d548SGarrett D'Amore value += *fmt - '0'; 502f967d548SGarrett D'Amore } 503f967d548SGarrett D'Amore --fmt; 504f967d548SGarrett D'Amore if (percent && value == '%') { 505f967d548SGarrett D'Amore *store++ = '%'; 506f967d548SGarrett D'Amore *store = '%'; 507f967d548SGarrett D'Amore } else 508f967d548SGarrett D'Amore *store = (char)value; 509f967d548SGarrett D'Amore break; 510f967d548SGarrett D'Amore default: 511f967d548SGarrett D'Amore *store = *fmt; 512f967d548SGarrett D'Amore break; 513f967d548SGarrett D'Amore } 514f967d548SGarrett D'Amore } 515f967d548SGarrett D'Amore *store = '\0'; 516f967d548SGarrett D'Amore *len = PTRDIFF(store, save); 517f967d548SGarrett D'Amore return (0); 518f967d548SGarrett D'Amore } 519f967d548SGarrett D'Amore 520f967d548SGarrett D'Amore static int 521f967d548SGarrett D'Amore getchr(void) 522f967d548SGarrett D'Amore { 523f967d548SGarrett D'Amore if (!*gargv) 524f967d548SGarrett D'Amore return ('\0'); 525f967d548SGarrett D'Amore return ((int)**gargv++); 526f967d548SGarrett D'Amore } 527f967d548SGarrett D'Amore 528f967d548SGarrett D'Amore static const char * 529f967d548SGarrett D'Amore getstr(void) 530f967d548SGarrett D'Amore { 531f967d548SGarrett D'Amore if (!*gargv) 532f967d548SGarrett D'Amore return (""); 533f967d548SGarrett D'Amore return (*gargv++); 534f967d548SGarrett D'Amore } 535f967d548SGarrett D'Amore 536f967d548SGarrett D'Amore static int 537f967d548SGarrett D'Amore getint(int *ip) 538f967d548SGarrett D'Amore { 539f967d548SGarrett D'Amore intmax_t val; 540f967d548SGarrett D'Amore uintmax_t uval; 541f967d548SGarrett D'Amore int rval; 542f967d548SGarrett D'Amore 543f967d548SGarrett D'Amore if (getnum(&val, &uval, 1)) 544f967d548SGarrett D'Amore return (1); 545f967d548SGarrett D'Amore rval = 0; 546f967d548SGarrett D'Amore if (val < INT_MIN || val > INT_MAX) { 547f967d548SGarrett D'Amore warnx3("%s: %s", *gargv, strerror(ERANGE)); 548f967d548SGarrett D'Amore rval = 1; 549f967d548SGarrett D'Amore } 550f967d548SGarrett D'Amore *ip = (int)val; 551f967d548SGarrett D'Amore return (rval); 552f967d548SGarrett D'Amore } 553f967d548SGarrett D'Amore 554f967d548SGarrett D'Amore static int 555f967d548SGarrett D'Amore getnum(intmax_t *ip, uintmax_t *uip, int signedconv) 556f967d548SGarrett D'Amore { 557f967d548SGarrett D'Amore char *ep; 558f967d548SGarrett D'Amore int rval; 559f967d548SGarrett D'Amore 560f967d548SGarrett D'Amore if (!*gargv) { 561*e56bd285SGarrett D'Amore *ip = *uip = 0; 562f967d548SGarrett D'Amore return (0); 563f967d548SGarrett D'Amore } 564f967d548SGarrett D'Amore if (**gargv == '"' || **gargv == '\'') { 565f967d548SGarrett D'Amore if (signedconv) 566f967d548SGarrett D'Amore *ip = asciicode(); 567f967d548SGarrett D'Amore else 568f967d548SGarrett D'Amore *uip = asciicode(); 569f967d548SGarrett D'Amore return (0); 570f967d548SGarrett D'Amore } 571f967d548SGarrett D'Amore rval = 0; 572f967d548SGarrett D'Amore errno = 0; 573f967d548SGarrett D'Amore if (signedconv) 574f967d548SGarrett D'Amore *ip = strtoimax(*gargv, &ep, 0); 575f967d548SGarrett D'Amore else 576f967d548SGarrett D'Amore *uip = strtoumax(*gargv, &ep, 0); 577f967d548SGarrett D'Amore if (ep == *gargv) { 578f967d548SGarrett D'Amore warnx2(_("%s: expected numeric value"), *gargv, NULL); 579f967d548SGarrett D'Amore rval = 1; 580f967d548SGarrett D'Amore } else if (*ep != '\0') { 581f967d548SGarrett D'Amore warnx2(_("%s: not completely converted"), *gargv, NULL); 582f967d548SGarrett D'Amore rval = 1; 583f967d548SGarrett D'Amore } 584f967d548SGarrett D'Amore if (errno == ERANGE) { 585f967d548SGarrett D'Amore warnx3("%s: %s", *gargv, strerror(ERANGE)); 586f967d548SGarrett D'Amore rval = 1; 587f967d548SGarrett D'Amore } 588f967d548SGarrett D'Amore ++gargv; 589f967d548SGarrett D'Amore return (rval); 590f967d548SGarrett D'Amore } 591f967d548SGarrett D'Amore 592f967d548SGarrett D'Amore static int 593f967d548SGarrett D'Amore getfloating(long double *dp, int mod_ldbl) 594f967d548SGarrett D'Amore { 595f967d548SGarrett D'Amore char *ep; 596f967d548SGarrett D'Amore int rval; 597f967d548SGarrett D'Amore 598f967d548SGarrett D'Amore if (!*gargv) { 599f967d548SGarrett D'Amore *dp = 0.0; 600f967d548SGarrett D'Amore return (0); 601f967d548SGarrett D'Amore } 602f967d548SGarrett D'Amore if (**gargv == '"' || **gargv == '\'') { 603f967d548SGarrett D'Amore *dp = asciicode(); 604f967d548SGarrett D'Amore return (0); 605f967d548SGarrett D'Amore } 606f967d548SGarrett D'Amore rval = 0; 607f967d548SGarrett D'Amore errno = 0; 608f967d548SGarrett D'Amore if (mod_ldbl) 609f967d548SGarrett D'Amore *dp = strtold(*gargv, &ep); 610f967d548SGarrett D'Amore else 611f967d548SGarrett D'Amore *dp = strtod(*gargv, &ep); 612f967d548SGarrett D'Amore if (ep == *gargv) { 613f967d548SGarrett D'Amore warnx2(_("%s: expected numeric value"), *gargv, NULL); 614f967d548SGarrett D'Amore rval = 1; 615f967d548SGarrett D'Amore } else if (*ep != '\0') { 616f967d548SGarrett D'Amore warnx2(_("%s: not completely converted"), *gargv, NULL); 617f967d548SGarrett D'Amore rval = 1; 618f967d548SGarrett D'Amore } 619f967d548SGarrett D'Amore if (errno == ERANGE) { 620f967d548SGarrett D'Amore warnx3("%s: %s", *gargv, strerror(ERANGE)); 621f967d548SGarrett D'Amore rval = 1; 622f967d548SGarrett D'Amore } 623f967d548SGarrett D'Amore ++gargv; 624f967d548SGarrett D'Amore return (rval); 625f967d548SGarrett D'Amore } 626f967d548SGarrett D'Amore 627f967d548SGarrett D'Amore static int 628f967d548SGarrett D'Amore asciicode(void) 629f967d548SGarrett D'Amore { 630f967d548SGarrett D'Amore int ch; 631f967d548SGarrett D'Amore 632f967d548SGarrett D'Amore ch = **gargv; 633f967d548SGarrett D'Amore if (ch == '\'' || ch == '"') 634f967d548SGarrett D'Amore ch = (*gargv)[1]; 635f967d548SGarrett D'Amore ++gargv; 636f967d548SGarrett D'Amore return (ch); 637f967d548SGarrett D'Amore } 638f967d548SGarrett D'Amore 639f967d548SGarrett D'Amore static void 640f967d548SGarrett D'Amore usage(void) 641f967d548SGarrett D'Amore { 642f967d548SGarrett D'Amore (void) fprintf(stderr, _("usage: printf format [arguments ...]\n")); 643f967d548SGarrett D'Amore } 644