diff options
Diffstat (limited to 'usr.bin/printf/printf.c')
| -rw-r--r-- | usr.bin/printf/printf.c | 550 |
1 files changed, 371 insertions, 179 deletions
diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c index a64e4d4c4d2c..0fbcfcf4ab22 100644 --- a/usr.bin/printf/printf.c +++ b/usr.bin/printf/printf.c @@ -32,17 +32,78 @@ */ #ifndef lint +#if !defined(SHELL) && !defined(BUILTIN) char copyright[] = "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ All rights reserved.\n"; +#endif #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)printf.c 5.9 (Berkeley) 6/1/90"; +/*static char sccsid[] = "from: @(#)printf.c 5.9 (Berkeley) 6/1/90";*/ +static char rcsid[] = "$Id: printf.c,v 1.2 1993/11/23 00:06:21 jtc Exp $"; #endif /* not lint */ -#include <sys/types.h> +#include <ctype.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <locale.h> +#include <err.h> + +static int print_escape_str __P((const char *)); +static int 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 char *getstr __P((void)); +static char *mklong __P((char *, int)); +static void usage __P((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') + +#ifdef SHELL +#define main printfcmd +#include "../../bin/sh/bltin/bltin.h" + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <vararg.h> +#endif + +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); +#endif + vsprintf(buf, fmt, ap); + va_end(ap); + + error(buf); +} +#endif /* SHELL */ #define PF(f, func) { \ if (fieldwidth) \ @@ -56,196 +117,303 @@ static char sccsid[] = "@(#)printf.c 5.9 (Berkeley) 6/1/90"; (void)printf(f, func); \ } -char **gargv; - +int +#ifdef BUILTIN +progprintf(argc, argv) +#else main(argc, argv) +#endif int argc; char **argv; { - static char *skip1, *skip2; - register char *format, *fmt, *start; - register int end, fieldwidth, precision; - char convch, nextch, *getstr(), *index(), *mklong(); - double getdouble(); - long getlong(); + register char *fmt, *start; + register int fieldwidth, precision; + char convch, nextch; + char *format; + int ch; + +#if !defined(SHELL) && !defined(BUILTIN) + setlocale (LC_ALL, ""); +#endif - if (argc < 2) { - fprintf(stderr, "usage: printf format [arg ...]\n"); - exit(1); + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + case '?': + default: + usage(); + return (1); + } } + argc -= optind; + argv += optind; - /* - * 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. - */ - skip1 = "#-+ 0"; - skip2 = "*0123456789"; + if (argc < 1) { + usage(); + return (1); + } - escape(fmt = format = *++argv); /* backslash interpretation */ + format = *argv; gargv = ++argv; - for (;;) { - end = 0; + +#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 */ -next: for (start = fmt;; ++fmt) { - if (!*fmt) { - /* avoid infinite loop */ - if (end == 1) { - fprintf(stderr, - "printf: missing format character.\n"); - exit(1); + 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; } - end = 1; - if (fmt > start) - (void)printf("%s", start); - if (!*gargv) - exit(0); - fmt = format; - goto next; - } - /* %% prints a % */ - if (*fmt == '%') { - if (*++fmt != '%') + + /* skip to field width */ + for (; index(SKIP1, *fmt); ++fmt); + fieldwidth = *fmt == '*' ? getint() : 0; + + /* skip to possible '.', get following precision */ + for (; index(SKIP2, *fmt); ++fmt); + if (*fmt == '.') + ++fmt; + precision = *fmt == '*' ? getint() : 0; + + for (; index(SKIP2, *fmt); ++fmt); + if (!*fmt) { + warnx ("missing format character"); + rval = 1; + return; + } + + 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': + case 'o': + case 'u': + case 'x': + case 'X': { + char *f = mklong(start, convch); + long p = getlong(); + PF(f, p); break; - *fmt++ = '\0'; - (void)printf("%s", start); - goto next; + } + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + double p = getdouble(); + PF(start, p); + break; + } + default: + warnx ("%s: invalid directive", start); + rval = 1; + } + *(fmt + 1) = nextch; + break; + + case '\\': + fmt += print_escape(fmt); + break; + + default: + putchar (*fmt); + break; } } + } while (gargv > argv && *gargv); - /* skip to field width */ - for (; index(skip1, *fmt); ++fmt); - fieldwidth = *fmt == '*' ? getint() : 0; + return (rval); +} - /* skip to possible '.', get following precision */ - for (; index(skip2, *fmt); ++fmt); - if (*fmt == '.') - ++fmt; - precision = *fmt == '*' ? getint() : 0; - /* skip to conversion char */ - for (; index(skip2, *fmt); ++fmt); - if (!*fmt) { - fprintf(stderr, "printf: missing format character.\n"); - exit(1); - } +/* + * 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) + register const char *str; +{ + int value; + int c; - convch = *fmt; - nextch = *++fmt; - *fmt = '\0'; - switch(convch) { - case 'c': { - char p = getchr(); - PF(start, p); - break; - } - case 's': { - char *p = getstr(); - PF(start, p); - break; + 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); + } + } else { + putchar (*str); } - case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { - char *f = mklong(start, convch); - long p = getlong(); - PF(f, p); - break; + str++; + } + + return 0; +} + +/* + * Print "standard" escape characters + */ +static int +print_escape(str) + register const char *str; +{ + const char *start = str; + int c; + int value; + + 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); } - case 'e': case 'E': case 'f': case 'g': case 'G': { - double p = getdouble(); - PF(start, p); - break; + putchar(value); + return str - start - 1; + /* NOTREACHED */ + + case 'x': + str++; + for (value = 0; isxdigit(*str); str++) { + value <<= 4; + value += hextobin(*str); } - default: - fprintf(stderr, "printf: illegal format character.\n"); - exit(1); + if (value > UCHAR_MAX) { + warnx ("escape sequence out of range for character"); + rval = 1; } - *fmt = nextch; + putchar (value); + return str - start - 1; + /* NOTREACHED */ + + case '\\': /* backslash */ + putchar('\\'); + break; + + case '\'': /* single quote */ + putchar('\''); + break; + + case '"': /* double quote */ + putchar('"'); + break; + + case 'a': /* alert */ +#ifdef __STDC__ + putchar('\a'); +#else + putchar(007); +#endif + break; + + case 'b': /* backspace */ + putchar('\b'); + break; + + case 'e': /* escape */ +#ifdef __GNUC__ + putchar('\e'); +#else + putchar(033); +#endif + 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; + + default: + putchar(*str); + warnx("unknown escape sequence `\\%c'", *str); + rval = 1; } - /* NOTREACHED */ + + return 1; } -char * +static char * mklong(str, ch) char *str, ch; { - int len; - char *copy, *malloc(); + static char copy[64]; + int len; len = strlen(str) + 2; - if (!(copy = malloc((u_int)len))) { /* never freed; XXX */ - fprintf(stderr, "printf: out of memory.\n"); - exit(1); - } - bcopy(str, copy, len - 3); + memmove(copy, str, len - 3); copy[len - 3] = 'l'; copy[len - 2] = ch; copy[len - 1] = '\0'; - return(copy); -} - -escape(fmt) - register char *fmt; -{ - register char *store; - register int value, c; - - for (store = fmt; c = *fmt; ++fmt, ++store) { - if (c != '\\') { - *store = c; - continue; - } - switch (*++fmt) { - case '\0': /* EOS, user error */ - *store = '\\'; - *++store = '\0'; - return; - case '\\': /* backslash */ - case '\'': /* single quote */ - *store = *fmt; - break; - case 'a': /* bell/alert */ - *store = '\7'; - break; - case 'b': /* backspace */ - *store = '\b'; - 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 = '\13'; - break; - /* octal constant */ - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - for (c = 3, value = 0; - c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { - value <<= 3; - value += *fmt - '0'; - } - --fmt; - *store = value; - break; - default: - *store = *fmt; - break; - } - } - *store = '\0'; + return (copy); } +static int getchr() { if (!*gargv) @@ -253,7 +421,7 @@ getchr() return((int)**gargv++); } -char * +static char * getstr() { if (!*gargv) @@ -262,46 +430,70 @@ getstr() } static char *number = "+-.0123456789"; +static int getint() { if (!*gargv) return(0); + if (index(number, **gargv)) return(atoi(*gargv++)); - return(asciicode()); + + return 0; } -long +static long getlong() { - long atol(); + long val; + char *ep; if (!*gargv) - return((long)0); - if (index(number, **gargv)) - return(strtol(*gargv++, (char **)NULL, 0)); - return((long)asciicode()); + return(0L); + + if (**gargv == '\"' || **gargv == '\'') { + val = gargv[0][1]; + gargv++; + return val; + } + + val = strtol (*gargv, &ep, 0); + if (*ep) { + warnx ("incompletely converted argument: %s", *gargv); + rval = 1; + } + + gargv++; + return val; } -double +static double getdouble() { - double atof(); + double val; + char *ep; if (!*gargv) - return((double)0); - if (index(number, **gargv)) - return(atof(*gargv++)); - return((double)asciicode()); + return(0.0); + + if (**gargv == '\"' || **gargv == '\'') { + val = gargv[0][1]; + gargv++; + return val; + } + + val = strtod (*gargv, &ep); + if (*ep) { + warnx ("incompletely converted argument: %s", *gargv); + rval = 1; + } + + gargv++; + return val; } -asciicode() +static void +usage() { - register char ch; - - ch = **gargv; - if (ch == '\'' || ch == '"') - ch = (*gargv)[1]; - ++gargv; - return(ch); + (void)fprintf(stderr, "usage: printf format [arg ...]\n"); } |
