diff options
Diffstat (limited to 'src/libfmt')
59 files changed, 4880 insertions, 0 deletions
diff --git a/src/libfmt/LICENSE b/src/libfmt/LICENSE new file mode 100644 index 00000000..5dc21cb5 --- /dev/null +++ b/src/libfmt/LICENSE @@ -0,0 +1,19 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 formatted I/O package. + +Please send comments about the packaging +to Russ Cox <rsc@post.harvard.edu>. + diff --git a/src/libfmt/Make.Darwin-PowerMacintosh b/src/libfmt/Make.Darwin-PowerMacintosh new file mode 100644 index 00000000..14b8d4e7 --- /dev/null +++ b/src/libfmt/Make.Darwin-PowerMacintosh @@ -0,0 +1,6 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I${PREFIX}/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O diff --git a/src/libfmt/Make.FreeBSD-386 b/src/libfmt/Make.FreeBSD-386 new file mode 100644 index 00000000..9799dcbb --- /dev/null +++ b/src/libfmt/Make.FreeBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include -pg +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O diff --git a/src/libfmt/Make.HP-UX-9000 b/src/libfmt/Make.HP-UX-9000 new file mode 100644 index 00000000..edbdc111 --- /dev/null +++ b/src/libfmt/Make.HP-UX-9000 @@ -0,0 +1,6 @@ +CC=cc +CFLAGS=-O -c -Ae -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O diff --git a/src/libfmt/Make.Linux-386 b/src/libfmt/Make.Linux-386 new file mode 100644 index 00000000..20432828 --- /dev/null +++ b/src/libfmt/Make.Linux-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -DNEEDLL +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O diff --git a/src/libfmt/Make.NetBSD-386 b/src/libfmt/Make.NetBSD-386 new file mode 100644 index 00000000..087ed3ab --- /dev/null +++ b/src/libfmt/Make.NetBSD-386 @@ -0,0 +1,7 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O # default, can be overriden by Make.$(SYSNAME) +NAN=nan64.$O diff --git a/src/libfmt/Make.OSF1-alpha b/src/libfmt/Make.OSF1-alpha new file mode 100644 index 00000000..3d45279b --- /dev/null +++ b/src/libfmt/Make.OSF1-alpha @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O diff --git a/src/libfmt/Make.SunOS-sun4u b/src/libfmt/Make.SunOS-sun4u new file mode 100644 index 00000000..c5fe67b8 --- /dev/null +++ b/src/libfmt/Make.SunOS-sun4u @@ -0,0 +1,2 @@ +include Make.SunOS-sun4u-$(CC) +NAN=nan64.$O diff --git a/src/libfmt/Make.SunOS-sun4u-cc b/src/libfmt/Make.SunOS-sun4u-cc new file mode 100644 index 00000000..829301de --- /dev/null +++ b/src/libfmt/Make.SunOS-sun4u-cc @@ -0,0 +1,6 @@ +CC=cc +CFLAGS+=-g -c -I. -O +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O diff --git a/src/libfmt/Make.SunOS-sun4u-gcc b/src/libfmt/Make.SunOS-sun4u-gcc new file mode 100644 index 00000000..5c415948 --- /dev/null +++ b/src/libfmt/Make.SunOS-sun4u-gcc @@ -0,0 +1,6 @@ +CC=gcc +CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c +O=o +AR=ar +ARFLAGS=rvc +NAN=nan64.$O diff --git a/src/libfmt/Makefile b/src/libfmt/Makefile new file mode 100644 index 00000000..4b8ff604 --- /dev/null +++ b/src/libfmt/Makefile @@ -0,0 +1,134 @@ + +# this works in gnu make +SYSNAME:=${shell uname} +OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'} + +# this works in bsd make +SYSNAME!=uname +OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g' + +# the gnu rules will mess up bsd but not vice versa, +# hence the gnu rules come first. + +include Make.$(SYSNAME)-$(OBJTYPE) + +PREFIX=/usr/local + +NUKEFILES= + +TGZFILES= + +LIB=libfmt.a +VERSION=2.0 +PORTPLACE=devel/libfmt +NAME=libfmt + +NUM=\ + charstod.$O\ + pow10.$O\ + +OFILES=\ + dofmt.$O\ + errfmt.$O\ + fltfmt.$O\ + fmt.$O\ + fmtfd.$O\ + fmtfdflush.$O\ + fmtlock.$O\ + fmtprint.$O\ + fmtquote.$O\ + fmtrune.$O\ + fmtstr.$O\ + fmtvprint.$O\ + fprint.$O\ + print.$O\ + runefmtstr.$O\ + runeseprint.$O\ + runesmprint.$O\ + runesnprint.$O\ + runesprint.$O\ + runevseprint.$O\ + runevsmprint.$O\ + runevsnprint.$O\ + seprint.$O\ + smprint.$O\ + snprint.$O\ + sprint.$O\ + strtod.$O\ + vfprint.$O\ + vseprint.$O\ + vsmprint.$O\ + vsnprint.$O\ + $(NUM)\ + $(NAN)\ + +HFILES=\ + fmtdef.h\ + fmt.h\ + +all: $(LIB) + +install: $(LIB) + test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3 + install -m 0644 print.3 $(PREFIX)/man/man3/print.3 + install -m 0644 fmtinstall.3 $(PREFIX)/man/man3/fmtinstall.3 + install -m 0644 fmt.h $(PREFIX)/include/fmt.h + install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB) + +$(NAN).$O: nan.h +strtod.$O: nan.h + +test: $(LIB) test.$O + $(CC) -o test test.$O $(LIB) -L$(PREFIX)/lib -lutf + +$(LIB): $(OFILES) + $(AR) $(ARFLAGS) $(LIB) $(OFILES) + +NUKEFILES+=$(LIB) +.c.$O: + $(CC) $(CFLAGS) -I$(PREFIX)/include $*.c + +%.$O: %.c + $(CC) $(CFLAGS) -I$(PREFIX)/include $*.c + + +$(OFILES): $(HFILES) + +tgz: + rm -rf $(NAME)-$(VERSION) + mkdir $(NAME)-$(VERSION) + cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.ports $(TGZFILES) $(NAME)-$(VERSION) + tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz + rm -rf $(NAME)-$(VERSION) + +clean: + rm -f $(OFILES) $(LIB) + +nuke: + rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES) + +rpm: + make tgz + cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES + rpm -ba rpm.spec + cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm . + cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm . + scp *.rpm rsc@amsterdam.lcs.mit.edu:public_html/software + +PORTDIR=/usr/ports/$(PORTPLACE) + +ports: + make tgz + rm -rf $(PORTDIR) + mkdir $(PORTDIR) + cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles + cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { ofile=$$2; next} {if(ofile) print >ofile}') + (cd $(PORTDIR); make makesum) + (cd $(PORTDIR); make) + (cd $(PORTDIR); /usr/local/bin/portlint) + rm -rf $(PORTDIR)/work + shar `find $(PORTDIR)` > ports.shar + (cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz + scp *.tgz rsc@amsterdam.lcs.mit.edu:public_html/software + +.phony: all clean nuke install tgz rpm ports diff --git a/src/libfmt/Makefile.MID b/src/libfmt/Makefile.MID new file mode 100644 index 00000000..8302c281 --- /dev/null +++ b/src/libfmt/Makefile.MID @@ -0,0 +1,63 @@ +LIB=libfmt.a +VERSION=2.0 +PORTPLACE=devel/libfmt +NAME=libfmt + +NUM=\ + charstod.$O\ + pow10.$O\ + +OFILES=\ + dofmt.$O\ + errfmt.$O\ + fltfmt.$O\ + fmt.$O\ + fmtfd.$O\ + fmtfdflush.$O\ + fmtlock.$O\ + fmtprint.$O\ + fmtquote.$O\ + fmtrune.$O\ + fmtstr.$O\ + fmtvprint.$O\ + fprint.$O\ + print.$O\ + runefmtstr.$O\ + runeseprint.$O\ + runesmprint.$O\ + runesnprint.$O\ + runesprint.$O\ + runevseprint.$O\ + runevsmprint.$O\ + runevsnprint.$O\ + seprint.$O\ + smprint.$O\ + snprint.$O\ + sprint.$O\ + strtod.$O\ + vfprint.$O\ + vseprint.$O\ + vsmprint.$O\ + vsnprint.$O\ + $(NUM)\ + $(NAN)\ + +HFILES=\ + fmtdef.h\ + fmt.h\ + +all: $(LIB) + +install: $(LIB) + test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3 + install -m 0644 print.3 $(PREFIX)/man/man3/print.3 + install -m 0644 fmtinstall.3 $(PREFIX)/man/man3/fmtinstall.3 + install -m 0644 fmt.h $(PREFIX)/include/fmt.h + install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB) + +$(NAN).$O: nan.h +strtod.$O: nan.h + +test: $(LIB) test.$O + $(CC) -o test test.$O $(LIB) -L$(PREFIX)/lib -lutf + diff --git a/src/libfmt/NOTICE b/src/libfmt/NOTICE new file mode 100644 index 00000000..5dc21cb5 --- /dev/null +++ b/src/libfmt/NOTICE @@ -0,0 +1,19 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 formatted I/O package. + +Please send comments about the packaging +to Russ Cox <rsc@post.harvard.edu>. + diff --git a/src/libfmt/README b/src/libfmt/README new file mode 100644 index 00000000..5dc21cb5 --- /dev/null +++ b/src/libfmt/README @@ -0,0 +1,19 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +This is a Unix port of the Plan 9 formatted I/O package. + +Please send comments about the packaging +to Russ Cox <rsc@post.harvard.edu>. + diff --git a/src/libfmt/bundle.ports b/src/libfmt/bundle.ports new file mode 100644 index 00000000..9ecf6a24 --- /dev/null +++ b/src/libfmt/bundle.ports @@ -0,0 +1,51 @@ +--- Makefile --- +# New ports collection makefile for: libfmt +# Date Created: 11 Feb 2003 +# Whom: rsc +# +# THIS LINE NEEDS REPLACING. IT'S HERE TO GET BY PORTLINT +# $FreeBSD: ports/devel/libfmt/Makefile,v 1.1 2003/02/12 00:51:22 rsc Exp $ + +PORTNAME= libfmt +PORTVERSION= 2.0 +CATEGORIES= devel +MASTER_SITES= http://pdos.lcs.mit.edu/~rsc/software/ +EXTRACT_SUFX= .tgz + +MAINTAINER= rsc@post.harvard.edu + +DEPENDS= ${PORTSDIR}/devel/libutf + +MAN3= print.3 fmtinstall.3 + +USE_REINPLACE=yes + +.include <bsd.port.pre.mk> + +post-patch: + ${REINPLACE_CMD} -e 's,$$(PREFIX),${PREFIX},g' ${WRKSRC}/Makefile + +.include <bsd.port.post.mk> +--- pkg-comment --- +Extensible formatted print C library (printf with user-defined verbs) +--- pkg-descr --- +Libfmt is a port of Plan 9's formatted print library. +As a base it provides all the syntax of ANSI printf +but adds the ability for client programs to install +new print verbs. One such print verb (installed by +default) is %r, which prints the system error string. +Instead of perror("foo"), you can write fprint(2, "foo: %r\n"). +This is especially nice when you write verbs to format +the data structures used by your particular program. + +WWW: http://pdos.lcs.mit.edu/~rsc/software/#libfmt +http://plan9.bell-labs.com/magic/man2html/2/print + +Russ Cox +rsc@post.harvard.edu +--- pkg-plist --- +lib/libfmt.a +include/fmt.h +--- /dev/null --- +This is just a way to make sure blank lines don't +creep into pkg-plist. diff --git a/src/libfmt/charstod.c b/src/libfmt/charstod.c new file mode 100644 index 00000000..ec403b11 --- /dev/null +++ b/src/libfmt/charstod.c @@ -0,0 +1,85 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * Reads a floating-point number by interpreting successive characters + * returned by (*f)(vp). The last call it makes to f terminates the + * scan, so is not a character in the number. It may therefore be + * necessary to back up the input stream up one byte after calling charstod. + */ + +double +fmtcharstod(int(*f)(void*), void *vp) +{ + double num, dem; + int neg, eneg, dig, exp, c; + + num = 0; + neg = 0; + dig = 0; + exp = 0; + eneg = 0; + + c = (*f)(vp); + while(c == ' ' || c == '\t') + c = (*f)(vp); + if(c == '-' || c == '+'){ + if(c == '-') + neg = 1; + c = (*f)(vp); + } + while(c >= '0' && c <= '9'){ + num = num*10 + c-'0'; + c = (*f)(vp); + } + if(c == '.') + c = (*f)(vp); + while(c >= '0' && c <= '9'){ + num = num*10 + c-'0'; + dig++; + c = (*f)(vp); + } + if(c == 'e' || c == 'E'){ + c = (*f)(vp); + if(c == '-' || c == '+'){ + if(c == '-'){ + dig = -dig; + eneg = 1; + } + c = (*f)(vp); + } + while(c >= '0' && c <= '9'){ + exp = exp*10 + c-'0'; + c = (*f)(vp); + } + } + exp -= dig; + if(exp < 0){ + exp = -exp; + eneg = !eneg; + } + dem = __fmtpow10(exp); + if(eneg) + num /= dem; + else + num *= dem; + if(neg) + return -num; + return num; +} diff --git a/src/libfmt/dofmt.c b/src/libfmt/dofmt.c new file mode 100644 index 00000000..d26f7158 --- /dev/null +++ b/src/libfmt/dofmt.c @@ -0,0 +1,558 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* format the output into f->to and return the number of characters fmted */ +int +dofmt(Fmt *f, char *fmt) +{ + Rune rune, *rt, *rs; + int r; + char *t, *s; + int n, nfmt; + + nfmt = f->nfmt; + for(;;){ + if(f->runes){ + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + while((r = *(uchar*)fmt) && r != '%'){ + if(r < Runeself) + fmt++; + else{ + fmt += chartorune(&rune, fmt); + r = rune; + } + FMTRCHAR(f, rt, rs, r); + } + fmt++; + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(!r) + return f->nfmt - nfmt; + f->stop = rs; + }else{ + t = (char*)f->to; + s = (char*)f->stop; + while((r = *(uchar*)fmt) && r != '%'){ + if(r < Runeself){ + FMTCHAR(f, t, s, r); + fmt++; + }else{ + n = chartorune(&rune, fmt); + if(t + n > s){ + t = (char*)__fmtflush(f, t, n); + if(t != nil) + s = (char*)f->stop; + else + return -1; + } + while(n--) + *t++ = *fmt++; + } + } + fmt++; + f->nfmt += t - (char *)f->to; + f->to = t; + if(!r) + return f->nfmt - nfmt; + f->stop = s; + } + + fmt = (char*)__fmtdispatch(f, fmt, 0); + if(fmt == nil) + return -1; + } + return 0; /* not reached */ +} + +void * +__fmtflush(Fmt *f, void *t, int len) +{ + if(f->runes) + f->nfmt += (Rune*)t - (Rune*)f->to; + else + f->nfmt += (char*)t - (char *)f->to; + f->to = t; + if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){ + f->stop = f->to; + return nil; + } + return f->to; +} + +/* + * put a formatted block of memory sz bytes long of n runes into the output buffer, + * left/right justified in a field of at least f->width charactes + */ +int +__fmtpad(Fmt *f, int n) +{ + char *t, *s; + int i; + + t = (char*)f->to; + s = (char*)f->stop; + for(i = 0; i < n; i++) + FMTCHAR(f, t, s, ' '); + f->nfmt += t - (char *)f->to; + f->to = t; + return 0; +} + +int +__rfmtpad(Fmt *f, int n) +{ + Rune *t, *s; + int i; + + t = (Rune*)f->to; + s = (Rune*)f->stop; + for(i = 0; i < n; i++) + FMTRCHAR(f, t, s, ' '); + f->nfmt += t - (Rune *)f->to; + f->to = t; + return 0; +} + +int +__fmtcpy(Fmt *f, const void *vm, int n, int sz) +{ + Rune *rt, *rs, r; + char *t, *s, *m, *me; + ulong fl; + int nc, w; + + m = (char*)vm; + me = m + sz; + w = f->width; + fl = f->flags; + if((fl & FmtPrec) && n > f->prec) + n = f->prec; + if(f->runes){ + if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) + return -1; + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + for(nc = n; nc > 0; nc--){ + r = *(uchar*)m; + if(r < Runeself) + m++; + else if((me - m) >= UTFmax || fullrune(m, me-m)) + m += chartorune(&r, m); + else + break; + FMTRCHAR(f, rt, rs, r); + } + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(m < me) + return -1; + if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) + return -1; + t = (char*)f->to; + s = (char*)f->stop; + for(nc = n; nc > 0; nc--){ + r = *(uchar*)m; + if(r < Runeself) + m++; + else if((me - m) >= UTFmax || fullrune(m, me-m)) + m += chartorune(&r, m); + else + break; + FMTRUNE(f, t, s, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - n) < 0) + return -1; + } + return 0; +} + +int +__fmtrcpy(Fmt *f, const void *vm, int n) +{ + Rune r, *m, *me, *rt, *rs; + char *t, *s; + ulong fl; + int w; + + m = (Rune*)vm; + w = f->width; + fl = f->flags; + if((fl & FmtPrec) && n > f->prec) + n = f->prec; + if(f->runes){ + if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) + return -1; + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + for(me = m + n; m < me; m++) + FMTRCHAR(f, rt, rs, *m); + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) + return -1; + t = (char*)f->to; + s = (char*)f->stop; + for(me = m + n; m < me; m++){ + r = *m; + FMTRUNE(f, t, s, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - n) < 0) + return -1; + } + return 0; +} + +/* fmt out one character */ +int +__charfmt(Fmt *f) +{ + char x[1]; + + x[0] = va_arg(f->args, int); + f->prec = 1; + return __fmtcpy(f, (const char*)x, 1, 1); +} + +/* fmt out one rune */ +int +__runefmt(Fmt *f) +{ + Rune x[1]; + + x[0] = va_arg(f->args, int); + return __fmtrcpy(f, (const void*)x, 1); +} + +/* public helper routine: fmt out a null terminated string already in hand */ +int +fmtstrcpy(Fmt *f, char *s) +{ + int p, i; + if(!s) + return __fmtcpy(f, "<nil>", 5, 5); + /* if precision is specified, make sure we don't wander off the end */ + if(f->flags & FmtPrec){ + p = f->prec; + for(i = 0; i < p; i++) + if(s[i] == 0) + break; + return __fmtcpy(f, s, utfnlen(s, i), i); /* BUG?: won't print a partial rune at end */ + } + + return __fmtcpy(f, s, utflen(s), strlen(s)); +} + +/* fmt out a null terminated utf string */ +int +__strfmt(Fmt *f) +{ + char *s; + + s = va_arg(f->args, char *); + return fmtstrcpy(f, s); +} + +/* public helper routine: fmt out a null terminated rune string already in hand */ +int +fmtrunestrcpy(Fmt *f, Rune *s) +{ + Rune *e; + int n, p; + + if(!s) + return __fmtcpy(f, "<nil>", 5, 5); + /* if precision is specified, make sure we don't wander off the end */ + if(f->flags & FmtPrec){ + p = f->prec; + for(n = 0; n < p; n++) + if(s[n] == 0) + break; + }else{ + for(e = s; *e; e++) + ; + n = e - s; + } + return __fmtrcpy(f, s, n); +} + +/* fmt out a null terminated rune string */ +int +__runesfmt(Fmt *f) +{ + Rune *s; + + s = va_arg(f->args, Rune *); + return fmtrunestrcpy(f, s); +} + +/* fmt a % */ +int +__percentfmt(Fmt *f) +{ + Rune x[1]; + + x[0] = f->r; + f->prec = 1; + return __fmtrcpy(f, (const void*)x, 1); +} + +/* fmt an integer */ +int +__ifmt(Fmt *f) +{ + char buf[70], *p, *conv; + uvlong vu; + ulong u; + int neg, base, i, n, fl, w, isv; + + neg = 0; + fl = f->flags; + isv = 0; + vu = 0; + u = 0; + /* + * Unsigned verbs + */ + switch(f->r){ + case 'o': + case 'u': + case 'x': + case 'X': + fl |= FmtUnsigned; + break; + } + if(f->r == 'p'){ + u = (ulong)va_arg(f->args, void*); + f->r = 'x'; + fl |= FmtUnsigned; + }else if(fl & FmtVLong){ + isv = 1; + if(fl & FmtUnsigned) + vu = va_arg(f->args, uvlong); + else + vu = va_arg(f->args, vlong); + }else if(fl & FmtLong){ + if(fl & FmtUnsigned) + u = va_arg(f->args, ulong); + else + u = va_arg(f->args, long); + }else if(fl & FmtByte){ + if(fl & FmtUnsigned) + u = (uchar)va_arg(f->args, int); + else + u = (char)va_arg(f->args, int); + }else if(fl & FmtShort){ + if(fl & FmtUnsigned) + u = (ushort)va_arg(f->args, int); + else + u = (short)va_arg(f->args, int); + }else{ + if(fl & FmtUnsigned) + u = va_arg(f->args, uint); + else + u = va_arg(f->args, int); + } + conv = "0123456789abcdef"; + switch(f->r){ + case 'd': + case 'i': + base = 10; + break; + case 'u': + base = 10; + break; + case 'x': + base = 16; + break; + case 'X': + base = 16; + conv = "0123456789ABCDEF"; + break; + case 'b': + base = 2; + break; + case 'o': + base = 8; + break; + default: + return -1; + } + if(!(fl & FmtUnsigned)){ + if(isv && (vlong)vu < 0){ + vu = -(vlong)vu; + neg = 1; + }else if(!isv && (long)u < 0){ + u = -(long)u; + neg = 1; + } + }else{ + fl &= ~(FmtSign|FmtSpace); /* no + for unsigned conversions */ + } + p = buf + sizeof buf - 1; + n = 0; + if(isv){ + while(vu){ + i = vu % base; + vu /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + *p-- = conv[i]; + n++; + } + }else{ + while(u){ + i = u % base; + u /= base; + if((fl & FmtComma) && n % 4 == 3){ + *p-- = ','; + n++; + } + *p-- = conv[i]; + n++; + } + } + if(n == 0){ + if(!(fl & FmtPrec) || f->prec != 0){ + *p-- = '0'; + n = 1; + } + fl &= ~FmtSharp; + } + for(w = f->prec; n < w && p > buf+3; n++) + *p-- = '0'; + if(neg || (fl & (FmtSign|FmtSpace))) + n++; + if(fl & FmtSharp){ + if(base == 16) + n += 2; + else if(base == 8){ + if(p[1] == '0') + fl &= ~FmtSharp; + else + n++; + } + } + if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){ + for(w = f->width; n < w && p > buf+3; n++) + *p-- = '0'; + f->width = 0; + } + if(fl & FmtSharp){ + if(base == 16) + *p-- = f->r; + if(base == 16 || base == 8) + *p-- = '0'; + } + if(neg) + *p-- = '-'; + else if(fl & FmtSign) + *p-- = '+'; + else if(fl & FmtSpace) + *p-- = ' '; + f->flags &= ~FmtPrec; + return __fmtcpy(f, p + 1, n, n); +} + +int +__countfmt(Fmt *f) +{ + void *p; + ulong fl; + + fl = f->flags; + p = va_arg(f->args, void*); + if(fl & FmtVLong){ + *(vlong*)p = f->nfmt; + }else if(fl & FmtLong){ + *(long*)p = f->nfmt; + }else if(fl & FmtByte){ + *(char*)p = f->nfmt; + }else if(fl & FmtShort){ + *(short*)p = f->nfmt; + }else{ + *(int*)p = f->nfmt; + } + return 0; +} + +int +__flagfmt(Fmt *f) +{ + switch(f->r){ + case ',': + f->flags |= FmtComma; + break; + case '-': + f->flags |= FmtLeft; + break; + case '+': + f->flags |= FmtSign; + break; + case '#': + f->flags |= FmtSharp; + break; + case ' ': + f->flags |= FmtSpace; + break; + case 'u': + f->flags |= FmtUnsigned; + break; + case 'h': + if(f->flags & FmtShort) + f->flags |= FmtByte; + f->flags |= FmtShort; + break; + case 'L': + f->flags |= FmtLDouble; + break; + case 'l': + if(f->flags & FmtLong) + f->flags |= FmtVLong; + f->flags |= FmtLong; + break; + } + return 1; +} + +/* default error format */ +int +__badfmt(Fmt *f) +{ + char x[3]; + + x[0] = '%'; + x[1] = f->r; + x[2] = '%'; + f->prec = 3; + __fmtcpy(f, (const void*)x, 3, 3); + return 0; +} diff --git a/src/libfmt/dorfmt.c b/src/libfmt/dorfmt.c new file mode 100644 index 00000000..cdaee8a5 --- /dev/null +++ b/src/libfmt/dorfmt.c @@ -0,0 +1,61 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* format the output into f->to and return the number of characters fmted */ + +int +dorfmt(Fmt *f, const Rune *fmt) +{ + Rune *rt, *rs; + int r; + char *t, *s; + int nfmt; + + nfmt = f->nfmt; + for(;;){ + if(f->runes){ + rt = f->to; + rs = f->stop; + while((r = *fmt++) && r != '%'){ + FMTRCHAR(f, rt, rs, r); + } + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(!r) + return f->nfmt - nfmt; + f->stop = rs; + }else{ + t = f->to; + s = f->stop; + while((r = *fmt++) && r != '%'){ + FMTRUNE(f, t, f->stop, r); + } + f->nfmt += t - (char *)f->to; + f->to = t; + if(!r) + return f->nfmt - nfmt; + f->stop = s; + } + + fmt = __fmtdispatch(f, fmt, 1); + if(fmt == nil) + return -1; + } + return 0; /* not reached */ +} diff --git a/src/libfmt/errfmt.c b/src/libfmt/errfmt.c new file mode 100644 index 00000000..21847054 --- /dev/null +++ b/src/libfmt/errfmt.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +int +__errfmt(Fmt *f) +{ + char *s; + + s = strerror(errno); + return fmtstrcpy(f, s); +} diff --git a/src/libfmt/fltfmt.c b/src/libfmt/fltfmt.c new file mode 100644 index 00000000..234f03d9 --- /dev/null +++ b/src/libfmt/fltfmt.c @@ -0,0 +1,610 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdio.h> +#include <math.h> +#include <float.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include "fmt.h" +#include "fmtdef.h" +#include "nan.h" + +enum +{ + FDEFLT = 6, + NSIGNIF = 17 +}; + +/* + * first few powers of 10, enough for about 1/2 of the + * total space for doubles. + */ +static double pows10[] = +{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, + 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, + 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, + 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, + 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, + 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, + 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, + 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, +}; + +static double +pow10(int n) +{ + double d; + int neg; + + neg = 0; + if(n < 0){ + if(n < DBL_MIN_10_EXP){ + return 0.; + } + neg = 1; + n = -n; + }else if(n > DBL_MAX_10_EXP){ + return HUGE_VAL; + } + if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))) + d = pows10[n]; + else{ + d = pows10[sizeof(pows10)/sizeof(pows10[0]) - 1]; + for(;;){ + n -= sizeof(pows10)/sizeof(pows10[0]) - 1; + if(n < (int)(sizeof(pows10)/sizeof(pows10[0]))){ + d *= pows10[n]; + break; + } + d *= pows10[sizeof(pows10)/sizeof(pows10[0]) - 1]; + } + } + if(neg){ + return 1./d; + } + return d; +} + +static int +xadd(char *a, int n, int v) +{ + char *b; + int c; + + if(n < 0 || n >= NSIGNIF) + return 0; + for(b = a+n; b >= a; b--) { + c = *b + v; + if(c <= '9') { + *b = c; + return 0; + } + *b = '0'; + v = 1; + } + *a = '1'; /* overflow adding */ + return 1; +} + +static int +xsub(char *a, int n, int v) +{ + char *b; + int c; + + for(b = a+n; b >= a; b--) { + c = *b - v; + if(c >= '0') { + *b = c; + return 0; + } + *b = '9'; + v = 1; + } + *a = '9'; /* underflow subtracting */ + return 1; +} + +static void +xaddexp(char *p, int e) +{ + char se[9]; + int i; + + *p++ = 'e'; + if(e < 0) { + *p++ = '-'; + e = -e; + } + i = 0; + while(e) { + se[i++] = e % 10 + '0'; + e /= 10; + } + if(i == 0) { + *p++ = '0'; + } else { + while(i > 0) + *p++ = se[--i]; + } + *p++ = '\0'; +} + +static char* +xdodtoa(char *s1, double f, int chr, int prec, int *decpt, int *rsign) +{ + char s2[NSIGNIF+10]; + double g, h; + int e, d, i; + int c2, sign, oerr; + + if(chr == 'F') + chr = 'f'; + if(prec > NSIGNIF) + prec = NSIGNIF; + if(prec < 0) + prec = 0; + if(__isNaN(f)) { + *decpt = 9999; + *rsign = 0; + strcpy(s1, "nan"); + return &s1[3]; + } + sign = 0; + if(f < 0) { + f = -f; + sign++; + } + *rsign = sign; + if(__isInf(f, 1) || __isInf(f, -1)) { + *decpt = 9999; + strcpy(s1, "inf"); + return &s1[3]; + } + + e = 0; + g = f; + if(g != 0) { + frexp(f, &e); + e = (int)(e * .301029995664); + if(e >= -150 && e <= +150) { + d = 0; + h = f; + } else { + d = e/2; + h = f * pow10(-d); + } + g = h * pow10(d-e); + while(g < 1) { + e--; + g = h * pow10(d-e); + } + while(g >= 10) { + e++; + g = h * pow10(d-e); + } + } + + /* + * convert NSIGNIF digits and convert + * back to get accuracy. + */ + for(i=0; i<NSIGNIF; i++) { + d = (int)g; + s1[i] = d + '0'; + g = (g - d) * 10; + } + s1[i] = 0; + + /* + * try decimal rounding to eliminate 9s + */ + c2 = prec + 1; + if(chr == 'f') + c2 += e; + oerr = errno; + if(c2 >= NSIGNIF-2) { + strcpy(s2, s1); + d = e; + s1[NSIGNIF-2] = '0'; + s1[NSIGNIF-1] = '0'; + xaddexp(s1+NSIGNIF, e-NSIGNIF+1); + g = fmtstrtod(s1, nil); + if(g == f) + goto found; + if(xadd(s1, NSIGNIF-3, 1)) { + e++; + xaddexp(s1+NSIGNIF, e-NSIGNIF+1); + } + g = fmtstrtod(s1, nil); + if(g == f) + goto found; + strcpy(s1, s2); + e = d; + } + + /* + * convert back so s1 gets exact answer + */ + for(d = 0; d < 10; d++) { + xaddexp(s1+NSIGNIF, e-NSIGNIF+1); + g = fmtstrtod(s1, nil); + if(f > g) { + if(xadd(s1, NSIGNIF-1, 1)) + e--; + continue; + } + if(f < g) { + if(xsub(s1, NSIGNIF-1, 1)) + e++; + continue; + } + break; + } + +found: + errno = oerr; + + /* + * sign + */ + d = 0; + i = 0; + + /* + * round & adjust 'f' digits + */ + c2 = prec + 1; + if(chr == 'f'){ + if(xadd(s1, c2+e, 5)) + e++; + c2 += e; + if(c2 < 0){ + c2 = 0; + e = -prec - 1; + } + }else{ + if(xadd(s1, c2, 5)) + e++; + } + if(c2 > NSIGNIF){ + c2 = NSIGNIF; + } + + *decpt = e + 1; + + /* + * terminate the converted digits + */ + s1[c2] = '\0'; + return &s1[c2]; +} + +/* + * this function works like the standard dtoa, if you want it. + */ +#if 0 +static char* +__dtoa(double f, int mode, int ndigits, int *decpt, int *rsign, char **rve) +{ + static char s2[NSIGNIF + 10]; + char *es; + int chr, prec; + + switch(mode) { + /* like 'e' */ + case 2: + case 4: + case 6: + case 8: + chr = 'e'; + break; + /* like 'g' */ + case 0: + case 1: + default: + chr = 'g'; + break; + /* like 'f' */ + case 3: + case 5: + case 7: + case 9: + chr = 'f'; + break; + } + + if(chr != 'f' && ndigits){ + ndigits--; + } + prec = ndigits; + if(prec > NSIGNIF) + prec = NSIGNIF; + if(ndigits == 0) + prec = NSIGNIF; + es = xdodtoa(s2, f, chr, prec, decpt, rsign); + + /* + * strip trailing 0 + */ + for(; es > s2 + 1; es--){ + if(es[-1] != '0'){ + break; + } + } + *es = '\0'; + if(rve != NULL) + *rve = es; + return s2; +} +#endif + +static int +fmtzdotpad(Fmt *f, int n, int pt) +{ + char *t, *s; + int i; + Rune *rt, *rs; + + if(f->runes){ + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + for(i = 0; i < n; i++){ + if(i == pt){ + FMTRCHAR(f, rt, rs, '.'); + } + FMTRCHAR(f, rt, rs, '0'); + } + f->nfmt += rt - (Rune*)f->to; + f->to = rt; + }else{ + t = (char*)f->to; + s = (char*)f->stop; + for(i = 0; i < n; i++){ + if(i == pt){ + FMTCHAR(f, t, s, '.'); + } + FMTCHAR(f, t, s, '0'); + } + f->nfmt += t - (char *)f->to; + f->to = t; + } + return 0; +} + +int +__efgfmt(Fmt *fmt) +{ + double f; + char s1[NSIGNIF+10]; + int e, d, n; + int c1, c2, c3, c4, ucase, sign, chr, prec, fl; + + f = va_arg(fmt->args, double); + prec = FDEFLT; + fl = fmt->flags; + fmt->flags = 0; + if(fl & FmtPrec) + prec = fmt->prec; + chr = fmt->r; + ucase = 0; + if(chr == 'E'){ + chr = 'e'; + ucase = 1; + }else if(chr == 'F'){ + chr = 'f'; + ucase = 1; + }else if(chr == 'G'){ + chr = 'g'; + ucase = 1; + } + if(prec > 0 && chr == 'g') + prec--; + if(prec < 0) + prec = 0; + + xdodtoa(s1, f, chr, prec, &e, &sign); + e--; + if(*s1 == 'i' || *s1 == 'n'){ + if(ucase){ + if(*s1 == 'i'){ + strcpy(s1, "INF"); + }else{ + strcpy(s1, "NAN"); + } + } + fmt->flags = fl & (FmtWidth|FmtLeft); + return __fmtcpy(fmt, (const void*)s1, 3, 3); + } + + /* + * copy into final place + * c1 digits of leading '0' + * c2 digits from conversion + * c3 digits of trailing '0' + * c4 digits after '.' + */ + c1 = 0; + c2 = prec + 1; + c3 = 0; + c4 = prec; + switch(chr) { + default: + chr = 'e'; + break; + case 'g': + /* + * decide on 'e' of 'f' style convers + */ + if(e >= -4 && e <= prec) { + c1 = -e; + c4 = prec - e; + chr = 'h'; /* flag for 'f' style */ + } + break; + case 'f': + c1 = -e; + if(c1 > prec) + c1 = prec + 1; + c2 += e; + break; + } + + /* + * clean up c1 c2 and c3 + */ + if(c1 < 0) + c1 = 0; + if(c2 < 0) + c2 = 0; + if(c2 > NSIGNIF) { + c3 = c2-NSIGNIF; + c2 = NSIGNIF; + } + + /* + * trim trailing zeros for %g + */ + if(!(fl & FmtSharp) + && (chr == 'g' || chr == 'h')){ + if(c4 >= c3){ + c4 -= c3; + c3 = 0; + }else{ + c3 -= c4; + c4 = 0; + } + while(c4 && c2 > 1 && s1[c2 - 1] == '0'){ + c4--; + c2--; + } + } + + /* + * calculate the total length + */ + n = c1 + c2 + c3; + if(sign || (fl & (FmtSign|FmtSpace))) + n++; + if(c4 || (fl & FmtSharp)){ + n++; + } + if(chr == 'e' || chr == 'g'){ + n += 4; + if(e >= 100) + n++; + } + + /* + * pad to width if right justified + */ + if((fl & (FmtWidth|FmtLeft)) == FmtWidth && n < fmt->width){ + if(fl & FmtZero){ + c1 += fmt->width - n; + }else{ + if(__fmtpad(fmt, fmt->width - n) < 0){ + return -1; + } + } + } + + /* + * sign + */ + d = 0; + if(sign) + d = '-'; + else if(fl & FmtSign) + d = '+'; + else if(fl & FmtSpace) + d = ' '; + if(d && fmtrune(fmt, d) < 0){ + return -1; + } + + /* + * copy digits + */ + c4 = c1 + c2 + c3 - c4; + if(c1 > 0){ + if(fmtzdotpad(fmt, c1, c4) < 0){ + return -1; + } + c4 -= c1; + } + d = 0; + if(c4 >= 0 && c4 < c2){ + if(__fmtcpy(fmt, s1, c4, c4) < 0 || fmtrune(fmt, '.') < 0) + return -1; + d = c4; + c2 -= c4; + c4 = -1; + } + if(__fmtcpy(fmt, (const void*)(s1 + d), c2, c2) < 0){ + return -1; + } + c4 -= c2; + if(c3 > 0){ + if(fmtzdotpad(fmt, c3, c4) < 0){ + return -1; + } + c4 -= c3; + } + + /* + * strip trailing '0' on g conv + */ + if((fl & FmtSharp) && c4 == 0 && fmtrune(fmt, '.') < 0){ + return -1; + } + if(chr == 'e' || chr == 'g') { + d = 0; + if(ucase) + s1[d++] = 'E'; + else + s1[d++] = 'e'; + c1 = e; + if(c1 < 0) { + s1[d++] = '-'; + c1 = -c1; + } else + s1[d++] = '+'; + if(c1 >= 100) { + s1[d++] = c1/100 + '0'; + c1 = c1%100; + } + s1[d++] = c1/10 + '0'; + s1[d++] = c1%10 + '0'; + if(__fmtcpy(fmt, s1, d, d) < 0){ + return -1; + } + } + if((fl & (FmtWidth|FmtLeft)) == (FmtWidth|FmtLeft) && n < fmt->width){ + if(__fmtpad(fmt, fmt->width - n) < 0){ + return -1; + } + } + return 0; +} diff --git a/src/libfmt/fmt.c b/src/libfmt/fmt.c new file mode 100644 index 00000000..06f6c950 --- /dev/null +++ b/src/libfmt/fmt.c @@ -0,0 +1,221 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +enum +{ + Maxfmt = 64 +}; + +typedef struct Convfmt Convfmt; +struct Convfmt +{ + int c; + volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ +}; + +struct +{ + /* lock by calling __fmtlock, __fmtunlock */ + int nfmt; + Convfmt fmt[Maxfmt]; +} fmtalloc; + +static Convfmt knownfmt[] = { + ' ', __flagfmt, + '#', __flagfmt, + '%', __percentfmt, + '+', __flagfmt, + ',', __flagfmt, + '-', __flagfmt, + 'C', __runefmt, /* Plan 9 addition */ + 'E', __efgfmt, + 'F', __efgfmt, /* ANSI only */ + 'G', __efgfmt, + 'L', __flagfmt, /* ANSI only */ + 'S', __runesfmt, /* Plan 9 addition */ + 'X', __ifmt, + 'b', __ifmt, /* Plan 9 addition */ + 'c', __charfmt, + 'd', __ifmt, + 'e', __efgfmt, + 'f', __efgfmt, + 'g', __efgfmt, + 'h', __flagfmt, + 'i', __ifmt, /* ANSI only */ + 'l', __flagfmt, + 'n', __countfmt, + 'o', __ifmt, + 'p', __ifmt, + 'r', __errfmt, + 's', __strfmt, + 'u', __ifmt, /* in Plan 9, __flagfmt */ + 'x', __ifmt, + 0, nil, +}; + + +int (*fmtdoquote)(int); + +/* + * __fmtlock() must be set + */ +static int +__fmtinstall(int c, Fmts f) +{ + Convfmt *p, *ep; + + if(c<=0 || c>=65536) + return -1; + if(!f) + f = __badfmt; + + ep = &fmtalloc.fmt[fmtalloc.nfmt]; + for(p=fmtalloc.fmt; p<ep; p++) + if(p->c == c) + break; + + if(p == &fmtalloc.fmt[Maxfmt]) + return -1; + + p->fmt = f; + if(p == ep){ /* installing a new format character */ + fmtalloc.nfmt++; + p->c = c; + } + + return 0; +} + +int +fmtinstall(int c, Fmts f) +{ + int ret; + + __fmtlock(); + ret = __fmtinstall(c, f); + __fmtunlock(); + return ret; +} + +static Fmts +fmtfmt(int c) +{ + Convfmt *p, *ep; + + ep = &fmtalloc.fmt[fmtalloc.nfmt]; + for(p=fmtalloc.fmt; p<ep; p++) + if(p->c == c){ + while(p->fmt == nil) /* loop until value is updated */ + ; + return p->fmt; + } + + /* is this a predefined format char? */ + __fmtlock(); + for(p=knownfmt; p->c; p++) + if(p->c == c){ + __fmtinstall(p->c, p->fmt); + __fmtunlock(); + return p->fmt; + } + __fmtunlock(); + + return __badfmt; +} + +void* +__fmtdispatch(Fmt *f, void *fmt, int isrunes) +{ + Rune rune, r; + int i, n; + + f->flags = 0; + f->width = f->prec = 0; + + for(;;){ + if(isrunes){ + r = *(Rune*)fmt; + fmt = (Rune*)fmt + 1; + }else{ + fmt = (char*)fmt + chartorune(&rune, (char*)fmt); + r = rune; + } + f->r = r; + switch(r){ + case '\0': + return nil; + case '.': + f->flags |= FmtWidth|FmtPrec; + continue; + case '0': + if(!(f->flags & FmtWidth)){ + f->flags |= FmtZero; + continue; + } + /* fall through */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = 0; + while(r >= '0' && r <= '9'){ + i = i * 10 + r - '0'; + if(isrunes){ + r = *(Rune*)fmt; + fmt = (Rune*)fmt + 1; + }else{ + r = *(char*)fmt; + fmt = (char*)fmt + 1; + } + } + if(isrunes) + fmt = (Rune*)fmt - 1; + else + fmt = (char*)fmt - 1; + numflag: + if(f->flags & FmtWidth){ + f->flags |= FmtPrec; + f->prec = i; + }else{ + f->flags |= FmtWidth; + f->width = i; + } + continue; + case '*': + i = va_arg(f->args, int); + if(i < 0){ + /* + * negative precision => + * ignore the precision. + */ + if(f->flags & FmtPrec){ + f->flags &= ~FmtPrec; + f->prec = 0; + continue; + } + i = -i; + f->flags |= FmtLeft; + } + goto numflag; + } + n = (*fmtfmt(r))(f); + if(n < 0) + return nil; + if(n == 0) + return fmt; + } +} diff --git a/src/libfmt/fmt.h b/src/libfmt/fmt.h new file mode 100644 index 00000000..c913e14a --- /dev/null +++ b/src/libfmt/fmt.h @@ -0,0 +1,100 @@ + +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +#ifndef _FMTH_ +#define _FMTH_ 1 + +#include <stdarg.h> + +#ifndef _UTFH_ +#include <utf.h> +#endif + +typedef struct Fmt Fmt; +struct Fmt{ + unsigned char runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + unsigned long flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + FmtLDouble = FmtByte << 1, + + FmtFlag = FmtLDouble << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); + +extern int quotestrfmt(Fmt *f); +extern void quotefmtinstall(void); +extern int (*fmtdoquote)(int); + + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); + +extern double fmtstrtod(const char *, char **); +extern double fmtcharstod(int(*)(void*), void*); + +#endif diff --git a/src/libfmt/fmtdef.h b/src/libfmt/fmtdef.h new file mode 100644 index 00000000..ca2010ab --- /dev/null +++ b/src/libfmt/fmtdef.h @@ -0,0 +1,121 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +/* + * dofmt -- format to a buffer + * the number of characters formatted is returned, + * or -1 if there was an error. + * if the buffer is ever filled, flush is called. + * it should reset the buffer and return whether formatting should continue. + */ +#define uchar _fmtuchar +#define ushort _fmtushort +#define uint _fmtuint +#define ulong _fmtulong +#define vlong _fmtvlong +#define uvlong _fmtuvlong + +#define USED(x) if(x);else + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifndef NOVLONGS +typedef unsigned long long uvlong; +typedef long long vlong; +#endif + +#define nil 0 /* cannot be ((void*)0) because used for function pointers */ + +typedef int (*Fmts)(Fmt*); + +typedef struct Quoteinfo Quoteinfo; +struct Quoteinfo +{ + int quoted; /* if set, string must be quoted */ + int nrunesin; /* number of input runes that can be accepted */ + int nbytesin; /* number of input bytes that can be accepted */ + int nrunesout; /* number of runes that will be generated */ + int nbytesout; /* number of bytes that will be generated */ +}; + +void *__fmtflush(Fmt*, void*, int); +void *__fmtdispatch(Fmt*, void*, int); +int __floatfmt(Fmt*, double); +int __fmtpad(Fmt*, int); +int __rfmtpad(Fmt*, int); +int __fmtFdFlush(Fmt*); + +int __efgfmt(Fmt*); +int __charfmt(Fmt*); +int __runefmt(Fmt*); +int __runesfmt(Fmt*); +int __countfmt(Fmt*); +int __flagfmt(Fmt*); +int __percentfmt(Fmt*); +int __ifmt(Fmt*); +int __strfmt(Fmt*); +int __badfmt(Fmt*); +int __fmtcpy(Fmt*, const void*, int, int); +int __fmtrcpy(Fmt*, const void*, int n); +int __errfmt(Fmt *f); + +double __fmtpow10(int); + +void __fmtlock(void); +void __fmtunlock(void); + +#define FMTCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (char*)s){\ + t = __fmtflush(f, t, 1);\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRCHAR(f, t, s, c)\ + do{\ + if(t + 1 > (Rune*)s){\ + t = __fmtflush(f, t, sizeof(Rune));\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + *t++ = c;\ + }while(0) + +#define FMTRUNE(f, t, s, r)\ + do{\ + Rune _rune;\ + int _runelen;\ + if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\ + t = __fmtflush(f, t, _runelen);\ + if(t != nil)\ + s = f->stop;\ + else\ + return -1;\ + }\ + if(r < Runeself)\ + *t++ = r;\ + else{\ + _rune = r;\ + t += runetochar(t, &_rune);\ + }\ + }while(0) diff --git a/src/libfmt/fmtfd.c b/src/libfmt/fmtfd.c new file mode 100644 index 00000000..d4251402 --- /dev/null +++ b/src/libfmt/fmtfd.c @@ -0,0 +1,46 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * public routine for final flush of a formatting buffer + * to a file descriptor; returns total char count. + */ +int +fmtfdflush(Fmt *f) +{ + if(__fmtFdFlush(f) <= 0) + return -1; + return f->nfmt; +} + +/* + * initialize an output buffer for buffered printing + */ +int +fmtfdinit(Fmt *f, int fd, char *buf, int size) +{ + f->runes = 0; + f->start = buf; + f->to = buf; + f->stop = buf + size; + f->flush = __fmtFdFlush; + f->farg = (void*)fd; + f->nfmt = 0; + return 0; +} diff --git a/src/libfmt/fmtfdflush.c b/src/libfmt/fmtfdflush.c new file mode 100644 index 00000000..796feab2 --- /dev/null +++ b/src/libfmt/fmtfdflush.c @@ -0,0 +1,33 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <unistd.h> +#include "fmt.h" +#include "fmtdef.h" + +/* + * generic routine for flushing a formatting buffer + * to a file descriptor + */ +int +__fmtFdFlush(Fmt *f) +{ + int n; + + n = (char*)f->to - (char*)f->start; + if(n && write((int)f->farg, f->start, n) != n) + return 0; + f->to = f->start; + return 1; +} diff --git a/src/libfmt/fmtinstall.3 b/src/libfmt/fmtinstall.3 new file mode 100644 index 00000000..2a0e55bf --- /dev/null +++ b/src/libfmt/fmtinstall.3 @@ -0,0 +1,346 @@ +.TH FMTINSTALL 3 +.de EX +.nf +.ft B +.. +.de EE +.fi +.ft R +.. +.SH NAME +fmtinstall, dofmt, fmtprint, fmtvprint, fmtstrcpy, fmtfdinit, fmtfdflush, fmtstrinit, fmtstrflush \- support for user-defined print formats and output routines +.SH SYNOPSIS +.B #include <fmt.h> +.PP +.ft L +.nf +.ta \w' 'u +\w' 'u +\w' 'u +\w' 'u +\w' 'u +typedef struct Fmt Fmt; +struct Fmt{ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt*); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format character */ + int width; + int prec; + unsigned long flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + FmtLDouble = FmtByte << 1, + + FmtFlag = FmtLDouble << 1 +}; +.fi +.PP +.B +.ta \w'\fLchar* 'u + +.PP +.B +int fmtfdinit(Fmt *f, int fd, char *buf, int nbuf); +.PP +.B +int fmtfdflush(Fmt *f); +.PP +.B +int fmtstrinit(Fmt *f); +.PP +.B +char* fmtstrflush(Fmt *f); +.PP +.B +int fmtinstall(int c, int (*fn)(Fmt*)); +.PP +.B +int dofmt(Fmt *f, char *fmt); +.PP +.B +int fmtprint(Fmt *f, char *fmt, ...); +.PP +.B +int fmtvprint(Fmt *f, char *fmt, va_list v); +.PP +.B +int fmtrune(Fmt *f, int r); +.PP +.B +int fmtstrcpy(Fmt *f, char *s); +.SH DESCRIPTION +The interface described here allows the construction of custom +.IR print (3) +verbs and output routines. +In essence, they provide access to the workings of the formatted print code. +.PP +The +.IR print (3) +suite maintains its state with a data structure called +.BR Fmt . +A typical call to +.IR print (3) +or its relatives initializes a +.B Fmt +structure, passes it to subsidiary routines to process the output, +and finishes by emitting any saved state recorded in the +.BR Fmt . +The details of the +.B Fmt +are unimportant to outside users, except insofar as the general +design influences the interface. +The +.B Fmt +records +the verb being processed, its precision and width, +and buffering parameters. +Most important, it also records a +.I flush +routine that the library will call if a buffer overflows. +When printing to a file descriptor, the flush routine will +emit saved characters and reset the buffer; when printing +to an allocated string, it will resize the string to receive more output. +The flush routine is nil when printing to fixed-size buffers. +User code need never provide a flush routine; this is done internally +by the library. +.SS Custom output routines +To write a custom output routine, such as an error handler that +formats and prints custom error messages, the output sequence can be run +from outside the library using the routines described here. +There are two main cases: output to an open file descriptor +and output to a string. +.PP +To write to a file descriptor, call +.I fmtfdinit +to initialize the local +.B Fmt +structure +.IR f , +giving the file descriptor +.IR fd , +the buffer +.IR buf , +and its size +.IR nbuf . +Then call +.IR fmtprint +or +.IR fmtvprint +to generate the output. +These behave just like +.B fprint +(see +.IR print (3)) +or +.B vfprint +except that the characters are buffered until +.I fmtfdflush +is called. +A typical example of this sequence appears in the Examples section. +.PP +The same basic sequence applies when outputting to an allocated string: +call +.I fmtstrinit +to initialize the +.BR Fmt , +then call +.I fmtprint +and +.I fmtvprint +to generate the output. +Finally, +.I fmtstrflush +will return the allocated string, which should be freed after use. +Regardless of the output style or type, +.I fmtprint +or +.I fmtvprint +generates the characters. +.SS Custom format verbs +.I Fmtinstall +is used to install custom verbs and flags labeled by character +.IR c , +which may be any non-zero Unicode character. +.I Fn +should be declared as +.IP +.EX +int fn(Fmt*) +.EE +.PP +.IB Fp ->r +is the flag or verb character to cause +.I fn +to be called. +In +.IR fn , +.IB fp ->width , +.IB fp ->prec +are the width and precision, and +.IB fp ->flags +the decoded flags for the verb (see +.IR print (3) +for a description of these items). +The standard flag values are: +.B FmtSign +.RB ( + ), +.B FmtLeft +.RB ( - ), +.B FmtSpace +.RB ( '\ ' ), +.B FmtSharp +.RB ( # ), +.B FmtComma +.RB ( , ), +.B FmtLong +.RB ( l ), +.B FmtShort +.RB ( h ), +.B FmtByte +.RB ( hh ), +.B FmtUnsigned +.RB ( u ), +.B FmtLDouble +.RB ( L ), +and +.B FmtVLong +.RB ( ll ). +The flag bits +.B FmtWidth +and +.B FmtPrec +identify whether a width and precision were specified. +.PP +.I Fn +is passed a pointer to the +.B Fmt +structure recording the state of the output. +If +.IB fp ->r +is a verb (rather than a flag), +.I fn +should use +.B Fmt->args +to fetch its argument from the list, +then format it, and return zero. +If +.IB fp ->r +is a flag, +.I fn +should return a negative value: +the negation of one of the above flag values, or some otherwise unused power of two. +All interpretation of +.IB fp ->width\f1, +.IB fp ->prec\f1, +and +.IB fp-> flags +is left up to the conversion routine. +.I Fmtinstall +returns 0 if the installation succeeds, \-1 if it fails. +.PP +.IR Fmtprint +and +.IR fmtvprint +may be called to +help prepare output in custom conversion routines. +However, these functions clear the width, precision, and flags. +The function +.I dofmt +is the underlying formatter; it +uses the existing contents of +.B Fmt +and should be called only by sophisticated conversion routines. +All these routines return the number of characters +produced. +.PP +Some internal functions may be useful to format primitive types. +They honor the width, precision and flags as described in +.IR print (3). +.I Fmtrune +formats a single character +.BR r . +.I Fmtstrcpy +formats a string +.BR s . +All these routines return zero for successful execution. +.SH EXAMPLES +This function prints an error message with a variable +number of arguments and then quits. +Compared to the corresponding example in +.IR print (3), +this version uses a smaller buffer, will never truncate +the output message, but might generate multiple +.B write +system calls to produce its output. +.IP +.EX +.ta 6n +6n +6n +6n +6n +6n +6n +6n +6n + +void fatal(char *fmt, ...) +{ + Fmt f; + char buf[64]; + va_list arg; + + fmtfdinit(&f, 1, buf, sizeof buf); + fmtprint(&f, "fatal: "); + va_start(arg, fmt); + fmtvprint(&f, fmt, arg); + va_end(arg); + fmtprint(&f, "\en"); + fmtfdflush(&f); + exits("fatal error"); +} +.EE +.PP +This example adds a verb to print complex numbers. +.IP +.EX +typedef +struct { + double r, i; +} Complex; + +int +Xfmt(Fmt *f) +{ + Complex c; + + c = va_arg(f->args, Complex); + return fmtprint(f, "(%g,%g)", c.r, c.i); +} + +main(...) +{ + Complex x; + + x.r = 1.5; + x.i = -2.3; + + fmtinstall('X', Xfmt); + print("x = %X\en", x); +} +.EE +.SH SEE ALSO +.IR print (3) +.SH HISTORY +This formatted print library originally +appeared as part of the Plan 9 C library. +.SH BUGS +The Plan 9 version supports Unicode strings and produces UTF output. +This version assumes that characters are always represented by 1-byte values. diff --git a/src/libfmt/fmtlock.c b/src/libfmt/fmtlock.c new file mode 100644 index 00000000..fffe81cf --- /dev/null +++ b/src/libfmt/fmtlock.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" +#include "fmtdef.h" + +void +__fmtlock(void) +{ + ; +} + +void +__fmtunlock(void) +{ + ; +} diff --git a/src/libfmt/fmtprint.c b/src/libfmt/fmtprint.c new file mode 100644 index 00000000..fe2ad3cc --- /dev/null +++ b/src/libfmt/fmtprint.c @@ -0,0 +1,47 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + + +/* + * format a string into the output buffer + * designed for formats which themselves call fmt, + * but ignore any width flags + */ +int +fmtprint(Fmt *f, char *fmt, ...) +{ + va_list va; + int n; + + f->flags = 0; + f->width = 0; + f->prec = 0; + va = f->args; + va_start(f->args, fmt); + n = dofmt(f, fmt); + va_end(f->args); + f->flags = 0; + f->width = 0; + f->prec = 0; + f->args = va; + if(n >= 0) + return 0; + return n; +} + diff --git a/src/libfmt/fmtquote.c b/src/libfmt/fmtquote.c new file mode 100644 index 00000000..9d5633d6 --- /dev/null +++ b/src/libfmt/fmtquote.c @@ -0,0 +1,262 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * How many bytes of output UTF will be produced by quoting (if necessary) this string? + * How many runes? How much of the input will be consumed? + * The parameter q is filled in by __quotesetup. + * The string may be UTF or Runes (s or r). + * Return count does not include NUL. + * Terminate the scan at the first of: + * NUL in input + * count exceeded in input + * count exceeded on output + * *ninp is set to number of input bytes accepted. + * nin may be <0 initially, to avoid checking input by count. + */ +void +__quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout) +{ + int w; + Rune c; + + q->quoted = 0; + q->nbytesout = 0; + q->nrunesout = 0; + q->nbytesin = 0; + q->nrunesin = 0; + if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){ + if(nout < 2) + return; + q->quoted = 1; + q->nbytesout = 2; + q->nrunesout = 2; + } + for(; nin!=0; nin-=w){ + if(s) + w = chartorune(&c, s); + else{ + c = *r; + w = runelen(c); + } + + if(c == '\0') + break; + if(runesout){ + if(q->nrunesout+1 > nout) + break; + }else{ + if(q->nbytesout+w > nout) + break; + } + + if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){ + if(!q->quoted){ + if(runesout){ + if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ + break; + }else{ + if(1+q->nbytesout+w+1 > nout) /* no room for quotes */ + break; + } + q->nrunesout += 2; /* include quotes */ + q->nbytesout += 2; /* include quotes */ + q->quoted = 1; + } + if(c == '\'') { + if(runesout){ + if(1+q->nrunesout+1 > nout) /* no room for quotes */ + break; + }else{ + if(1+q->nbytesout+w > nout) /* no room for quotes */ + break; + } + q->nbytesout++; + q->nrunesout++; /* quotes reproduce as two characters */ + } + } + + /* advance input */ + if(s) + s += w; + else + r++; + q->nbytesin += w; + q->nrunesin++; + + /* advance output */ + q->nbytesout += w; + q->nrunesout++; + } +} + +static int +qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f) +{ + Rune r, *rm, *rme; + char *t, *s, *m, *me; + Rune *rt, *rs; + ulong fl; + int nc, w; + + m = sin; + me = m + q->nbytesin; + rm = rin; + rme = rm + q->nrunesin; + + w = f->width; + fl = f->flags; + if(f->runes){ + if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0) + return -1; + }else{ + if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + } + t = (char*)f->to; + s = (char*)f->stop; + rt = (Rune*)f->to; + rs = (Rune*)f->stop; + if(f->runes) + FMTRCHAR(f, rt, rs, '\''); + else + FMTRUNE(f, t, s, '\''); + for(nc = q->nrunesin; nc > 0; nc--){ + if(sin){ + r = *(uchar*)m; + if(r < Runeself) + m++; + else if((me - m) >= UTFmax || fullrune(m, me-m)) + m += chartorune(&r, m); + else + break; + }else{ + if(rm >= rme) + break; + r = *(uchar*)rm++; + } + if(f->runes){ + FMTRCHAR(f, rt, rs, r); + if(r == '\'') + FMTRCHAR(f, rt, rs, r); + }else{ + FMTRUNE(f, t, s, r); + if(r == '\'') + FMTRUNE(f, t, s, r); + } + } + + if(f->runes){ + FMTRCHAR(f, rt, rs, '\''); + USED(rs); + f->nfmt += rt - (Rune *)f->to; + f->to = rt; + if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0) + return -1; + }else{ + FMTRUNE(f, t, s, '\''); + USED(s); + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + } + return 0; +} + +int +__quotestrfmt(int runesin, Fmt *f) +{ + int outlen; + Rune *r; + char *s; + Quoteinfo q; + + f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */ + if(runesin){ + r = va_arg(f->args, Rune *); + s = nil; + }else{ + s = va_arg(f->args, char *); + r = nil; + } + if(!s && !r) + return __fmtcpy(f, (void*)"<nil>", 5, 5); + + if(f->flush) + outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ + else if(f->runes) + outlen = (Rune*)f->stop - (Rune*)f->to; + else + outlen = (char*)f->stop - (char*)f->to; + + __quotesetup(s, r, -1, outlen, &q, f->flags&FmtSharp, f->runes); +//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); + + if(runesin){ + if(!q.quoted) + return __fmtrcpy(f, r, q.nrunesin); + return qstrfmt(nil, r, &q, f); + } + + if(!q.quoted) + return __fmtcpy(f, s, q.nrunesin, q.nbytesin); + return qstrfmt(s, nil, &q, f); +} + +int +quotestrfmt(Fmt *f) +{ + return __quotestrfmt(0, f); +} + +int +quoterunestrfmt(Fmt *f) +{ + return __quotestrfmt(1, f); +} + +void +quotefmtinstall(void) +{ + fmtinstall('q', quotestrfmt); + fmtinstall('Q', quoterunestrfmt); +} + +int +__needsquotes(char *s, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0); + *quotelenp = q.nbytesout; + + return q.quoted; +} + +int +__runeneedsquotes(Rune *r, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0); + *quotelenp = q.nrunesout; + + return q.quoted; +} diff --git a/src/libfmt/fmtrune.c b/src/libfmt/fmtrune.c new file mode 100644 index 00000000..a034546b --- /dev/null +++ b/src/libfmt/fmtrune.c @@ -0,0 +1,40 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +int +fmtrune(Fmt *f, int r) +{ + Rune *rt; + char *t; + int n; + + if(f->runes){ + rt = (Rune*)f->to; + FMTRCHAR(f, rt, f->stop, r); + f->to = rt; + n = 1; + }else{ + t = (char*)f->to; + FMTRUNE(f, t, f->stop, r); + n = t - (char*)f->to; + f->to = t; + } + f->nfmt += n; + return 0; +} diff --git a/src/libfmt/fmtstr.c b/src/libfmt/fmtstr.c new file mode 100644 index 00000000..7af1fa24 --- /dev/null +++ b/src/libfmt/fmtstr.c @@ -0,0 +1,65 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +static int +fmtStrFlush(Fmt *f) +{ + char *s; + int n; + + n = (int)f->farg; + n += 256; + f->farg = (void*)n; + s = (char*)f->start; + f->start = realloc(s, n); + if(f->start == nil){ + f->start = s; + return 0; + } + f->to = (char*)f->start + ((char*)f->to - s); + f->stop = (char*)f->start + n - 1; + return 1; +} + +int +fmtstrinit(Fmt *f) +{ + int n; + + f->runes = 0; + n = 32; + f->start = malloc(n); + if(f->start == nil) + return -1; + f->to = f->start; + f->stop = (char*)f->start + n - 1; + f->flush = fmtStrFlush; + f->farg = (void*)n; + f->nfmt = 0; + return 0; +} + +char* +fmtstrflush(Fmt *f) +{ + *(char*)f->to = '\0'; + f->to = f->start; + return (char*)f->start; +} diff --git a/src/libfmt/fmtvprint.c b/src/libfmt/fmtvprint.c new file mode 100644 index 00000000..6aed013d --- /dev/null +++ b/src/libfmt/fmtvprint.c @@ -0,0 +1,46 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + + +/* + * format a string into the output buffer + * designed for formats which themselves call fmt, + * but ignore any width flags + */ +int +fmtvprint(Fmt *f, char *fmt, va_list args) +{ + va_list va; + int n; + + f->flags = 0; + f->width = 0; + f->prec = 0; + va = f->args; + f->args = args; + n = dofmt(f, fmt); + f->flags = 0; + f->width = 0; + f->prec = 0; + f->args = va; + if(n >= 0) + return 0; + return n; +} + diff --git a/src/libfmt/fprint.c b/src/libfmt/fprint.c new file mode 100644 index 00000000..c8b889de --- /dev/null +++ b/src/libfmt/fprint.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "utf.h" +#include "fmt.h" + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vfprint(fd, fmt, args); + va_end(args); + return n; +} diff --git a/src/libfmt/mkfile b/src/libfmt/mkfile new file mode 100644 index 00000000..bb99a25a --- /dev/null +++ b/src/libfmt/mkfile @@ -0,0 +1 @@ +<../libutf/mkfile diff --git a/src/libfmt/nan.h b/src/libfmt/nan.h new file mode 100644 index 00000000..be78e14e --- /dev/null +++ b/src/libfmt/nan.h @@ -0,0 +1,4 @@ +extern double __NaN(void); +extern double __Inf(int); +extern int __isNaN(double); +extern int __isInf(double, int); diff --git a/src/libfmt/nan64.c b/src/libfmt/nan64.c new file mode 100644 index 00000000..dbd03fc6 --- /dev/null +++ b/src/libfmt/nan64.c @@ -0,0 +1,72 @@ +/* + * 64-bit IEEE not-a-number routines. + * This is big/little-endian portable assuming that + * the 64-bit doubles and 64-bit integers have the + * same byte ordering. + */ + +#include "nan.h" + +typedef unsigned long long uvlong; +typedef unsigned long ulong; + +#ifdef NEEDLL +static uvlong uvnan = 0x7FF0000000000001LL; +static uvlong uvinf = 0x7FF0000000000000LL; +static uvlong uvneginf = 0xFFF0000000000000LL; +#else +static uvlong uvnan = 0x7FF0000000000001; +static uvlong uvinf = 0x7FF0000000000000; +static uvlong uvneginf = 0xFFF0000000000000; +#endif + +double +__NaN(void) +{ + uvlong *p; + + /* gcc complains about "return *(double*)&uvnan;" */ + p = &uvnan; + return *(double*)p; +} + +int +__isNaN(double d) +{ + uvlong x; + double *p; + + p = &d; + x = *(uvlong*)p; + return (ulong)(x>>32)==0x7FF00000 && !__isInf(d, 0); +} + +double +__Inf(int sign) +{ + uvlong *p; + + if(sign < 0) + p = &uvinf; + else + p = &uvneginf; + return *(double*)p; +} + +int +__isInf(double d, int sign) +{ + uvlong x; + double *p; + + p = &d; + x = *(uvlong*)p; + if(sign == 0) + return x==uvinf || x==uvneginf; + else if(sign > 0) + return x==uvinf; + else + return x==uvneginf; +} + + diff --git a/src/libfmt/pow10.c b/src/libfmt/pow10.c new file mode 100644 index 00000000..65a426c3 --- /dev/null +++ b/src/libfmt/pow10.c @@ -0,0 +1,57 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * this table might overflow 127-bit exponent representations. + * in that case, truncate it after 1.0e38. + * it is important to get all one can from this + * routine since it is used in atof to scale numbers. + * the presumption is that C converts fp numbers better + * than multipication of lower powers of 10. + */ + +static +double tab[] = +{ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19, + 1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29, + 1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39, + 1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49, + 1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59, + 1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69, +}; + +double +__fmtpow10(int n) +{ + int m; + + if(n < 0) { + n = -n; + if(n < (int)(sizeof(tab)/sizeof(tab[0]))) + return 1/tab[n]; + m = n/2; + return __fmtpow10(-m) * __fmtpow10(m-n); + } + if(n < (int)(sizeof(tab)/sizeof(tab[0]))) + return tab[n]; + m = n/2; + return __fmtpow10(m) * __fmtpow10(n-m); +} diff --git a/src/libfmt/print.3 b/src/libfmt/print.3 new file mode 100644 index 00000000..1fab0ad8 --- /dev/null +++ b/src/libfmt/print.3 @@ -0,0 +1,469 @@ +.TH PRINT 3 +.de EX +.nf +.ft B +.. +.de EE +.fi +.ft R +.. +.SH NAME +print, fprint, sprint, snprint, seprint, smprint, vfprint, vsnprint, vseprint, vsmprint \- print formatted output +.SH SYNOPSIS +.B #include <utf.h> +.PP +.B #include <fmt.h> +.PP +.ta \w'\fLchar* 'u +.B +int print(char *format, ...) +.PP +.B +int fprint(int fd, char *format, ...) +.PP +.B +int sprint(char *s, char *format, ...) +.PP +.B +int snprint(char *s, int len, char *format, ...) +.PP +.B +char* seprint(char *s, char *e, char *format, ...) +.PP +.B +char* smprint(char *format, ...) +.PP +.B +int runesprint(Rune *s, char *format, ...) +.PP +.B +int runesnprint(Rune *s, int len, char *format, ...) +.PP +.B +Rune* runeseprint(Rune *s, Rune *e, char *format, ...) +.PP +.B +Rune* runesmprint(char *format, ...) +.PP +.B +int vfprint(int fd, char *format, va_list v) +.PP +.B +int vsnprint(char *s, int len, char *format, va_list v) +.PP +.B +char* vseprint(char *s, char *e, char *format, va_list v) +.PP +.B +char* vsmprint(char *format, va_list v) +.PP +.B +int runevsnprint(Rune *s, int len, char *format, va_list v) +.PP +.B +Rune* runevseprint(Rune *s, Rune *e, char *format, va_list v) +.PP +.B +Rune* runevsmprint(Rune *format, va_list v) +.PP +.B +.SH DESCRIPTION +.I Print +writes text to the standard output. +.I Fprint +writes to the named output +file descriptor. +.I Sprint +places text +followed by the NUL character +.RB ( \e0 ) +in consecutive bytes starting at +.IR s ; +it is the user's responsibility to ensure that +enough storage is available. +Each function returns the number of bytes +transmitted (not including the NUL +in the case of +.IR sprint ), +or +a negative value if an output error was encountered. +.PP +.I Snprint +is like +.IR sprint , +but will not place more than +.I len +bytes in +.IR s . +Its result is always NUL-terminated and holds the maximal +number of characters that can fit. +.I Seprint +is like +.IR snprint , +except that the end is indicated by a pointer +.I e +rather than a count and the return value points to the terminating NUL of the +resulting string. +.I Smprint +is like +.IR sprint , +except that it prints into and returns a string of the required length, which is +allocated by +.IR malloc (3). +.PP +The routines +.IR runesprint , +.IR runesnprint , +.IR runeseprint , +and +.I runesmprint +are the same as +.IR sprint , +.IR snprint , +.IR seprint +and +.I smprint +except that their output is rune strings instead of byte strings. +.PP +Finally, the routines +.IR vfprint , +.IR vsnprint , +.IR vseprint , +.IR vsmprint , +.IR runevsnprint , +.IR runevseprint , +and +.I runevsmprint +are like their +.BR v-less +relatives except they take as arguments a +.B va_list +parameter, so they can be called within a variadic function. +The Example section shows a representative usage. +.PP +Each of these functions +converts, formats, and prints its +trailing arguments +under control of a +.IR format +string. +The +format +contains two types of objects: +plain characters, which are simply copied to the +output stream, +and conversion specifications, +each of which results in fetching of +zero or more +arguments. +The results are undefined if there are arguments of the +wrong type or too few +arguments for the format. +If the format is exhausted while +arguments remain, the excess +is ignored. +.PP +Each conversion specification has the following format: +.IP +.B "% [flags] verb +.PP +The verb is a single character and each flag is a single character or a +(decimal) numeric string. +Up to two numeric strings may be used; +the first is called +.IR width , +the second +.IR precision . +A period can be used to separate them, and if the period is +present then +.I width +and +.I precision +are taken to be zero if missing, otherwise they are `omitted'. +Either or both of the numbers may be replaced with the character +.BR * , +meaning that the actual number will be obtained from the argument list +as an integer. +The flags and numbers are arguments to +the +.I verb +described below. +.PP +The numeric verbs +.BR d , +.BR i , +.BR u , +.BR o , +.BR b , +.BR x , +and +.B X +format their arguments in decimal, decimal, +unsigned decimal, octal, binary, hexadecimal, and upper case hexadecimal. +Each interprets the flags +.BR 0 , +.BR h , +.BR hh , +.BR l , +.BR + , +.BR - , +.BR , , +and +.B # +to mean pad with zeros, +short, byte, long, always print a sign, left justified, commas every three digits, +and alternate format. +Also, a space character in the flag +position is like +.BR + , +but prints a space instead of a plus sign for non-negative values. +If neither +short nor long is specified, +then the argument is an +.BR int . +If an unsigned verb is specified, +then the argument is interpreted as a +positive number and no sign is output; +space and +.B + +flags are ignored for unsigned verbs. +If two +.B l +flags are given, +then the argument is interpreted as a +.B vlong +(usually an 8-byte, sometimes a 4-byte integer). +If +.I precision +is not omitted, the number is padded on the left with zeros +until at least +.I precision +digits appear. +If +.I precision +is explicitly 0, and the number is 0, +no digits are generated, and alternate formatting +does not apply. +Then, if alternate format is specified, +for +.B o +conversion, the number is preceded by a +.B 0 +if it doesn't already begin with one. +For non-zero numbers and +.B x +conversion, the number is preceded by +.BR 0x ; +for +.B X +conversion, the number is preceded by +.BR 0X . +Finally, if +.I width +is not omitted, the number is padded on the left (or right, if +left justification is specified) with enough blanks to +make the field at least +.I width +characters long. +.PP +The floating point verbs +.BR f , +.BR e , +.BR E , +.BR g , +and +.B G +take a +.B double +argument. +Each interprets the flags +.BR 0 , +.BR L +.BR + , +.BR - , +and +.B # +to mean pad with zeros, +long double argument, +always print a sign, +left justified, +and +alternate format. +.I Width +is the minimum field width and, +if the converted value takes up less than +.I width +characters, it is padded on the left (or right, if `left justified') +with spaces. +.I Precision +is the number of digits that are converted after the decimal place for +.BR e , +.BR E , +and +.B f +conversions, +and +.I precision +is the maximum number of significant digits for +.B g +and +.B G +conversions. +The +.B f +verb produces output of the form +.RB [ - ] digits [ .digits\fR]. +.B E +conversion appends an exponent +.BR E [ - ] digits , +and +.B e +conversion appends an exponent +.BR e [ - ] digits . +The +.B g +verb will output the argument in either +.B e +or +.B f +with the goal of producing the smallest output. +Also, trailing zeros are omitted from the fraction part of +the output, and a trailing decimal point appears only if it is followed +by a digit. +The +.B G +verb is similar, but uses +.B E +format instead of +.BR e . +When alternate format is specified, the result will always contain a decimal point, +and for +.B g +and +.B G +conversions, trailing zeros are not removed. +.PP +The +.B s +verb copies a string +(pointer to +.BR char ) +to the output. +The number of characters copied +.RI ( n ) +is the minimum +of the size of the string and +.IR precision . +These +.I n +characters are justified within a field of +.I width +characters as described above. +If a +.I precision +is given, it is safe for the string not to be nul-terminated +as long as it is at least +.I precision +characters (not bytes!) long. +The +.B S +verb is similar, but it interprets its pointer as an array +of runes (see +.IR utf (7)); +the runes are converted to +.SM UTF +before output. +.PP +The +.B c +verb copies a single +.B char +(promoted to +.BR int ) +justified within a field of +.I width +characters as described above. +The +.B C +verb is similar, but works on runes. +.PP +The +.B p +verb formats a pointer value. +At the moment, it is a synonym for +.BR x , +but that will change if pointers and integers are different sizes. +.PP +The +.B r +verb takes no arguments; it copies the error string returned by a call to +.IR strerror (3) +with an argument of +.IR errno. +.PP +Custom verbs may be installed using +.IR fmtinstall (3). +.SH EXAMPLE +This function prints an error message with a variable +number of arguments and then quits. +.IP +.EX +.ta 6n +6n +6n +void fatal(char *msg, ...) +{ + char buf[1024], *out; + va_list arg; + + out = vseprint(buf, buf+sizeof buf, "Fatal error: "); + va_start(arg, msg); + out = vseprint(out, buf+sizeof buf, msg, arg); + va_end(arg); + write(2, buf, out-buf); + exit(1); +} +.EE +.SH SEE ALSO +.IR fmtinstall (3), +.IR fprintf (3), +.IR utf (7) +.SH DIAGNOSTICS +Routines that write to a file descriptor or call +.IR malloc +set +.IR errstr . +.SH BUGS +The formatting is close to that specified for ANSI +.IR fprintf (3); +the main difference is that +.B b +and +.B r +are not in ANSI and some +.B C9X +verbs are missing. +Also, and distinctly not a bug, +.I print +and friends generate +.SM UTF +rather than +.SM ASCII. +.PP +There is no +.BR runeprint , +.BR runefprint , +etc. because runes are byte-order dependent and should not be written directly to a file; use the +UTF output of +.I print +or +.I fprint +instead. +Also, +.I sprint +is deprecated for safety reasons; use +.IR snprint , +.IR seprint , +or +.I smprint +instead. +Safety also precludes the existence of +.IR runesprint . diff --git a/src/libfmt/print.c b/src/libfmt/print.c new file mode 100644 index 00000000..381a1da9 --- /dev/null +++ b/src/libfmt/print.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "utf.h" +#include "fmt.h" + +int +print(char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vfprint(1, fmt, args); + va_end(args); + return n; +} diff --git a/src/libfmt/rpm.spec b/src/libfmt/rpm.spec new file mode 100644 index 00000000..2cd51484 --- /dev/null +++ b/src/libfmt/rpm.spec @@ -0,0 +1,34 @@ +Summary: Extensible formatted print library. (Printf with user-defined verbs.) +Name: libfmt +Version: 2.0 +Release: 1 +Group: Development/C +Copyright: BSD-like +Packager: Russ Cox <rsc@post.harvard.edu> +Source: http://pdos.lcs.mit.edu/~rsc/software/libfmt-2.0.tgz +URL: http://pdos.lcs.mit.edu/~rsc/software/#libfmt +Requires: libutf + +%description +Libfmt is a port of Plan 9's formatted print library. +As a base it provides all the syntax of ANSI printf +but adds the ability for client programs to install +new print verbs. One such print verb (installed by +default) is %r, which prints the system error string. +Instead of perror("foo"), you can write fprint(2, "foo: %r\n"). +This is especially nice when you write verbs to format +the data structures used by your particular program. +%prep +%setup + +%build +make + +%install +make install + +%files +/usr/local/include/fmt.h +/usr/local/lib/libfmt.a +/usr/local/man/man3/print.3 +/usr/local/man/man3/fmtinstall.3 diff --git a/src/libfmt/runefmtstr.c b/src/libfmt/runefmtstr.c new file mode 100644 index 00000000..a2ec6cb4 --- /dev/null +++ b/src/libfmt/runefmtstr.c @@ -0,0 +1,65 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +static int +runeFmtStrFlush(Fmt *f) +{ + Rune *s; + int n; + + n = (int)f->farg; + n += 256; + f->farg = (void*)n; + s = (Rune*)f->start; + f->start = realloc(s, sizeof(Rune)*n); + if(f->start == nil){ + f->start = s; + return 0; + } + f->to = (Rune*)f->start + ((Rune*)f->to - s); + f->stop = (Rune*)f->start + n - 1; + return 1; +} + +int +runefmtstrinit(Fmt *f) +{ + int n; + + f->runes = 1; + n = 32; + f->start = malloc(sizeof(Rune)*n); + if(f->start == nil) + return -1; + f->to = f->start; + f->stop = (Rune*)f->start + n - 1; + f->flush = runeFmtStrFlush; + f->farg = (void*)n; + f->nfmt = 0; + return 0; +} + +Rune* +runefmtstrflush(Fmt *f) +{ + *(Rune*)f->to = '\0'; + f->to = f->start; + return f->start; +} diff --git a/src/libfmt/runeseprint.c b/src/libfmt/runeseprint.c new file mode 100644 index 00000000..7829a439 --- /dev/null +++ b/src/libfmt/runeseprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runeseprint(Rune *buf, Rune *e, char *fmt, ...) +{ + Rune *p; + va_list args; + + va_start(args, fmt); + p = runevseprint(buf, e, fmt, args); + va_end(args); + return p; +} diff --git a/src/libfmt/runesmprint.c b/src/libfmt/runesmprint.c new file mode 100644 index 00000000..538ae0ee --- /dev/null +++ b/src/libfmt/runesmprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runesmprint(char *fmt, ...) +{ + va_list args; + Rune *p; + + va_start(args, fmt); + p = runevsmprint(fmt, args); + va_end(args); + return p; +} diff --git a/src/libfmt/runesnprint.c b/src/libfmt/runesnprint.c new file mode 100644 index 00000000..5abb6dbf --- /dev/null +++ b/src/libfmt/runesnprint.c @@ -0,0 +1,31 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +int +runesnprint(Rune *buf, int len, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = runevsnprint(buf, len, fmt, args); + va_end(args); + return n; +} + diff --git a/src/libfmt/runesprint.c b/src/libfmt/runesprint.c new file mode 100644 index 00000000..8abfeb65 --- /dev/null +++ b/src/libfmt/runesprint.c @@ -0,0 +1,30 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +int +runesprint(Rune *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = runevsnprint(buf, 256, fmt, args); + va_end(args); + return n; +} diff --git a/src/libfmt/runevseprint.c b/src/libfmt/runevseprint.c new file mode 100644 index 00000000..e4cc1ea8 --- /dev/null +++ b/src/libfmt/runevseprint.c @@ -0,0 +1,39 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +Rune* +runevseprint(Rune *buf, Rune *e, char *fmt, va_list args) +{ + Fmt f; + + if(e <= buf) + return nil; + f.runes = 1; + f.start = buf; + f.to = buf; + f.stop = e - 1; + f.flush = nil; + f.farg = nil; + f.nfmt = 0; + f.args = args; + dofmt(&f, fmt); + *(Rune*)f.to = '\0'; + return (Rune*)f.to; +} + diff --git a/src/libfmt/runevsmprint.c b/src/libfmt/runevsmprint.c new file mode 100644 index 00000000..71c23d84 --- /dev/null +++ b/src/libfmt/runevsmprint.c @@ -0,0 +1,37 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <stdlib.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +/* + * print into an allocated string buffer + */ +Rune* +runevsmprint(char *fmt, va_list args) +{ + Fmt f; + int n; + + if(runefmtstrinit(&f) < 0) + return nil; + f.args = args; + n = dofmt(&f, fmt); + if(n < 0) + return nil; + *(Rune*)f.to = '\0'; + return (Rune*)f.start; +} diff --git a/src/libfmt/runevsnprint.c b/src/libfmt/runevsnprint.c new file mode 100644 index 00000000..933a04ca --- /dev/null +++ b/src/libfmt/runevsnprint.c @@ -0,0 +1,38 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <string.h> +#include "utf.h" +#include "fmt.h" +#include "fmtdef.h" + +int +runevsnprint(Rune *buf, int len, char *fmt, va_list args) +{ + Fmt f; + + if(len <= 0) + return -1; + f.runes = 1; + f.start = buf; + f.to = buf; + f.stop = buf + len - 1; + f.flush = nil; + f.farg = nil; + f.nfmt = 0; + f.args = args; + dofmt(&f, fmt); + *(Rune*)f.to = '\0'; + return (Rune*)f.to - buf; +} diff --git a/src/libfmt/seprint.c b/src/libfmt/seprint.c new file mode 100644 index 00000000..d5031a2b --- /dev/null +++ b/src/libfmt/seprint.c @@ -0,0 +1,27 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" + +char* +seprint(char *buf, char *e, char *fmt, ...) +{ + char *p; + va_list args; + + va_start(args, fmt); + p = vseprint(buf, e, fmt, args); + va_end(args); + return p; +} diff --git a/src/libfmt/smprint.c b/src/libfmt/smprint.c new file mode 100644 index 00000000..543411a0 --- /dev/null +++ b/src/libfmt/smprint.c @@ -0,0 +1,27 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" + +char* +smprint(char *fmt, ...) +{ + va_list args; + char *p; + + va_start(args, fmt); + p = vsmprint(fmt, args); + va_end(args); + return p; +} diff --git a/src/libfmt/snprint.c b/src/libfmt/snprint.c new file mode 100644 index 00000000..594606d2 --- /dev/null +++ b/src/libfmt/snprint.c @@ -0,0 +1,28 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" + +int +snprint(char *buf, int len, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vsnprint(buf, len, fmt, args); + va_end(args); + return n; +} + diff --git a/src/libfmt/sprint.c b/src/libfmt/sprint.c new file mode 100644 index 00000000..57150c36 --- /dev/null +++ b/src/libfmt/sprint.c @@ -0,0 +1,27 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" + +int +sprint(char *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = vsnprint(buf, 65536, fmt, args); /* big number, but sprint is deprecated anyway */ + va_end(args); + return n; +} diff --git a/src/libfmt/strtod.c b/src/libfmt/strtod.c new file mode 100644 index 00000000..685b7fae --- /dev/null +++ b/src/libfmt/strtod.c @@ -0,0 +1,539 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdlib.h> +#include <math.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "fmt.h" +#include "nan.h" + +#ifndef nelem +#define nelem(x) (sizeof(x)/sizeof *(x)) +#endif +#define nil ((void*)0) +#define ulong _fmtulong +typedef unsigned long ulong; + +static ulong +umuldiv(ulong a, ulong b, ulong c) +{ + double d; + + d = ((double)a * (double)b) / (double)c; + if(d >= 4294967295.) + d = 4294967295.; + return (ulong)d; +} + +/* + * This routine will convert to arbitrary precision + * floating point entirely in multi-precision fixed. + * The answer is the closest floating point number to + * the given decimal number. Exactly half way are + * rounded ala ieee rules. + * Method is to scale input decimal between .500 and .999... + * with external power of 2, then binary search for the + * closest mantissa to this decimal number. + * Nmant is is the required precision. (53 for ieee dp) + * Nbits is the max number of bits/word. (must be <= 28) + * Prec is calculated - the number of words of fixed mantissa. + */ +enum +{ + Nbits = 28, /* bits safely represented in a ulong */ + Nmant = 53, /* bits of precision required */ + Prec = (Nmant+Nbits+1)/Nbits, /* words of Nbits each to represent mantissa */ + Sigbit = 1<<(Prec*Nbits-Nmant), /* first significant bit of Prec-th word */ + Ndig = 1500, + One = (ulong)(1<<Nbits), + Half = (ulong)(One>>1), + Maxe = 310, + + Fsign = 1<<0, /* found - */ + Fesign = 1<<1, /* found e- */ + Fdpoint = 1<<2, /* found . */ + + S0 = 0, /* _ _S0 +S1 #S2 .S3 */ + S1, /* _+ #S2 .S3 */ + S2, /* _+# #S2 .S4 eS5 */ + S3, /* _+. #S4 */ + S4, /* _+#.# #S4 eS5 */ + S5, /* _+#.#e +S6 #S7 */ + S6, /* _+#.#e+ #S7 */ + S7, /* _+#.#e+# #S7 */ +}; + +static int xcmp(char*, char*); +static int fpcmp(char*, ulong*); +static void frnorm(ulong*); +static void divascii(char*, int*, int*, int*); +static void mulascii(char*, int*, int*, int*); + +typedef struct Tab Tab; +struct Tab +{ + int bp; + int siz; + char* cmp; +}; + +double +fmtstrtod(const char *as, char **aas) +{ + int na, ex, dp, bp, c, i, flag, state; + ulong low[Prec], hig[Prec], mid[Prec]; + double d; + char *s, a[Ndig]; + + flag = 0; /* Fsign, Fesign, Fdpoint */ + na = 0; /* number of digits of a[] */ + dp = 0; /* na of decimal point */ + ex = 0; /* exonent */ + + state = S0; + for(s=(char*)as;; s++) { + c = *s; + if(c >= '0' && c <= '9') { + switch(state) { + case S0: + case S1: + case S2: + state = S2; + break; + case S3: + case S4: + state = S4; + break; + + case S5: + case S6: + case S7: + state = S7; + ex = ex*10 + (c-'0'); + continue; + } + if(na == 0 && c == '0') { + dp--; + continue; + } + if(na < Ndig-50) + a[na++] = c; + continue; + } + switch(c) { + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + if(state == S0) + continue; + break; + case '-': + if(state == S0) + flag |= Fsign; + else + flag |= Fesign; + case '+': + if(state == S0) + state = S1; + else + if(state == S5) + state = S6; + else + break; /* syntax */ + continue; + case '.': + flag |= Fdpoint; + dp = na; + if(state == S0 || state == S1) { + state = S3; + continue; + } + if(state == S2) { + state = S4; + continue; + } + break; + case 'e': + case 'E': + if(state == S2 || state == S4) { + state = S5; + continue; + } + break; + } + break; + } + + /* + * clean up return char-pointer + */ + switch(state) { + case S0: + if(xcmp(s, "nan") == 0) { + if(aas != nil) + *aas = s+3; + goto retnan; + } + case S1: + if(xcmp(s, "infinity") == 0) { + if(aas != nil) + *aas = s+8; + goto retinf; + } + if(xcmp(s, "inf") == 0) { + if(aas != nil) + *aas = s+3; + goto retinf; + } + case S3: + if(aas != nil) + *aas = (char*)as; + goto ret0; /* no digits found */ + case S6: + s--; /* back over +- */ + case S5: + s--; /* back over e */ + break; + } + if(aas != nil) + *aas = s; + + if(flag & Fdpoint) + while(na > 0 && a[na-1] == '0') + na--; + if(na == 0) + goto ret0; /* zero */ + a[na] = 0; + if(!(flag & Fdpoint)) + dp = na; + if(flag & Fesign) + ex = -ex; + dp += ex; + if(dp < -Maxe){ + errno = ERANGE; + goto ret0; /* underflow by exp */ + } else + if(dp > +Maxe) + goto retinf; /* overflow by exp */ + + /* + * normalize the decimal ascii number + * to range .[5-9][0-9]* e0 + */ + bp = 0; /* binary exponent */ + while(dp > 0) + divascii(a, &na, &dp, &bp); + while(dp < 0 || a[0] < '5') + mulascii(a, &na, &dp, &bp); + + /* close approx by naive conversion */ + mid[0] = 0; + mid[1] = 1; + for(i=0; c=a[i]; i++) { + mid[0] = mid[0]*10 + (c-'0'); + mid[1] = mid[1]*10; + if(i >= 8) + break; + } + low[0] = umuldiv(mid[0], One, mid[1]); + hig[0] = umuldiv(mid[0]+1, One, mid[1]); + for(i=1; i<Prec; i++) { + low[i] = 0; + hig[i] = One-1; + } + + /* binary search for closest mantissa */ + for(;;) { + /* mid = (hig + low) / 2 */ + c = 0; + for(i=0; i<Prec; i++) { + mid[i] = hig[i] + low[i]; + if(c) + mid[i] += One; + c = mid[i] & 1; + mid[i] >>= 1; + } + frnorm(mid); + + /* compare */ + c = fpcmp(a, mid); + if(c > 0) { + c = 1; + for(i=0; i<Prec; i++) + if(low[i] != mid[i]) { + c = 0; + low[i] = mid[i]; + } + if(c) + break; /* between mid and hig */ + continue; + } + if(c < 0) { + for(i=0; i<Prec; i++) + hig[i] = mid[i]; + continue; + } + + /* only hard part is if even/odd roundings wants to go up */ + c = mid[Prec-1] & (Sigbit-1); + if(c == Sigbit/2 && (mid[Prec-1]&Sigbit) == 0) + mid[Prec-1] -= c; + break; /* exactly mid */ + } + + /* normal rounding applies */ + c = mid[Prec-1] & (Sigbit-1); + mid[Prec-1] -= c; + if(c >= Sigbit/2) { + mid[Prec-1] += Sigbit; + frnorm(mid); + } + goto out; + +ret0: + return 0; + +retnan: + return __NaN(); + +retinf: + /* + * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ + errno = ERANGE; + if(flag & Fsign) + return -HUGE_VAL; + return HUGE_VAL; + +out: + d = 0; + for(i=0; i<Prec; i++) + d = d*One + mid[i]; + if(flag & Fsign) + d = -d; + d = ldexp(d, bp - Prec*Nbits); + if(d == 0){ /* underflow */ + errno = ERANGE; + } + return d; +} + +static void +frnorm(ulong *f) +{ + int i, c; + + c = 0; + for(i=Prec-1; i>0; i--) { + f[i] += c; + c = f[i] >> Nbits; + f[i] &= One-1; + } + f[0] += c; +} + +static int +fpcmp(char *a, ulong* f) +{ + ulong tf[Prec]; + int i, d, c; + + for(i=0; i<Prec; i++) + tf[i] = f[i]; + + for(;;) { + /* tf *= 10 */ + for(i=0; i<Prec; i++) + tf[i] = tf[i]*10; + frnorm(tf); + d = (tf[0] >> Nbits) + '0'; + tf[0] &= One-1; + + /* compare next digit */ + c = *a; + if(c == 0) { + if('0' < d) + return -1; + if(tf[0] != 0) + goto cont; + for(i=1; i<Prec; i++) + if(tf[i] != 0) + goto cont; + return 0; + } + if(c > d) + return +1; + if(c < d) + return -1; + a++; + cont:; + } + return 0; +} + +static void +divby(char *a, int *na, int b) +{ + int n, c; + char *p; + + p = a; + n = 0; + while(n>>b == 0) { + c = *a++; + if(c == 0) { + while(n) { + c = n*10; + if(c>>b) + break; + n = c; + } + goto xx; + } + n = n*10 + c-'0'; + (*na)--; + } + for(;;) { + c = n>>b; + n -= c<<b; + *p++ = c + '0'; + c = *a++; + if(c == 0) + break; + n = n*10 + c-'0'; + } + (*na)++; +xx: + while(n) { + n = n*10; + c = n>>b; + n -= c<<b; + *p++ = c + '0'; + (*na)++; + } + *p = 0; +} + +static Tab tab1[] = +{ + 1, 0, "", + 3, 1, "7", + 6, 2, "63", + 9, 3, "511", + 13, 4, "8191", + 16, 5, "65535", + 19, 6, "524287", + 23, 7, "8388607", + 26, 8, "67108863", + 27, 9, "134217727", +}; + +static void +divascii(char *a, int *na, int *dp, int *bp) +{ + int b, d; + Tab *t; + + d = *dp; + if(d >= (int)(nelem(tab1))) + d = (int)(nelem(tab1))-1; + t = tab1 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) > 0) + d--; + *dp -= d; + *bp += b; + divby(a, na, b); +} + +static void +mulby(char *a, char *p, char *q, int b) +{ + int n, c; + + n = 0; + *p = 0; + for(;;) { + q--; + if(q < a) + break; + c = *q - '0'; + c = (c<<b) + n; + n = c/10; + c -= n*10; + p--; + *p = c + '0'; + } + while(n) { + c = n; + n = c/10; + c -= n*10; + p--; + *p = c + '0'; + } +} + +static Tab tab2[] = +{ + 1, 1, "", /* dp = 0-0 */ + 3, 3, "125", + 6, 5, "15625", + 9, 7, "1953125", + 13, 10, "1220703125", + 16, 12, "152587890625", + 19, 14, "19073486328125", + 23, 17, "11920928955078125", + 26, 19, "1490116119384765625", + 27, 19, "7450580596923828125", /* dp 8-9 */ +}; + +static void +mulascii(char *a, int *na, int *dp, int *bp) +{ + char *p; + int d, b; + Tab *t; + + d = -*dp; + if(d >= (int)(nelem(tab2))) + d = (int)(nelem(tab2))-1; + t = tab2 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) < 0) + d--; + p = a + *na; + *bp -= b; + *dp += d; + *na += d; + mulby(a, p+d, p, b); +} + +static int +xcmp(char *a, char *b) +{ + int c1, c2; + + while(c1 = *b++) { + c2 = *a++; + if(isupper(c2)) + c2 = tolower(c2); + if(c1 != c2) + return 1; + } + return 0; +} diff --git a/src/libfmt/strtod.h b/src/libfmt/strtod.h new file mode 100644 index 00000000..82c3d46e --- /dev/null +++ b/src/libfmt/strtod.h @@ -0,0 +1,4 @@ +extern double __NaN(void); +extern double __Inf(int); +extern double __isNaN(double); +extern double __isInf(double, int); diff --git a/src/libfmt/test.c b/src/libfmt/test.c new file mode 100644 index 00000000..a1a1d5ed --- /dev/null +++ b/src/libfmt/test.c @@ -0,0 +1,39 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include <utf.h> +#include "fmt.h" + +int +main(int argc, char *argv[]) +{ + quotefmtinstall(); + print("hello world\n"); + print("x: %x\n", 0x87654321); + print("u: %u\n", 0x87654321); + print("d: %d\n", 0x87654321); + print("s: %s\n", "hi there"); + print("q: %q\n", "hi i'm here"); + print("c: %c\n", '!'); + print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); + print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); + print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); + print("smiley: %C\n", (Rune)0x263a); + print("%g %.18\n", 2e25, 2e25); + print("%2.18g\n", 1.0); + print("%f\n", 3.1415927/4); + print("%d\n", 23); + print("%i\n", 23); + return 0; +} diff --git a/src/libfmt/vfprint.c b/src/libfmt/vfprint.c new file mode 100644 index 00000000..e4ab82ac --- /dev/null +++ b/src/libfmt/vfprint.c @@ -0,0 +1,31 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" +#include "fmtdef.h" + +int +vfprint(int fd, char *fmt, va_list args) +{ + Fmt f; + char buf[256]; + int n; + + fmtfdinit(&f, fd, buf, sizeof(buf)); + f.args = args; + n = dofmt(&f, fmt); + if(n > 0 && __fmtFdFlush(&f) == 0) + return -1; + return n; +} diff --git a/src/libfmt/vseprint.c b/src/libfmt/vseprint.c new file mode 100644 index 00000000..85ed810b --- /dev/null +++ b/src/libfmt/vseprint.c @@ -0,0 +1,37 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdarg.h> +#include "fmt.h" +#include "fmtdef.h" + +char* +vseprint(char *buf, char *e, char *fmt, va_list args) +{ + Fmt f; + + if(e <= buf) + return nil; + f.runes = 0; + f.start = buf; + f.to = buf; + f.stop = e - 1; + f.flush = 0; + f.farg = nil; + f.nfmt = 0; + f.args = args; + dofmt(&f, fmt); + *(char*)f.to = '\0'; + return (char*)f.to; +} + diff --git a/src/libfmt/vsmprint.c b/src/libfmt/vsmprint.c new file mode 100644 index 00000000..38ace62d --- /dev/null +++ b/src/libfmt/vsmprint.c @@ -0,0 +1,36 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdlib.h> +#include <stdarg.h> +#include "fmt.h" +#include "fmtdef.h" + +/* + * print into an allocated string buffer + */ +char* +vsmprint(char *fmt, va_list args) +{ + Fmt f; + int n; + + if(fmtstrinit(&f) < 0) + return nil; + f.args = args; + n = dofmt(&f, fmt); + if(n < 0) + return nil; + *(char*)f.to = '\0'; + return (char*)f.start; +} diff --git a/src/libfmt/vsnprint.c b/src/libfmt/vsnprint.c new file mode 100644 index 00000000..21662e6d --- /dev/null +++ b/src/libfmt/vsnprint.c @@ -0,0 +1,37 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include <stdlib.h> +#include <stdarg.h> +#include "fmt.h" +#include "fmtdef.h" + +int +vsnprint(char *buf, int len, char *fmt, va_list args) +{ + Fmt f; + + if(len <= 0) + return -1; + f.runes = 0; + f.start = buf; + f.to = buf; + f.stop = buf + len - 1; + f.flush = 0; + f.farg = nil; + f.nfmt = 0; + f.args = args; + dofmt(&f, fmt); + *(char*)f.to = '\0'; + return (char*)f.to - buf; +} |