[BACK]Return to printf.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / printf

Annotation of src/usr.bin/printf/printf.c, Revision 1.13

1.13    ! millert     1: /*     $OpenBSD: printf.c,v 1.12 2004/05/31 15:48:26 pedro Exp $       */
1.2       deraadt     2:
1.1       deraadt     3: /*
                      4:  * Copyright (c) 1989 The Regents of the University of California.
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
1.9       millert    15:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  */
                     31:
                     32: #ifndef lint
                     33: #if !defined(SHELL) && !defined(BUILTIN)
                     34: char copyright[] =
                     35: "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
                     36:  All rights reserved.\n";
                     37: #endif
                     38: #endif /* not lint */
                     39:
                     40: #ifndef lint
                     41: /*static char sccsid[] = "from: @(#)printf.c   5.9 (Berkeley) 6/1/90";*/
1.13    ! millert    42: static char rcsid[] = "$OpenBSD: printf.c,v 1.12 2004/05/31 15:48:26 pedro Exp $";
1.1       deraadt    43: #endif /* not lint */
                     44:
                     45: #include <ctype.h>
                     46: #include <stdio.h>
                     47: #include <stdlib.h>
                     48: #include <string.h>
                     49: #include <limits.h>
                     50: #include <locale.h>
                     51: #include <errno.h>
                     52: #include <err.h>
                     53:
1.6       millert    54: static int      print_escape_str(const char *);
                     55: static int      print_escape(const char *);
1.1       deraadt    56:
1.6       millert    57: static int      getchr(void);
                     58: static double   getdouble(void);
                     59: static int      getint(void);
                     60: static long     getlong(void);
                     61: static unsigned long getulong(void);
                     62: static char    *getstr(void);
                     63: static char    *mklong(const char *, int);
                     64: static void      check_conversion(const char *, const char *);
                     65: static void     usage(void);
1.1       deraadt    66:
                     67: static int     rval;
                     68: static char  **gargv;
                     69:
                     70: #define isodigit(c)    ((c) >= '0' && (c) <= '7')
                     71: #define octtobin(c)    ((c) - '0')
                     72: #define hextobin(c)    ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
                     73:
                     74: #ifdef SHELL
                     75: #define main printfcmd
                     76: #include "../../bin/sh/bltin/bltin.h"
                     77: #include <stdarg.h>
                     78:
                     79: static void
                     80: warnx(const char *fmt, ...)
                     81: {
                     82:
                     83:        char buf[64];
                     84:        va_list ap;
                     85:
                     86:        va_start(ap, fmt);
1.8       deraadt    87:        vsnprintf(buf, sizeof buf, fmt, ap);
1.1       deraadt    88:        va_end(ap);
                     89:
                     90:        error(buf);
                     91: }
                     92: #endif /* SHELL */
                     93:
                     94: #define PF(f, func) { \
                     95:        if (fieldwidth) \
                     96:                if (precision) \
                     97:                        (void)printf(f, fieldwidth, precision, func); \
                     98:                else \
                     99:                        (void)printf(f, fieldwidth, func); \
                    100:        else if (precision) \
                    101:                (void)printf(f, precision, func); \
                    102:        else \
                    103:                (void)printf(f, func); \
                    104: }
                    105:
                    106: int
                    107: #ifdef BUILTIN
1.10      deraadt   108: progprintf(int argc, char *argv[])
1.1       deraadt   109: #else
1.10      deraadt   110: main(int argc, char *argv[])
1.1       deraadt   111: #endif
                    112: {
1.5       mpech     113:        char *fmt, *start;
                    114:        int fieldwidth, precision;
1.1       deraadt   115:        char convch, nextch;
                    116:        char *format;
                    117:
                    118: #if !defined(SHELL) && !defined(BUILTIN)
                    119:        setlocale (LC_ALL, "");
                    120: #endif
1.13    ! millert   121:
        !           122:        /* Need to accept/ignore "--" option. */
        !           123:        if (argc > 1 && strcmp(argv[1], "--") == 0) {
        !           124:                argc--;
        !           125:                argv++;
        !           126:        }
1.1       deraadt   127:
1.11      millert   128:        if (argc < 2) {
1.1       deraadt   129:                usage();
                    130:                return (1);
                    131:        }
                    132:
1.11      millert   133:        format = *++argv;
1.1       deraadt   134:        gargv = ++argv;
                    135:
                    136: #define SKIP1  "#-+ 0"
                    137: #define SKIP2  "*0123456789"
                    138:        do {
                    139:                /*
                    140:                 * Basic algorithm is to scan the format string for conversion
                    141:                 * specifications -- once one is found, find out if the field
                    142:                 * width or precision is a '*'; if it is, gather up value.
                    143:                 * Note, format strings are reused as necessary to use up the
                    144:                 * provided arguments, arguments of zero/null string are
                    145:                 * provided to use up the format string.
                    146:                 */
                    147:
                    148:                /* find next format specification */
                    149:                for (fmt = format; *fmt; fmt++) {
                    150:                        switch (*fmt) {
                    151:                        case '%':
                    152:                                start = fmt++;
                    153:
                    154:                                if (*fmt == '%') {
                    155:                                        putchar ('%');
                    156:                                        break;
                    157:                                } else if (*fmt == 'b') {
                    158:                                        char *p = getstr();
                    159:                                        if (print_escape_str(p)) {
                    160:                                                return (rval);
                    161:                                        }
                    162:                                        break;
                    163:                                }
                    164:
                    165:                                /* skip to field width */
1.3       millert   166:                                for (; strchr(SKIP1, *fmt); ++fmt) ;
1.1       deraadt   167:                                fieldwidth = *fmt == '*' ? getint() : 0;
                    168:
                    169:                                /* skip to possible '.', get following precision */
1.3       millert   170:                                for (; strchr(SKIP2, *fmt); ++fmt) ;
1.1       deraadt   171:                                if (*fmt == '.')
                    172:                                        ++fmt;
                    173:                                precision = *fmt == '*' ? getint() : 0;
                    174:
1.3       millert   175:                                for (; strchr(SKIP2, *fmt); ++fmt) ;
1.1       deraadt   176:                                if (!*fmt) {
                    177:                                        warnx ("missing format character");
                    178:                                        return(1);
                    179:                                }
                    180:
                    181:                                convch = *fmt;
                    182:                                nextch = *(fmt + 1);
                    183:                                *(fmt + 1) = '\0';
                    184:                                switch(convch) {
                    185:                                case 'c': {
                    186:                                        char p = getchr();
                    187:                                        PF(start, p);
                    188:                                        break;
                    189:                                }
                    190:                                case 's': {
                    191:                                        char *p = getstr();
                    192:                                        PF(start, p);
                    193:                                        break;
                    194:                                }
                    195:                                case 'd':
                    196:                                case 'i': {
1.4       deraadt   197:                                        long p;
1.1       deraadt   198:                                        char *f = mklong(start, convch);
1.4       deraadt   199:                                        if (!f) {
                    200:                                                warnx("out of memory");
                    201:                                                return (1);
                    202:                                        }
                    203:                                        p = getlong();
1.1       deraadt   204:                                        PF(f, p);
                    205:                                        break;
                    206:                                }
                    207:                                case 'o':
                    208:                                case 'u':
                    209:                                case 'x':
                    210:                                case 'X': {
1.4       deraadt   211:                                        unsigned long p;
1.1       deraadt   212:                                        char *f = mklong(start, convch);
1.4       deraadt   213:                                        if (!f) {
                    214:                                                warnx("out of memory");
                    215:                                                return (1);
                    216:                                        }
                    217:                                        p = getulong();
1.1       deraadt   218:                                        PF(f, p);
                    219:                                        break;
                    220:                                }
                    221:                                case 'e':
                    222:                                case 'E':
                    223:                                case 'f':
                    224:                                case 'g':
                    225:                                case 'G': {
                    226:                                        double p = getdouble();
                    227:                                        PF(start, p);
                    228:                                        break;
                    229:                                }
                    230:                                default:
                    231:                                        warnx ("%s: invalid directive", start);
                    232:                                        return(1);
                    233:                                }
                    234:                                *(fmt + 1) = nextch;
                    235:                                break;
                    236:
                    237:                        case '\\':
                    238:                                fmt += print_escape(fmt);
                    239:                                break;
                    240:
                    241:                        default:
                    242:                                putchar (*fmt);
                    243:                                break;
                    244:                        }
                    245:                }
                    246:        } while (gargv > argv && *gargv);
                    247:
                    248:        return (rval);
                    249: }
                    250:
                    251:
                    252: /*
                    253:  * Print SysV echo(1) style escape string
                    254:  *     Halts processing string and returns 1 if a \c escape is encountered.
                    255:  */
                    256: static int
1.10      deraadt   257: print_escape_str(const char *str)
1.1       deraadt   258: {
                    259:        int value;
                    260:        int c;
                    261:
                    262:        while (*str) {
                    263:                if (*str == '\\') {
                    264:                        str++;
                    265:                        /*
                    266:                         * %b string octal constants are not like those in C.
                    267:                         * They start with a \0, and are followed by 0, 1, 2,
                    268:                         * or 3 octal digits.
                    269:                         */
                    270:                        if (*str == '0') {
                    271:                                str++;
                    272:                                for (c = 3, value = 0; c-- && isodigit(*str); str++) {
                    273:                                        value <<= 3;
                    274:                                        value += octtobin(*str);
                    275:                                }
                    276:                                putchar (value);
                    277:                                str--;
                    278:                        } else if (*str == 'c') {
                    279:                                return 1;
                    280:                        } else {
                    281:                                str--;
                    282:                                str += print_escape(str);
                    283:                        }
                    284:                } else {
                    285:                        putchar (*str);
                    286:                }
                    287:                str++;
                    288:        }
                    289:
                    290:        return 0;
                    291: }
                    292:
                    293: /*
                    294:  * Print "standard" escape characters
                    295:  */
                    296: static int
1.10      deraadt   297: print_escape(const char *str)
1.1       deraadt   298: {
                    299:        const char *start = str;
                    300:        int value;
                    301:        int c;
                    302:
                    303:        str++;
                    304:
                    305:        switch (*str) {
                    306:        case '0': case '1': case '2': case '3':
                    307:        case '4': case '5': case '6': case '7':
                    308:                for (c = 3, value = 0; c-- && isodigit(*str); str++) {
                    309:                        value <<= 3;
                    310:                        value += octtobin(*str);
                    311:                }
                    312:                putchar(value);
                    313:                return str - start - 1;
                    314:                /* NOTREACHED */
                    315:
                    316:        case 'x':
                    317:                str++;
                    318:                for (value = 0; isxdigit(*str); str++) {
                    319:                        value <<= 4;
                    320:                        value += hextobin(*str);
                    321:                }
                    322:                if (value > UCHAR_MAX) {
                    323:                        warnx ("escape sequence out of range for character");
                    324:                        rval = 1;
                    325:                }
                    326:                putchar (value);
                    327:                return str - start - 1;
                    328:                /* NOTREACHED */
                    329:
                    330:        case '\\':                      /* backslash */
                    331:                putchar('\\');
                    332:                break;
                    333:
                    334:        case '\'':                      /* single quote */
                    335:                putchar('\'');
                    336:                break;
                    337:
                    338:        case '"':                       /* double quote */
                    339:                putchar('"');
                    340:                break;
                    341:
                    342:        case 'a':                       /* alert */
                    343:                putchar('\a');
                    344:                break;
                    345:
                    346:        case 'b':                       /* backspace */
                    347:                putchar('\b');
                    348:                break;
                    349:
                    350:        case 'e':                       /* escape */
                    351: #ifdef __GNUC__
                    352:                putchar('\e');
                    353: #else
                    354:                putchar(033);
                    355: #endif
                    356:                break;
                    357:
                    358:        case 'f':                       /* form-feed */
                    359:                putchar('\f');
                    360:                break;
                    361:
                    362:        case 'n':                       /* newline */
                    363:                putchar('\n');
                    364:                break;
                    365:
                    366:        case 'r':                       /* carriage-return */
                    367:                putchar('\r');
                    368:                break;
                    369:
                    370:        case 't':                       /* tab */
                    371:                putchar('\t');
                    372:                break;
                    373:
                    374:        case 'v':                       /* vertical-tab */
                    375:                putchar('\v');
                    376:                break;
                    377:
                    378:        default:
                    379:                putchar(*str);
                    380:                warnx("unknown escape sequence `\\%c'", *str);
                    381:                rval = 1;
                    382:        }
                    383:
                    384:        return 1;
                    385: }
                    386:
                    387: static char *
1.10      deraadt   388: mklong(const char *str, int ch)
1.1       deraadt   389: {
1.4       deraadt   390:        static char *copy;
                    391:        static int copysize;
1.1       deraadt   392:        int len;
                    393:
                    394:        len = strlen(str) + 2;
1.4       deraadt   395:        if (copysize < len) {
                    396:                char *newcopy;
                    397:                copysize = len + 256;
                    398:
                    399:                newcopy = realloc(copy, copysize);
                    400:                if (newcopy == NULL) {
                    401:                        copysize = 0;
                    402:                        free(copy);
                    403:                        copy = NULL;
                    404:                        return (NULL);
                    405:                }
                    406:                copy = newcopy;
                    407:        }
1.1       deraadt   408:        (void) memmove(copy, str, len - 3);
                    409:        copy[len - 3] = 'l';
                    410:        copy[len - 2] = ch;
                    411:        copy[len - 1] = '\0';
                    412:        return (copy);
                    413: }
                    414:
                    415: static int
1.10      deraadt   416: getchr(void)
1.1       deraadt   417: {
                    418:        if (!*gargv)
                    419:                return((int)'\0');
                    420:        return((int)**gargv++);
                    421: }
                    422:
                    423: static char *
1.10      deraadt   424: getstr(void)
1.1       deraadt   425: {
                    426:        if (!*gargv)
                    427:                return("");
                    428:        return(*gargv++);
                    429: }
                    430:
                    431: static char *number = "+-.0123456789";
                    432: static int
1.10      deraadt   433: getint(void)
1.1       deraadt   434: {
                    435:        if (!*gargv)
                    436:                return(0);
                    437:
1.3       millert   438:        if (strchr(number, **gargv))
1.1       deraadt   439:                return(atoi(*gargv++));
                    440:
                    441:        return 0;
                    442: }
                    443:
                    444: static long
1.10      deraadt   445: getlong(void)
1.1       deraadt   446: {
                    447:        long val;
                    448:        char *ep;
                    449:
                    450:        if (!*gargv)
                    451:                return(0L);
                    452:
                    453:        if (**gargv == '\"' || **gargv == '\'')
                    454:                return (long) *((*gargv++)+1);
                    455:
                    456:        errno = 0;
                    457:        val = strtol (*gargv, &ep, 0);
                    458:        check_conversion(*gargv++, ep);
                    459:        return val;
                    460: }
                    461:
                    462: static unsigned long
1.10      deraadt   463: getulong(void)
1.1       deraadt   464: {
                    465:        unsigned long val;
                    466:        char *ep;
                    467:
                    468:        if (!*gargv)
                    469:                return(0UL);
                    470:
                    471:        if (**gargv == '\"' || **gargv == '\'')
                    472:                return (unsigned long) *((*gargv++)+1);
                    473:
                    474:        errno = 0;
                    475:        val = strtoul (*gargv, &ep, 0);
                    476:        check_conversion(*gargv++, ep);
                    477:        return val;
                    478: }
                    479:
                    480: static double
1.10      deraadt   481: getdouble(void)
1.1       deraadt   482: {
                    483:        double val;
                    484:        char *ep;
                    485:
                    486:        if (!*gargv)
                    487:                return(0.0);
                    488:
                    489:        if (**gargv == '\"' || **gargv == '\'')
                    490:                return (double) *((*gargv++)+1);
                    491:
                    492:        errno = 0;
                    493:        val = strtod (*gargv, &ep);
                    494:        check_conversion(*gargv++, ep);
                    495:        return val;
                    496: }
                    497:
                    498: static void
1.10      deraadt   499: check_conversion(const char *s, const char *ep)
1.1       deraadt   500: {
                    501:        if (*ep) {
                    502:                if (ep == s)
                    503:                        warnx ("%s: expected numeric value", s);
                    504:                else
                    505:                        warnx ("%s: not completely converted", s);
                    506:                rval = 1;
                    507:        } else if (errno == ERANGE) {
                    508:                warnx ("%s: %s", s, strerror(ERANGE));
                    509:                rval = 1;
                    510:        }
                    511: }
                    512:
                    513: static void
1.10      deraadt   514: usage(void)
1.1       deraadt   515: {
                    516:        (void)fprintf(stderr, "usage: printf format [arg ...]\n");
                    517: }