diff options
author | Garrett D'Amore <garrett@nexenta.com> | 2010-10-14 09:27:05 -0700 |
---|---|---|
committer | Garrett D'Amore <garrett@nexenta.com> | 2010-10-14 09:27:05 -0700 |
commit | f967d548b23673f68e7356c6c741329b7698269c (patch) | |
tree | 998883f222b70cb60d6eab1800694aa38704f524 /usr | |
parent | 4aed303fe6fe4f743ab401f6fafa1c161060c57b (diff) | |
download | illumos-joyent-f967d548b23673f68e7356c6c741329b7698269c.tar.gz |
345 need an open printf(1)
Reviewed by: cjlove@san.rr.com
Reviewed by: jason.brian.king@gmail.com
Reviewed by: gwr@nexenta.com
Approved by: gwr@nexenta.com
Diffstat (limited to 'usr')
-rw-r--r-- | usr/src/Makefile.lint | 1 | ||||
-rw-r--r-- | usr/src/cmd/Makefile | 2 | ||||
-rw-r--r-- | usr/src/cmd/printf/Makefile | 30 | ||||
-rw-r--r-- | usr/src/cmd/printf/printf.c | 555 | ||||
-rw-r--r-- | usr/src/pkg/manifests/SUNWcs.mf | 1 | ||||
-rw-r--r-- | usr/src/pkg/manifests/text-locale.mf | 1 |
6 files changed, 589 insertions, 1 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 987455a16c..c3d9cd6c15 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -233,6 +233,7 @@ COMMON_SUBDIRS = \ cmd/pools \ cmd/power \ cmd/powertop \ + cmd/printf \ cmd/latencytop \ cmd/ppgsz \ cmd/praudit \ diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 9a844e6dd5..dac3533bbb 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -20,6 +20,7 @@ # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2010 Nexenta Systems, Inc. All rights reserved. include ../Makefile.master @@ -308,6 +309,7 @@ COMMON_SUBDIRS= \ pr \ prctl \ print \ + printf \ priocntl \ profiles \ projadd \ diff --git a/usr/src/cmd/printf/Makefile b/usr/src/cmd/printf/Makefile new file mode 100644 index 0000000000..e90154574b --- /dev/null +++ b/usr/src/cmd/printf/Makefile @@ -0,0 +1,30 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy is of the CDDL is also available via the Internet +# at http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2010 Nexenta Systems, Inc. All rights reserved. +# + +PROG= printf + +include ../Makefile.cmd + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/printf/printf.c b/usr/src/cmd/printf/printf.c new file mode 100644 index 0000000000..d0b2f527e1 --- /dev/null +++ b/usr/src/cmd/printf/printf.c @@ -0,0 +1,555 @@ +/* + * Copyright 2010 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 1989, 1993 + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <locale.h> +#include <note.h> + +#define warnx1(a, b, c) warnx(a) +#define warnx2(a, b, c) warnx(a, b) +#define warnx3(a, b, c) warnx(a, b, c) + +#define PTRDIFF(x, y) ((uintptr_t)(x) - (uintptr_t)(y)) + +#define _(x) gettext(x) + +#define PF(f, func) do { \ + char *b = NULL; \ + int dollar = 0; \ + if (*f == '$') { \ + dollar++; \ + *f = '%'; \ + } \ + if (havewidth) \ + if (haveprec) \ + (void) asprintf(&b, f, fieldwidth, precision, func); \ + else \ + (void) asprintf(&b, f, fieldwidth, func); \ + else if (haveprec) \ + (void) asprintf(&b, f, precision, func); \ + else \ + (void) asprintf(&b, f, func); \ + if (b) { \ + (void) fputs(b, stdout); \ + free(b); \ + } \ + if (dollar) \ + *f = '$'; \ +_NOTE(CONSTCOND) } while (0) + +static int asciicode(void); +static char *doformat(char *, int *); +static int escape(char *, int, size_t *); +static int getchr(void); +static int getfloating(long double *, int); +static int getint(int *); +static int getnum(intmax_t *, uintmax_t *, int); +static const char + *getstr(void); +static char *mknum(char *, char); +static void usage(void); + +static int myargc; +static char **myargv; +static char **gargv; + +int +main(int argc, char *argv[]) +{ + size_t len; + int chopped, end, rval; + char *format, *fmt, *start; + + (void) setlocale(LC_ALL, ""); + + argv++; + argc--; + + /* + * POSIX says: Standard utilities that do not accept options, + * but that do accept operands, shall recognize "--" as a + * first argument to be discarded. + */ + if (strcmp(argv[0], "--") == 0) { + argc--; + argv++; + } + + if (argc < 1) { + usage(); + return (1); + } + + /* + * Basic algorithm is to scan the format string for conversion + * specifications -- once one is found, find out if the field + * width or precision is a '*'; if it is, gather up value. Note, + * format strings are reused as necessary to use up the provided + * arguments, arguments of zero/null string are provided to use + * up the format string. + */ + fmt = format = *argv; + chopped = escape(fmt, 1, &len); /* backslash interpretation */ + rval = end = 0; + gargv = ++argv; + + for (;;) { + char **maxargv = gargv; + + myargv = gargv; + for (myargc = 0; gargv[myargc]; myargc++) + /* nop */; + start = fmt; + while (fmt < format + len) { + if (fmt[0] == '%') { + (void) fwrite(start, 1, PTRDIFF(fmt, start), + stdout); + if (fmt[1] == '%') { + /* %% prints a % */ + (void) putchar('%'); + fmt += 2; + } else { + fmt = doformat(fmt, &rval); + if (fmt == NULL) + return (1); + end = 0; + } + start = fmt; + } else + fmt++; + if (gargv > maxargv) + maxargv = gargv; + } + gargv = maxargv; + + if (end == 1) { + warnx1(_("missing format character"), NULL, NULL); + return (1); + } + (void) fwrite(start, 1, PTRDIFF(fmt, start), stdout); + if (chopped || !*gargv) + return (rval); + /* Restart at the beginning of the format string. */ + fmt = format; + end = 1; + } + /* NOTREACHED */ +} + + +static char * +doformat(char *start, int *rval) +{ + static const char skip1[] = "#'-+ 0"; + static const char skip2[] = "0123456789"; + char *fmt; + int fieldwidth, haveprec, havewidth, mod_ldbl, precision; + char convch, nextch; + + fmt = start + 1; + + /* look for "n$" field index specifier */ + fmt += strspn(fmt, skip2); + if ((*fmt == '$') && (fmt != (start + 1))) { + int idx = atoi(start + 1); + if (idx <= myargc) { + gargv = &myargv[idx - 1]; + } else { + gargv = &myargv[myargc]; + } + start = fmt; + fmt++; + } else { + fmt = start + 1; + } + + /* skip to field width */ + fmt += strspn(fmt, skip1); + if (*fmt == '*') { + if (getint(&fieldwidth)) + return (NULL); + havewidth = 1; + ++fmt; + } else { + havewidth = 0; + + /* skip to possible '.', get following precision */ + fmt += strspn(fmt, skip2); + } + if (*fmt == '.') { + /* precision present? */ + ++fmt; + if (*fmt == '*') { + if (getint(&precision)) + return (NULL); + haveprec = 1; + ++fmt; + } else { + haveprec = 0; + + /* skip to conversion char */ + fmt += strspn(fmt, skip2); + } + } else + haveprec = 0; + if (!*fmt) { + warnx1(_("missing format character"), NULL, NULL); + return (NULL); + } + + /* + * Look for a length modifier. POSIX doesn't have these, so + * we only support them for floating-point conversions, which + * are extensions. This is useful because the L modifier can + * be used to gain extra range and precision, while omitting + * it is more likely to produce consistent results on different + * architectures. This is not so important for integers + * because overflow is the only bad thing that can happen to + * them, but consider the command printf %a 1.1 + */ + if (*fmt == 'L') { + mod_ldbl = 1; + fmt++; + if (!strchr("aAeEfFgG", *fmt)) { + warnx2(_("bad modifier L for %%%c"), *fmt, NULL); + return (NULL); + } + } else { + mod_ldbl = 0; + } + + convch = *fmt; + nextch = *++fmt; + *fmt = '\0'; + switch (convch) { + case 'b': { + size_t len; + char *p; + int getout; + + p = strdup(getstr()); + if (p == NULL) { + warnx2("%s", strerror(ENOMEM), NULL); + return (NULL); + } + getout = escape(p, 0, &len); + *(fmt - 1) = 's'; + PF(start, p); + *(fmt - 1) = 'b'; + free(p); + + if (getout) + return (fmt); + break; + } + case 'c': { + char p; + + p = getchr(); + PF(start, p); + break; + } + case 's': { + const char *p; + + p = getstr(); + PF(start, p); + break; + } + case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { + char *f; + intmax_t val; + uintmax_t uval; + int signedconv; + + signedconv = (convch == 'd' || convch == 'i'); + if ((f = mknum(start, convch)) == NULL) + return (NULL); + if (getnum(&val, &uval, signedconv)) + *rval = 1; + if (signedconv) + PF(f, val); + else + PF(f, uval); + break; + } + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'a': case 'A': { + long double p; + + if (getfloating(&p, mod_ldbl)) + *rval = 1; + if (mod_ldbl) + PF(start, p); + else + PF(start, (double)p); + break; + } + default: + warnx2(_("illegal format character %c"), convch, NULL); + return (NULL); + } + *fmt = nextch; + return (fmt); +} + +static char * +mknum(char *str, char ch) +{ + static char *copy; + static size_t copy_size; + char *newcopy; + size_t len, newlen; + + len = strlen(str) + 2; + if (len > copy_size) { + newlen = ((len + 1023) >> 10) << 10; + if ((newcopy = realloc(copy, newlen)) == NULL) { + warnx2("%s", strerror(ENOMEM), NULL); + return (NULL); + } + copy = newcopy; + copy_size = newlen; + } + + (void) memmove(copy, str, len - 3); + copy[len - 3] = 'j'; + copy[len - 2] = ch; + copy[len - 1] = '\0'; + return (copy); +} + +static int +escape(char *fmt, int percent, size_t *len) +{ + char *save, *store, c; + int value; + + for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { + if (c != '\\') { + *store = c; + continue; + } + switch (*++fmt) { + case '\0': /* EOS, user error */ + *store = '\\'; + *++store = '\0'; + *len = PTRDIFF(store, save); + return (0); + case '\\': /* backslash */ + case '\'': /* single quote */ + *store = *fmt; + break; + case 'a': /* bell/alert */ + *store = '\a'; + break; + case 'b': /* backspace */ + *store = '\b'; + break; + case 'c': + *store = '\0'; + *len = PTRDIFF(store, save); + return (1); + case 'f': /* form-feed */ + *store = '\f'; + break; + case 'n': /* newline */ + *store = '\n'; + break; + case 'r': /* carriage-return */ + *store = '\r'; + break; + case 't': /* horizontal tab */ + *store = '\t'; + break; + case 'v': /* vertical tab */ + *store = '\v'; + break; + /* octal constant */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = (!percent && *fmt == '0') ? 4 : 3; + for (value = 0; + c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { + value <<= 3; + value += *fmt - '0'; + } + --fmt; + if (percent && value == '%') { + *store++ = '%'; + *store = '%'; + } else + *store = (char)value; + break; + default: + *store = *fmt; + break; + } + } + *store = '\0'; + *len = PTRDIFF(store, save); + return (0); +} + +static int +getchr(void) +{ + if (!*gargv) + return ('\0'); + return ((int)**gargv++); +} + +static const char * +getstr(void) +{ + if (!*gargv) + return (""); + return (*gargv++); +} + +static int +getint(int *ip) +{ + intmax_t val; + uintmax_t uval; + int rval; + + if (getnum(&val, &uval, 1)) + return (1); + rval = 0; + if (val < INT_MIN || val > INT_MAX) { + warnx3("%s: %s", *gargv, strerror(ERANGE)); + rval = 1; + } + *ip = (int)val; + return (rval); +} + +static int +getnum(intmax_t *ip, uintmax_t *uip, int signedconv) +{ + char *ep; + int rval; + + if (!*gargv) { + *ip = 0; + return (0); + } + if (**gargv == '"' || **gargv == '\'') { + if (signedconv) + *ip = asciicode(); + else + *uip = asciicode(); + return (0); + } + rval = 0; + errno = 0; + if (signedconv) + *ip = strtoimax(*gargv, &ep, 0); + else + *uip = strtoumax(*gargv, &ep, 0); + if (ep == *gargv) { + warnx2(_("%s: expected numeric value"), *gargv, NULL); + rval = 1; + } else if (*ep != '\0') { + warnx2(_("%s: not completely converted"), *gargv, NULL); + rval = 1; + } + if (errno == ERANGE) { + warnx3("%s: %s", *gargv, strerror(ERANGE)); + rval = 1; + } + ++gargv; + return (rval); +} + +static int +getfloating(long double *dp, int mod_ldbl) +{ + char *ep; + int rval; + + if (!*gargv) { + *dp = 0.0; + return (0); + } + if (**gargv == '"' || **gargv == '\'') { + *dp = asciicode(); + return (0); + } + rval = 0; + errno = 0; + if (mod_ldbl) + *dp = strtold(*gargv, &ep); + else + *dp = strtod(*gargv, &ep); + if (ep == *gargv) { + warnx2(_("%s: expected numeric value"), *gargv, NULL); + rval = 1; + } else if (*ep != '\0') { + warnx2(_("%s: not completely converted"), *gargv, NULL); + rval = 1; + } + if (errno == ERANGE) { + warnx3("%s: %s", *gargv, strerror(ERANGE)); + rval = 1; + } + ++gargv; + return (rval); +} + +static int +asciicode(void) +{ + int ch; + + ch = **gargv; + if (ch == '\'' || ch == '"') + ch = (*gargv)[1]; + ++gargv; + return (ch); +} + +static void +usage(void) +{ + (void) fprintf(stderr, _("usage: printf format [arguments ...]\n")); +} diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf index 07558dd340..27944b3c75 100644 --- a/usr/src/pkg/manifests/SUNWcs.mf +++ b/usr/src/pkg/manifests/SUNWcs.mf @@ -844,6 +844,7 @@ file path=usr/bin/pgrep mode=0555 file path=usr/bin/pkg2du mode=0555 file path=usr/bin/pktool mode=0555 file path=usr/bin/pr mode=0555 +file path=usr/bin/printf mode=0555 file path=usr/bin/priocntl mode=0555 file path=usr/bin/profiles mode=0555 file path=usr/bin/projects mode=0555 diff --git a/usr/src/pkg/manifests/text-locale.mf b/usr/src/pkg/manifests/text-locale.mf index f44ba81583..b79023e8b0 100644 --- a/usr/src/pkg/manifests/text-locale.mf +++ b/usr/src/pkg/manifests/text-locale.mf @@ -47,7 +47,6 @@ file path=usr/bin/locale mode=0555 file path=usr/bin/localedef mode=0555 file path=usr/bin/mkmsgs group=root mode=0555 file path=usr/bin/msgfmt mode=0555 -file path=usr/bin/printf mode=0555 file path=usr/bin/srchtxt mode=0555 file path=usr/bin/xgettext mode=0555 file path=usr/lib/gmsgfmt mode=0555 |