diff --git a/README b/README index 3cd5059..8e5d4fe 100644 --- a/README +++ b/README @@ -51,7 +51,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support, =* nohup yes none #* paste yes none =* printenv non-posix none - printf stolen stolen +#* printf yes none =* pwd yes none = readlink non-posix none =* renice yes none diff --git a/TODO b/TODO index 843185c..3325541 100644 --- a/TODO +++ b/TODO @@ -24,7 +24,7 @@ tput The following programs have been imported from OpenBSD and need replacing or cleaning up: -printf +(none) If you are looking for some work to do on sbase, another option is to pick a utility from the list in the README which has missing flags or diff --git a/printf.1 b/printf.1 index 5d3a4da..041a451 100644 --- a/printf.1 +++ b/printf.1 @@ -1,386 +1,33 @@ -.\" $OpenBSD: src/usr.bin/printf/printf.1,v 1.27 2014/05/25 07:36:36 jmc Exp $ -.\" -.\" Copyright (c) 1989, 1990 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" the Institute of Electrical and Electronics Engineers, Inc. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 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. -.\" -.\" from: @(#)printf.1 5.11 (Berkeley) 7/24/91 -.\" -.Dd $Mdocdate: May 13 2014 $ +.Dd February 15, 2015 .Dt PRINTF 1 -.Os +.Os sbase .Sh NAME .Nm printf -.Nd formatted output +.Nd print formatted data .Sh SYNOPSIS .Nm .Ar format -.Op Ar argument ... +.Op Ar arg ... .Sh DESCRIPTION .Nm -formats and prints its arguments, after the first, under control -of the -.Ar format . -The +writes formatted data according to .Ar format -is a character string which contains three types of objects: plain characters, -which are simply copied to standard output, character escape sequences which -are converted and copied to the standard output, and format specifications, -each of which causes printing of the next successive -.Ar argument . +using each +.Ar arg +until drained. .Pp -The arguments after the first are treated as strings -if the corresponding format is -.Cm b , -.Cm c -or -.Cm s ; -otherwise it is evaluated as a C constant, with the following extensions: -.Bl -bullet -offset indent -.It -A leading plus or minus sign is allowed. -.It -If the leading character is a single or double quote, the value is the -.Tn ASCII -code of the next character. -.El -.Pp -The format string is reused as often as necessary to satisfy the arguments. -Any extra format specifications are evaluated with zero or the null -string. -.Pp -Character escape sequences are in backslash notation as defined in -.St -ansiC . -The characters and their meanings are as follows: -.Pp -.Bl -tag -width Ds -offset indent -compact -.It Cm \ee -Write an character. -.It Cm \ea -Write a character. -.It Cm \eb -Write a character. -.It Cm \ef -Write a character. -.It Cm \en -Write a character. -.It Cm \er -Write a character. -.It Cm \et -Write a character. -.It Cm \ev -Write a character. -.It Cm \e\' -Write a character. -.It Cm \e\e -Write a backslash character. -.It Cm \e Ns Ar num -Write an 8-bit character whose -.Tn ASCII -value is the 1-, 2-, or 3-digit -octal number -.Ar num . -.El -.Pp -Each format specification is introduced by the percent -.Pq Sq \&% -character. -The remainder of the format specifiers include, -in the following order: -.Bl -tag -width Ds -.It "Zero or more of the following flags:" -.Bl -tag -width Ds -.It Cm # -Specifies that the value should be printed in an -.Dq alternate form . -For the -.Cm o -format the precision of the number is increased to force the first -character of the output string to a zero. -For the -.Cm x -.Pq Cm X -format, a non-zero result has the string -.Li 0x -.Pq Li 0X -prepended to it. -For -.Cm a , -.Cm A , -.Cm e , -.Cm E , -.Cm f , -.Cm F , -.Cm g , -and -.Cm G -formats, the result will always contain a decimal point, even if no -digits follow the point (normally, a decimal point only appears in the -results of those formats if a digit follows the decimal point). -For -.Cm g -and -.Cm G -formats, trailing zeros are not removed from the result as they -would otherwise be. -For all other formats, behaviour is undefined. -.It Cm \&\- -Specifies the -.Em left adjustment -of the output in the indicated field. -.It Cm \&+ -Specifies that there should always be -a sign placed before the number when using signed formats. -.It Sq \&\ \& -A space specifies that a blank should be left before a positive number -for a signed format. -A -.Ql + -overrides a space if both are used. -.It Cm \&0 -A zero character specifies that zero-padding should be used -rather than blank-padding. -This flag is ignored if used with a precision -specifier and any of the -.Cm d , i , o , u , -or -.Cm x -.Pq Cm X -formats. -A -.Ql \&- -overrides a -.Ql \&0 -if both are used. -.El -.It "Field Width:" -An optional digit string specifying a -.Em field width ; -if the output string has fewer characters than the field width it will -be blank-padded on the left (or right, if the left-adjustment indicator -has been given) to make up the field width (note that a leading zero -is a flag, but an embedded zero is part of a field width). -.It Precision: -An optional period -.Pq Sq \&. , -followed by an optional digit string giving a -.Em precision -which specifies the number of digits to appear after the decimal point, -for -.Cm e -and -.Cm f -formats, or the maximum number of characters to be printed -from a string; if the digit string is missing, the precision is treated -as zero. -.It Format: -A character which indicates the type of format to use (one of -.Cm diouxXfFeEgGaAbcs ) . -.El -.Pp -A field width or precision may be -.Ql \&* -instead of a digit string. -In this case an -.Ar argument -supplies the field width or precision. -.Pp -The format characters and their meanings are: -.Bl -tag -width Fl -.It Cm diouXx -The -.Ar argument -is printed as a signed decimal -.Pq Cm d No or Cm i , -unsigned octal, unsigned decimal, -or unsigned hexadecimal -.Pq Cm x No or Cm X , -respectively. -.It Cm fF -The -.Ar argument -is printed in the style -.Sm off -.Pf [\-]ddd Cm \&. No ddd -.Sm on -where the number of d's -after the decimal point is equal to the precision specification for -the argument. -If the precision is missing, 6 digits are given; if the precision -is explicitly 0, no digits and no decimal point are printed. -.Pp -If the argument is infinity, it will be converted to [-]inf -.Pq Cm f -or [-]INF -.Pq Cm F , -respectively. -If the argument is not-a-number (NaN), it will be converted to -[-]nan -.Pq Cm f -or [-]NAN -.Pq Cm F , -respectively. -.It Cm eE -The -.Ar argument -is printed in the style -.Sm off -.Pf [\-]d Cm \&. No ddd Cm e No \*(Pmdd -.Sm on -where there -is one digit before the decimal point and the number after is equal to -the precision specification for the argument; when the precision is -missing, 6 digits are produced. -An upper-case -.Sq E -is used for an -.Cm E -format. -.Pp -If the argument is infinity, it will be converted to [-]inf -.Pq Cm e -or [-]INF -.Pq Cm E , -respectively. -If the argument is not-a-number (NaN), it will be converted to -[-]nan -.Pq Cm e -or [-]NAN -.Pq Cm E , -respectively. -.It Cm gG -The -.Ar argument -is printed in style -.Cm f -or in style -.Cm e -.Pq Cm E -whichever gives full precision in minimum space. -.Pp -If the argument is infinity, it will be converted to [-]inf -.Pq Cm g -or [-]INF -.Pq Cm G , -respectively. -If the argument is not-a-number (NaN), it will be converted to -[-]nan -.Pq Cm g -or [-]NAN -.Pq Cm G , -respectively. -.It Cm aA -The -.Ar argument -is printed in style -.Sm off -.Pf [\-]0xh Cm \&. No hhh Cm p No [\*(Pm]d -.Sm on -where there is one digit before the hexadecimal point and the number -after is equal to the precision specification for the argument. -When the precision is missing, enough digits are produced to convey -the argument's exact double-precision floating-point representation. -.Pp -If the argument is infinity, it will be converted to [-]inf -.Pq Cm a -or [-]INF -.Pq Cm A , -respectively. -If the argument is not-a-number (NaN), it will be converted to -[-]nan -.Pq Cm a -or [-]NAN -.Pq Cm A , -respectively. -.It Cm b -Characters from the string -.Ar argument -are printed with backslash-escape sequences expanded. -.It Cm c -The first character of -.Ar argument -is printed. -.It Cm s -Characters from the string -.Ar argument -are printed until the end is reached or until the number of characters -indicated by the precision specification is reached; however if the -precision is 0 or missing, all characters in the string are printed. -.It Cm \&% -Print a -.Ql \&% ; -no argument is used. -.El -.Pp -In no case does a non-existent or small field width cause truncation of -a field; padding takes place only if the specified field width exceeds -the actual width. -.Sh EXIT STATUS -.Ex -std printf -.Sh EXAMPLES -Convert a hexadecimal value to decimal and print it out: -.Pp -.D1 Ic $ printf \&"%d\en\&" 0x20 -.Pp -Print the decimal representation of the character 'a' (see -.Xr ascii 7 ) : -.Pp -.D1 Ic $ printf \&"%d\en\&" \e'a -.Sh SEE ALSO -.Xr echo 1 , +.Nm +interprets the standard escape sequences \e\e, \e', \e", \ea, \eb, \ee, +\ef, \en, \er, \et, \ev, \exH[H], \eO[OO], the sequence \ec, which +terminates further output if it's found inside +.Ar format +or a %b format string, the format specification %b for an unescaped string and all C .Xr printf 3 +format specifications ending with csdiouxXaAeEfFgG, including variable width and precision. .Sh STANDARDS The .Nm utility is compliant with the .St -p1003.1-2008 -specification. -.Pp -The escape sequences \ee and \e' are extensions to that specification. -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.3 Reno . -.Sh CAVEATS -It is important never to pass a string with user-supplied data as a -format without using -.Ql %s . -An attacker can put format specifiers in the string to mangle your stack, -leading to a possible security hole. -.Pp -Always be sure to use the proper secure idiom: -.Bd -literal -offset indent -printf "%s" "$STRING" -.Ed -.Sh BUGS -Since arguments are translated from -.Tn ASCII -to floating-point, and -then back again, floating-point precision may be lost. +specification except from the octal-escape format in %b format strings, which has been changed +from \e0[ooo] to \eo[oo] for consistency reasons. diff --git a/printf.c b/printf.c index b0014c8..bfd8670 100644 --- a/printf.c +++ b/printf.c @@ -1,497 +1,147 @@ -/* $OpenBSD: printf.c,v 1.22 2014/05/25 07:36:36 jmc Exp $ */ - -/* - * Copyright (c) 1989 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. - */ - +/* See LICENSE file for copyright and license details. */ #include +#include #include #include #include -#include -#include -#include -#include -static int print_escape_str(const char *); -static int print_escape(const char *); +#include "utf.h" +#include "util.h" -static int getchr(void); -static double getdouble(void); -static int getint(void); -static long getlong(void); -static unsigned long getulong(void); -static char *getstr(void); -static char *mklong(const char *, int); -static void check_conversion(const char *, const char *); -static void usage(void); - -static int rval; -static char **gargv; - -#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') - -#define PF(f, func) { \ - if (havefieldwidth) \ - if (haveprecision) \ - (void)printf(f, fieldwidth, precision, func); \ - else \ - (void)printf(f, fieldwidth, func); \ - else if (haveprecision) \ - (void)printf(f, precision, func); \ - else \ - (void)printf(f, func); \ +static void usage(void) +{ + eprintf("%s format [arg ...]\n", argv0); } -int -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { - char *fmt, *start; - int havefieldwidth, haveprecision; - int fieldwidth, precision; - char convch, nextch; - char *format; + Rune *rarg; + size_t i, j, argi, lastargi, formatlen, arglen; + long long num; + double dou; + int cooldown = 0, width, precision; + char *format, *tmp, *arg, *fmt; - setlocale (LC_ALL, ""); - - /* Need to accept/ignore "--" option. */ - if (argc > 1 && strcmp(argv[1], "--") == 0) { - argc--; - argv++; - } - - if (argc < 2) { + argv0 = argv[0]; + if (argc < 2) usage(); - return (1); + + format = argv[1]; + if ((tmp = strstr(format, "\\c"))) { + *tmp = 0; + cooldown = 1; } + formatlen = unescape(format); - format = *++argv; - 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 == '%') { - putchar ('%'); - break; - } else if (*fmt == 'b') { - char *p = getstr(); - if (print_escape_str(p)) { - return (rval); - } - break; - } - - /* skip to field width */ - for (; strchr(SKIP1, *fmt); ++fmt) - ; - if (*fmt == '*') { - ++fmt; - havefieldwidth = 1; - fieldwidth = getint(); - } else - havefieldwidth = 0; - - /* skip to field precision */ - for (; strchr(SKIP2, *fmt); ++fmt) - ; - haveprecision = 0; - if (*fmt == '.') { - ++fmt; - if (*fmt == '*') { - ++fmt; - haveprecision = 1; - precision = getint(); - } - 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': { - long p; - char *f = mklong(start, convch); - if (!f) { - warnx("out of memory"); - return (1); - } - p = getlong(); - PF(f, p); - break; - } - case 'o': - case 'u': - case 'x': - case 'X': { - unsigned long p; - char *f = mklong(start, convch); - if (!f) { - warnx("out of memory"); - return (1); - } - p = getulong(); - PF(f, p); - break; - } - case 'a': - case 'A': - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': { - double p = getdouble(); - PF(start, p); - break; - } - default: - warnx ("%s: invalid directive", start); - return(1); - } - *(fmt + 1) = nextch; + lastargi = 0; + for (i = 0, argi = 2; !cooldown || i < formatlen; i++, i = cooldown ? i : (i % formatlen)) { + if (i == 0) { + if (lastargi == argi) break; - - case '\\': - fmt += print_escape(fmt); - break; - - default: - putchar (*fmt); - break; - } + lastargi = argi; + } + if (format[i] != '%') { + putchar(format[i]); + continue; } - } while (gargv > argv && *gargv); - return (rval); -} - - -/* - * Print SysV echo(1) style escape string - * Halts processing string and returns 1 if a \c escape is encountered. - */ -static int -print_escape_str(const char *str) -{ - 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); - } - putchar (value); - str--; - } else if (*str == 'c') { - return 1; - } else { - str--; - str += print_escape(str); - } + /* field width */ + width = 0; + for (i++; strchr("#-+ 0", format[i]); i++); + if (format[i] == '*') { + if (argi < argc) + width = estrtonum(argv[argi++], 0, INT_MAX); + else + cooldown = 1; + i++; } else { - putchar (*str); + j = i; + for (; strchr("+-0123456789", format[i]); i++); + if (j != i) { + tmp = estrndup(format + j, i - j); + tmp[i - j] = 0; + width = estrtonum(tmp, 0, INT_MAX); + free(tmp); + } } - str++; - } + /* field precision */ + precision = 6; + if (format[i] == '.') { + if (format[++i] == '*') { + if (argi < argc) + precision = estrtonum(argv[argi++], 0, INT_MAX); + else + cooldown = 1; + } else { + j = i; + for (; strchr("+-0123456789", format[i]); i++); + if (j != i) { + tmp = estrndup(format + j, i - j); + tmp[i - j] = 0; + precision = estrtonum(tmp, 0, INT_MAX); + free(tmp); + } + } + } + + if (format[i] != '%') { + if (argi < argc) + arg = argv[argi++]; + else { + arg = ""; + cooldown = 1; + } + } else + putchar('%'); + + switch (format[i]) { + case 'b': + if ((tmp = strstr(arg, "\\c"))) { + *tmp = 0; + argi = argc; + } + unescape(arg); + fputs(arg, stdout); + break; + case 'c': + unescape(arg); + rarg = emalloc((utflen(arg) + 1) * sizeof(*rarg)); + utftorunestr(arg, rarg); + efputrune(rarg, stdout, ""); + free(rarg); + break; + case 's': + fputs(arg, stdout); + break; + case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': + arglen = strlen(arg); + for (j = 0; j < arglen && isspace(arg[j]); j++); + if (arg[j] == '\'' || arg[j] == '\"') { + arg += j + 1; + unescape(arg); + rarg = emalloc((utflen(arg) + 1) * sizeof(*rarg)); + utftorunestr(arg, rarg); + num = rarg[0]; + } else + num = (strlen(arg) > 0) ? estrtonum(arg, LLONG_MIN, LLONG_MAX) : 0; + fmt = estrdup("%*ll#"); + fmt[4] = format[i]; + printf(fmt, width, num); + free(fmt); + break; + case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + fmt = estrdup("%*.*#"); + fmt[4] = format[i]; + dou = (strlen(arg) > 0) ? estrtod(arg) : 0; + printf(fmt, width, precision, dou); + free(fmt); + break; + default: + eprintf("Invalid format specifier '%c'.\n", format[i]); + } + if (argi >= argc) + cooldown = 1; + } return 0; } - -/* - * Print "standard" escape characters - */ -static int -print_escape(const char *str) -{ - const char *start = str; - int value; - int c; - - str++; - - 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); - } - putchar(value); - return str - start - 1; - /* NOTREACHED */ - - case 'x': - str++; - for (value = 0; isxdigit((unsigned char)*str); str++) { - value <<= 4; - value += hextobin(*str); - } - if (value > UCHAR_MAX) { - warnx ("escape sequence out of range for character"); - rval = 1; - } - putchar (value); - return str - start - 1; - /* NOTREACHED */ - - case '\\': /* backslash */ - putchar('\\'); - break; - - case '\'': /* single quote */ - putchar('\''); - break; - - case '"': /* double quote */ - putchar('"'); - break; - - case 'a': /* alert */ - putchar('\a'); - break; - - case 'b': /* backspace */ - putchar('\b'); - break; - - case 'e': /* escape */ - putchar(033); - break; - - case 'f': /* form-feed */ - putchar('\f'); - break; - - case 'n': /* newline */ - putchar('\n'); - break; - - case 'r': /* carriage-return */ - putchar('\r'); - break; - - case 't': /* tab */ - putchar('\t'); - break; - - case 'v': /* vertical-tab */ - putchar('\v'); - break; - - case '\0': - warnx("null escape sequence"); - rval = 1; - return 0; - - default: - putchar(*str); - warnx("unknown escape sequence `\\%c'", *str); - rval = 1; - } - - return 1; -} - -static char * -mklong(const char *str, int ch) -{ - static char *copy; - static int copysize; - int len; - - len = strlen(str) + 2; - if (copysize < len) { - char *newcopy; - copysize = len + 256; - - newcopy = realloc(copy, copysize); - if (newcopy == NULL) { - copysize = 0; - free(copy); - copy = NULL; - return (NULL); - } - copy = newcopy; - } - (void) memmove(copy, str, len - 3); - copy[len - 3] = 'l'; - copy[len - 2] = ch; - copy[len - 1] = '\0'; - return (copy); -} - -static int -getchr(void) -{ - if (!*gargv) - return((int)'\0'); - return((int)**gargv++); -} - -static char * -getstr(void) -{ - if (!*gargv) - return(""); - return(*gargv++); -} - -static char *number = "+-.0123456789"; -static int -getint(void) -{ - if (!*gargv) - return(0); - - if (strchr(number, **gargv)) - return(atoi(*gargv++)); - - return 0; -} - -static long -getlong(void) -{ - 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; -} - -static unsigned long -getulong(void) -{ - unsigned long val; - char *ep; - - if (!*gargv) - return(0UL); - - if (**gargv == '\"' || **gargv == '\'') - return (unsigned long) *((*gargv++)+1); - - errno = 0; - val = strtoul (*gargv, &ep, 0); - check_conversion(*gargv++, ep); - return val; -} - -static double -getdouble(void) -{ - double val; - char *ep; - - if (!*gargv) - return(0.0); - - if (**gargv == '\"' || **gargv == '\'') - return (double) *((*gargv++)+1); - - errno = 0; - val = strtod (*gargv, &ep); - check_conversion(*gargv++, ep); - return val; -} - -static void -check_conversion(const char *s, const char *ep) -{ - if (*ep) { - if (ep == s) - warnx ("%s: expected numeric value", s); - else - warnx ("%s: not completely converted", s); - rval = 1; - } else if (errno == ERANGE) { - (void)fprintf(stderr, "%s: %s\n", s, strerror(ERANGE)); - rval = 1; - } -} - -static void -usage(void) -{ - (void)fprintf(stderr, "usage: printf format [argument ...]\n"); -}