diff options
Diffstat (limited to 'snprintfv')
-rw-r--r-- | snprintfv/AUTHORS | 8 | ||||
-rw-r--r-- | snprintfv/Makefile.am | 48 | ||||
-rw-r--r-- | snprintfv/Makefile.in | 590 | ||||
-rw-r--r-- | snprintfv/README | 30 | ||||
-rw-r--r-- | snprintfv/THANKS | 13 | ||||
-rw-r--r-- | snprintfv/compat.h | 315 | ||||
-rw-r--r-- | snprintfv/custom.c | 182 | ||||
-rw-r--r-- | snprintfv/filament.c | 232 | ||||
-rw-r--r-- | snprintfv/filament.h | 257 | ||||
-rw-r--r-- | snprintfv/filament.in | 197 | ||||
-rw-r--r-- | snprintfv/filament.stamp | 0 | ||||
-rw-r--r-- | snprintfv/format.c | 1275 | ||||
-rw-r--r-- | snprintfv/mem.c | 82 | ||||
-rw-r--r-- | snprintfv/mem.h | 120 | ||||
-rw-r--r-- | snprintfv/printf.c | 1568 | ||||
-rw-r--r-- | snprintfv/printf.h | 846 | ||||
-rw-r--r-- | snprintfv/printf.in | 318 | ||||
-rw-r--r-- | snprintfv/printf.stamp | 0 | ||||
-rw-r--r-- | snprintfv/stream.c | 226 | ||||
-rw-r--r-- | snprintfv/stream.h | 192 | ||||
-rw-r--r-- | snprintfv/stream.in | 96 | ||||
-rw-r--r-- | snprintfv/stream.stamp | 0 |
22 files changed, 6595 insertions, 0 deletions
diff --git a/snprintfv/AUTHORS b/snprintfv/AUTHORS new file mode 100644 index 0000000..d17456b --- /dev/null +++ b/snprintfv/AUTHORS @@ -0,0 +1,8 @@ +Authors of libsnprintfv. See individual files for their licenses. + +Gary V. Vaughan <gary@gnu.org>: + Designed, implemented and maintained libsnprintfv up to the 0.98h release. + +Paolo Bonzini <bonzini@gnu.org> and +Bruce Korb <bkorb@gnu.org>: + Are currently maintaining libsnprintfv (starting from Summer 2002). diff --git a/snprintfv/Makefile.am b/snprintfv/Makefile.am new file mode 100644 index 0000000..a86859a --- /dev/null +++ b/snprintfv/Makefile.am @@ -0,0 +1,48 @@ +## -*- Mode: Makefile -*- +## --------------------------------------------------------------------- +## Makefile.am -- process this file with automake to produce Makefile.in +## Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan +## Originally by Gary V. Vaughan, 1998 +## This file is part of Snprintfv +## +## Snprintfv is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Snprintfv program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## +## As a special exception to the GNU General Public License, if you +## distribute this file as part of a program that also links with and +## uses the libopts library from AutoGen, you may include it under +## the same distribution terms used by the libopts library. + +## Code: + +INCLUDES = -I$(top_srcdir) +noinst_HEADERS = mem.h filament.h stream.h printf.h compat.h +dist_noinst_DATA = filament.stamp stream.stamp printf.stamp +noinst_LTLIBRARIES = libsnprintfv.la +libsnprintfv_la_LDFLAGS = -no-undefined +CSRC = filament.c format.c printf.c mem.c stream.c custom.c + +nodist_libsnprintfv_la_SOURCES = snv.c + +# These files are the raw sources used to generate similarly named +# header files after extracting the prototypes from the sources +# +EXTRA_DIST = filament.in printf.in stream.in $(CSRC) + +snv.c : $(CSRC) + for f in $(CSRC) ; do echo "#include \"$$f\"" ; done > $@ + +.NOTPARALLEL: + +# Makefile.am ends here diff --git a/snprintfv/Makefile.in b/snprintfv/Makefile.in new file mode 100644 index 0000000..23b6acb --- /dev/null +++ b/snprintfv/Makefile.in @@ -0,0 +1,590 @@ +# Makefile.in generated by automake 1.12.2 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2012 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = snprintfv +DIST_COMMON = README $(dist_noinst_DATA) $(noinst_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/config/depcomp AUTHORS THANKS +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/config/ag_macros.m4 \ + $(top_srcdir)/config/extensions.m4 \ + $(top_srcdir)/config/libopts.m4 \ + $(top_srcdir)/config/libtool.m4 \ + $(top_srcdir)/config/ltoptions.m4 \ + $(top_srcdir)/config/ltsugar.m4 \ + $(top_srcdir)/config/ltversion.m4 \ + $(top_srcdir)/config/lt~obsolete.m4 \ + $(top_srcdir)/config/onceonly.m4 \ + $(top_srcdir)/config/snprintfv.m4 \ + $(top_srcdir)/config/unlocked-io.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libsnprintfv_la_LIBADD = +nodist_libsnprintfv_la_OBJECTS = snv.lo +libsnprintfv_la_OBJECTS = $(nodist_libsnprintfv_la_OBJECTS) +libsnprintfv_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libsnprintfv_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(nodist_libsnprintfv_la_SOURCES) +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(dist_noinst_DATA) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AGEN5_TESTS = @AGEN5_TESTS@ +AG_GUILE = @AG_GUILE@ +AG_LDFLAGS = @AG_LDFLAGS@ +AG_MAJOR_VERSION = @AG_MAJOR_VERSION@ +AG_MINOR_VERSION = @AG_MINOR_VERSION@ +AG_TIMEOUT = @AG_TIMEOUT@ +AG_VERSION = @AG_VERSION@ +AG_XML2 = @AG_XML2@ +AGexe = @AGexe@ +AGnam = @AGnam@ +AMTAR = @AMTAR@ +AO_AGE = @AO_AGE@ +AO_CURRENT = @AO_CURRENT@ +AO_REVISION = @AO_REVISION@ +AO_TEMPLATE_VERSION = @AO_TEMPLATE_VERSION@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CLexe = @CLexe@ +CLnam = @CLnam@ +CONFIG_SHELL = @CONFIG_SHELL@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUG_ENABLED = @DEBUG_ENABLED@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DYNAMIC_AG = @DYNAMIC_AG@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_STATIC = @ENABLE_STATIC@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GDexe = @GDexe@ +GDnam = @GDnam@ +GO_AGE = @GO_AGE@ +GO_CURRENT = @GO_CURRENT@ +GO_REVISION = @GO_REVISION@ +GREP = @GREP@ +GUILE_VERSION = @GUILE_VERSION@ +INCLIST = @INCLIST@ +INCSNPRINTFV = @INCSNPRINTFV@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBGUILE_CFLAGS = @LIBGUILE_CFLAGS@ +LIBGUILE_LIBS = @LIBGUILE_LIBS@ +LIBGUILE_PATH = @LIBGUILE_PATH@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSNPRINTFV = @LIBSNPRINTFV@ +LIBTOOL = @LIBTOOL@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIBXML2_PATH = @LIBXML2_PATH@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +M4_SRC = @M4_SRC@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPTS_TESTDIR = @OPTS_TESTDIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSIX_SHELL = @POSIX_SHELL@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +TEXI2HTML = @TEXI2HTML@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_aux_dir = @ac_aux_dir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I$(top_srcdir) +noinst_HEADERS = mem.h filament.h stream.h printf.h compat.h +dist_noinst_DATA = filament.stamp stream.stamp printf.stamp +noinst_LTLIBRARIES = libsnprintfv.la +libsnprintfv_la_LDFLAGS = -no-undefined +CSRC = filament.c format.c printf.c mem.c stream.c custom.c +nodist_libsnprintfv_la_SOURCES = snv.c + +# These files are the raw sources used to generate similarly named +# header files after extracting the prototypes from the sources +# +EXTRA_DIST = filament.in printf.in stream.in $(CSRC) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu snprintfv/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu snprintfv/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +libsnprintfv.la: $(libsnprintfv_la_OBJECTS) $(libsnprintfv_la_DEPENDENCIES) $(EXTRA_libsnprintfv_la_DEPENDENCIES) + $(libsnprintfv_la_LINK) $(libsnprintfv_la_OBJECTS) $(libsnprintfv_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snv.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +cscopelist: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am + + +snv.c : $(CSRC) + for f in $(CSRC) ; do echo "#include \"$$f\"" ; done > $@ + +.NOTPARALLEL: + +# Makefile.am ends here + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/snprintfv/README b/snprintfv/README new file mode 100644 index 0000000..9251106 --- /dev/null +++ b/snprintfv/README @@ -0,0 +1,30 @@ +This is libsnprintfv, a portable, extensible reimplementation of the +POSIX format printing API. libsnprintfv provides all the features +which should be present in a POSIX format printing implementation, +but which often are not, such as guaranteed return of number of +characters printed and support for %n$ format specifiers. + +In addition the the POSIX features, libsnprintfv also provides some +extensions to the API, and a GNU glibc-2 compatible printf custom +format specifier, all of which you can use with impunity if you link +with libsnprintfv, rather than worrying about whether the target C +library provides the extensions. See the info manual for details of +the API calls available, and an explanation of how to write custom +specifier handlers. + +The latest version of libsnprintfv is available from the author's +homepage: http://www.oranda.demon.co.uk. + +libsnprintfv is written in a very portable K&R compatible style, and +should build anywhere that provides a reasonable C compiler and runtime. +See the file INSTALL for instructions on how to build and install +libsnprintfv. + +See the file NEWS for a description of user visible changes to +libsnprintfv between releases. + +See the file TODO for a list of outstanding work. + +If you have any suggestions or bug reports, please send email to the +author at <gary@gnu.org>. + diff --git a/snprintfv/THANKS b/snprintfv/THANKS new file mode 100644 index 0000000..0480bbe --- /dev/null +++ b/snprintfv/THANKS @@ -0,0 +1,13 @@ +libsnprintfv would not be what it is without the invaluable help of +these people: + +Everybody who was kind enough to spend time testing libsnprintfv, +use it in their packages and report bugs. + +The following people made especially gracious contributions of their +time and energy in helping to track down bugs, port to new systems, +and generally assist in the maintainership process: + +Bruce Korb <bkorb@gnu.org> +Kaveh R. Ghazi <ghazi@caip.rutgers.edu> +Robert Lipe <robertlipe@usa.net> diff --git a/snprintfv/compat.h b/snprintfv/compat.h new file mode 100644 index 0000000..0145b97 --- /dev/null +++ b/snprintfv/compat.h @@ -0,0 +1,315 @@ +/* -*- Mode: C -*- + * -------------------------------------------------------------------- + * compat.h.in --- verbose but portable cpp defines for snprintfv + * Copyright (C) 1999 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1999 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + * + * Code: */ + +#ifndef SNPRINTFV_COMPAT_H +#define SNPRINTFV_COMPAT_H 1 + +#define _GNU_SOURCE 1 /* for strsignal in GNU's libc */ +#define __USE_GNU 1 /* exact same thing as above */ +#define __EXTENSIONS__ 1 /* and another way to call for it */ + +#ifdef __cplusplus +extern "C" { +#define SNV_END_EXTERN_C } +#else +#define SNV_END_EXTERN_C +#endif /* __cplusplus */ + +#define NO_FLOAT_PRINTING + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#ifdef HAVE_ERRNO_H +# include <errno.h> +# ifndef errno + /* Some sytems #define this! */ + extern int errno; +# endif +#endif + +#if defined( HAVE_LIMITS_H ) +# include <limits.h> + +#elif defined( HAVE_SYS_LIMITS_H ) +# include <sys/limits.h> + +#elif defined( HAVE_VALUES_H ) +# ifndef MAXINT +# include <values.h> +# endif /* MAXINT */ +#endif + +#if defined( HAVE_STRING_H ) +# include <string.h> + +#elif defined( HAVE_STRINGS_H ) +# include <strings.h> +#endif + +#if defined( HAVE_MEMORY_H ) +# include <memory.h> +#endif + +#if defined( HAVE_INTTYPES_H ) +# include <inttypes.h> + +#elif defined( HAVE_STDINT_H ) +# include <stdint.h> +#endif + +#ifndef HAVE_UINTMAX_T +# if defined( HAVE_LONG_LONG ) + typedef long long intmax_t; + typedef unsigned long long uintmax_t; +# else + typedef long intmax_t; + typedef unsigned long uintmax_t; +# endif +#endif + +#if defined( HAVE_STDARG_H ) +# include <stdarg.h> +# ifndef VA_START +# define VA_START(a, f) va_start(a, f) +# define VA_END(a) va_end(a) +# endif /* VA_START */ +# define SNV_USING_STDARG_H +#elif defined( HAVE_VARARGS_H ) +# include <varargs.h> +# ifndef VA_START +# define VA_START(a, f) va_start(a) +# define VA_END(a) va_end(a) +# endif /* VA_START */ +# undef SNV_USING_STDARG_H +#else +# include "must-have-stdarg-or-varargs" +#endif + +#if HAVE_RUNETYPE_H +# include <runetype.h> +#endif + +#ifdef HAVE_WCHAR_H +# include <wchar.h> +#endif + +#ifdef HAVE_WCHAR_T +typedef wchar_t snv_wchar_t; +#else +typedef int snv_wchar_t; +#endif + +#ifdef HAVE_WINT_T +typedef wint_t snv_wint_t; +#else +typedef int snv_wint_t; +#endif + +/* inline and const keywords are (mostly) handled by config.h */ +#ifdef __GNUC__ +# ifndef const +# define const __const +# endif +# ifndef inline +# define inline __inline +# endif +# ifndef signed +# define signed __signed +# endif +#else +# ifndef __STDC__ +# undef signed +# define signed +# endif +#endif + +#ifdef __STDC__ +# define _SNV_STR(x) #x + typedef void *snv_pointer; + typedef const void *snv_constpointer; +#else +# define _SNV_STR(x) "x" + typedef char *snv_pointer; + typedef char *snv_constpointer; +#endif + +#if defined(HAVE_FPUTC_UNLOCKED) && defined(HAVE_FLOCKFILE) +# define SNV_FPUTC_UNLOCKED fputc_unlocked +# define SNV_PUTC_UNLOCKED putc_unlocked +# define SNV_WITH_LOCKED_FP(fp, tmp_var) \ + for (flockfile (fp), tmp_var = 1; \ + tmp_var--; funlockfile (fp)) +#else +# define SNV_FPUTC_UNLOCKED fputc +# define SNV_PUTC_UNLOCKED putc +# define SNV_WITH_LOCKED_FP(fp, tmp_var) \ + for (tmp_var = 1; tmp_var--; ) +#endif + +/* + * Define macros for storing integers inside pointers. + * Be aware that it is only safe to use these macros to store `int' + * values in `char*' (or `void*') words, and then extract them later. + * Although it will work the other way round on many common + * architectures, it is not portable to assume a `char*' can be + * stored in an `int' and extracted later without loss of the msb's + */ +#define SNV_POINTER_TO_LONG(p) ((long)(p)) +#define SNV_POINTER_TO_ULONG(p) ((unsigned long)(p)) +#define SNV_LONG_TO_POINTER(i) ((snv_pointer)(long)(i)) +#define SNV_ULONG_TO_POINTER(u) ((snv_pointer)(unsigned long)(u)) + +#ifdef HAVE_STDBOOL_H +#include <stdbool.h> +#else +typedef enum { + false = 0, + true = 1 +} bool; +#endif + +#ifdef __CYGWIN32__ +# ifndef __CYGWIN__ +# define __CYGWIN__ +# endif +#endif +#ifdef __CYGWIN__ +# ifndef _WIN32 +# define _WIN32 +# endif +#endif + +#ifndef PARAMS +# define PARAMS(args) args +#endif + +#undef SNV_STMT_START +#undef SNV_STMT_END +#if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus) +# define SNV_STMT_START (void)( +# define SNV_STMT_END ) + +#elif (defined (sun) || defined (__sun__)) +# define SNV_STMT_START if (1) +# define SNV_STMT_END else (void)0 + +#else +# define SNV_STMT_START do +# define SNV_STMT_END while (false) +#endif + +#ifdef _WIN32 +# ifdef DLL_EXPORT +# define SNV_SCOPE extern __declspec(dllexport) +# else +# ifdef LIBSNPRINTFV_DLL_IMPORT +# define SNV_SCOPE extern __declspec(dllimport) +# endif +# endif +#endif +#ifndef SNV_SCOPE +# define SNV_SCOPE extern +#endif + +#undef SNV_GNUC_PRINTF +#undef SNV_GNUC_NORETURN +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define SNV_GNUC_PRINTF( args, format_idx, arg_idx ) \ + args __attribute__((format (printf, format_idx, arg_idx))) +# define SNV_GNUC_NORETURN \ + __attribute__((noreturn)) +# define SNV_ASSERT_FCN " (", __PRETTY_FUNCTION__, ")" +#else /* !__GNUC__ */ +# define SNV_GNUC_PRINTF( args, format_idx, arg_idx ) args +# define SNV_GNUC_NORETURN +# define SNV_ASSERT_FCN "", "", "" +#endif /* !__GNUC__ */ + +#define SNV_ASSERT_FMT "file %s: line %d%s%s%s: assertion \"%s\" failed.\n" + +#define snv_assert(expr) snv_fassert(stderr, expr) +#define snv_fassert(stream, expr) SNV_STMT_START { \ + if (!(expr)) { \ + fprintf (stream, SNV_ASSERT_FMT, __FILE__, __LINE__, \ + SNV_ASSERT_FCN, _SNV_STR(expr)); \ + exit(EXIT_FAILURE); \ + }; } SNV_STMT_END + +#define return_if_fail(expr) freturn_if_fail(stderr, expr) +#define freturn_if_fail(expr) SNV_STMT_START { \ + if (!(expr)) { \ + fprintf (stream, SNV_ASSERT_FMT, __FILE__, __LINE__, \ + SNV_ASSERT_FCN, _SNV_STR(expr)); \ + return; \ + }; } SNV_STMT_END + +#define return_val_if_fail(expr, val) freturn_val_if_fail(stderr, expr, val) +#define freturn_val_if_fail(stream, expr, val) SNV_STMT_START { \ + if (!(expr)) { \ + fprintf (stream, SNV_ASSERT_FMT, __FILE__, __LINE__, \ + SNV_ASSERT_FCN, _SNV_STR(expr)); \ + return val; \ + }; } SNV_STMT_END + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ABS +#define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + +typedef SNV_LONG_DOUBLE snv_long_double; + +#ifndef HAVE_STRTOUL +extern unsigned long +strtoul( const char *nptrm, char **endptr, register int base ); +#endif + +SNV_END_EXTERN_C +#endif /* SNPRINTFV_COMPAT_H */ + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/compat.h */ diff --git a/snprintfv/custom.c b/snprintfv/custom.c new file mode 100644 index 0000000..05793ea --- /dev/null +++ b/snprintfv/custom.c @@ -0,0 +1,182 @@ +/* -*- Mode: C -*- */ + +/* custom.c --- printf clone for argv arrays + * Copyright (C) 2003 Gary V. Vaughan + * Originally by Paolo Bonzini, 2002 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef WITH_DMALLOC +# include <dmalloc.h> +#endif + +#include <stddef.h> + +#if HAVE_RUNETYPE_H +# include <runetype.h> +#endif + +#ifdef HAVE_WCHAR_H +# include <wchar.h> +#endif + +#include "printf.h" + + + +/** + * printf_generic_info: + * @pinfo: the current state information for the format + * string parser. + * @n: the number of available slots in the @argtypes array + * @argtypes: the pointer to the first slot to be filled by the + * function + * + * An example implementation of a %printf_arginfo_function, which + * takes the basic type from the type given in the %spec_entry + * and adds flags depending on what was parsed (e.g. %PA_FLAG_SHORT + * is %pparser->is_short and so on). + * + * Return value: + * Always 1. + */ +int +printf_generic_info (struct printf_info *const pinfo, size_t n, int *argtypes) +{ + int type = pinfo->type; + + if (!n) + return 1; + + if ((type & PA_TYPE_MASK) == PA_POINTER) + type |= PA_FLAG_UNSIGNED; + + if (pinfo->is_char) + type = PA_CHAR; + + if (pinfo->is_short) + type |= PA_FLAG_SHORT; + + if (pinfo->is_long) + type |= PA_FLAG_LONG; + + if (pinfo->is_long_double) + type |= PA_FLAG_LONG_LONG; + + argtypes[0] = type; + return 1; +} + + +/** + * printf_generic: + * @stream: the stream (possibly a struct printfv_stream appropriately + * cast) on which to write output. + * @pinfo: the current state information for the format string parser. + * @args: the pointer to the first argument to be read by the handler + * + * An example implementation of a %printf_function, used to provide easy + * access to justification, width and precision options. + * + * Return value: + * The number of characters output. + **/ +int +printf_generic (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args) +{ + int len = 0, count_or_errorcode = SNV_OK; + char *p = NULL; + + /* Used to interface to the custom function. */ + STREAM *out; + Filament *fil; + printf_function *user_func = (printf_function *) pinfo->extra; + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* Read these now to advance the argument pointer appropriately */ + if (pinfo->prec == -1) + pinfo->prec = 0; + + /* Check for valid pre-state. */ + if (pinfo->prec <= -1) + { + PRINTF_ERROR (pinfo, "invalid flags"); + return -1; + } + + /* Print to a stream using a user-supplied function. */ + fil = filnew (NULL, (size_t)0); + out = stream_new (fil, SNV_UNLIMITED, NULL, snv_filputc); + user_func (out, pinfo, args); + stream_delete (out); + len = fillen (fil); + p = fildelete (fil); + + /* Left pad to the width if the supplied argument is less than + the width specifier. */ + if (p != NULL && pinfo->prec && pinfo->prec < len) + len = pinfo->prec; + + if ((len < pinfo->width) && !pinfo->left) + { + int padwidth = pinfo->width - len; + while ((count_or_errorcode >= 0) && (count_or_errorcode < padwidth)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + } + + /* Fill the buffer with as many characters from the format argument + * as possible without overflowing or exceeding the precision. + */ + if ((count_or_errorcode >= 0) && (p != NULL)) + { + int mark = count_or_errorcode; + while ((count_or_errorcode >= 0) && *p != '\0' + && ((pinfo->prec == 0) || (count_or_errorcode - mark < len))) + SNV_EMIT (*p++, stream, count_or_errorcode); + } + + /* Right pad to the width if we still didn't reach the specified + * width and the left justify flag was set. + */ + if ((count_or_errorcode < pinfo->width) && pinfo->left) + while ((count_or_errorcode >= 0) + && (count_or_errorcode < pinfo->width)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Return the number of characters emitted. */ + return count_or_errorcode; +} + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/custom.c */ diff --git a/snprintfv/filament.c b/snprintfv/filament.c new file mode 100644 index 0000000..a4b138f --- /dev/null +++ b/snprintfv/filament.c @@ -0,0 +1,232 @@ +/* -*- Mode: C -*- */ + +/* filament.c --- a bit like a string, but different =)O| + * Copyright (C) 1999 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1999 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Commentary: + * + * Try to exploit usage patterns to optimise string handling, and + * as a happy consequence handle NUL's embedded in strings properly. + * + * o Since finding the length of a (long) string is time consuming and + * requires careful coding to cache the result in local scope: We + * keep count of the length of a Filament all the time, so finding the + * length is O(1) at the expense of a little bookkeeping while + * manipulating the Filament contents. + * + * o Constantly resizing a block of memory to hold a string is memory + * efficient, but slow: Filaments are only ever expanded in size, + * doubling at each step to minimise the number of times the block + * needs to be reallocated and the contents copied (this problem is + * especially poignant for very large strings). + * + * o Most strings tend to be either relatively small and short-lived, + * or else long-lived but growing in asymptotically in size: To + * care for the former case, Filaments start off with a modest static + * buffer for the string contents to avoid any mallocations (except + * the initial one to get the structure!); the latter case is handled + * gracefully by the resizing algorithm in the previous point. + * + * o Extracting a C-style NUL terminated string from the Filament is + * an extremely common operation: We ensure there is always a + * terminating NUL character after the last character in the string + * so that the conversion can be performed quickly. + * + * In summary, Filaments are good where you need to do a lot of length + * calculations with your strings and/or gradually append more text + * onto existing strings. Filaments are also an easy way to get 8-bit + * clean strings is a more lightweight approach isn't required. + * + * They probably don't buy much if you need to do insertions and partial + * deletions, but optimising for that is a whole other problem! + */ + +/* Code: */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef WITH_DMALLOC +# include <dmalloc.h> +#endif + +#include "mem.h" +#include "filament.h" + + + +/** + * filnew: constructor + * @init: address of the first byte to copy into the new object. + * @len: the number of bytes to copy into the new object. + * + * Create a new Filament object, initialised to hold a copy of the + * first @len bytes starting at address @init. If @init is NULL, or + * @len is 0 (or less), then the initialised Filament will return the + * empty string, "", if its value is queried. + * + * Return value: + * A newly created Filament object is returned. + **/ +Filament * +filnew (const char *const init, size_t len) +{ + Filament *new; + + new = snv_new (Filament, 1); + + new->value = new->buffer; + new->length = 0; + new->size = FILAMENT_BUFSIZ; + + return (init || len) ? filinit (new, init, len) : new; +} + +/** + * filinit: + * @fil: The Filament object to initialise. + * @init: address of the first byte to copy into the new object. + * @len: the number of bytes to copy into the new object. + * + * Initialise a Filament object to hold a copy of the first @len bytes + * starting at address @init. If @init is NULL, or @len is 0 (or less), + * then the Filament will be reset to hold the empty string, "". + * + * Return value: + * The initialised Filament object is returned. + **/ +Filament * +filinit (Filament *fil, const char *const init, size_t len) +{ + if (init == NULL || len < 1) + { + /* Recycle any dynamic memory assigned to the previous + contents of @fil, and point back into the static buffer. */ + if (fil->value != fil->buffer) + snv_delete (fil->value); + + fil->value = fil->buffer; + fil->length = 0; + fil->size = FILAMENT_BUFSIZ; + } + else + { + if (len < FILAMENT_BUFSIZ) + { + /* We have initialisation data which will easily fit into + the static buffer: recycle any memory already assigned + and initialise in the static buffer. */ + if (fil->value != fil->buffer) + { + snv_delete (fil->value); + fil->value = fil->buffer; + fil->size = FILAMENT_BUFSIZ; + } + } + else + { + /* If we get to here then we never try to shrink the already + allocated dynamic buffer (if any), we just leave it in + place all ready to expand into later... */ + fil_maybe_extend (fil, len, false); + } + + snv_assert (len < fil->size); + + fil->length = len; + memcpy (fil->value, init, len); + } + + return fil; +} + +/** + * fildelete: destructor + * @fil: The Filament object for recycling. + * + * The memory being used by @fil is recycled. + * + * Return value: + * The original contents of @fil are converted to a null terminated + * string which is returned, either to be freed itself or else used + * as a normal C string. The entire Filament contents are copied into + * this string including any embedded nulls. + **/ +char * +fildelete (Filament *fil) +{ + char *value; + + if (fil->value == fil->buffer) + { + value = memcpy (snv_new (char, 1 + fil->length), + fil->buffer, 1 + fil->length); + value[fil->length] = '\0'; + } + else + value = filval (fil); + + snv_delete (fil); + + return value; +} + +/** + * _fil_extend: + * @fil: The Filament object which may need more string space. + * @len: The length of the data to be stored in @fil. + * @copy: whether to copy data from the static buffer on reallocation. + * + * This function will will assign a bigger block of memory to @fil + * considering the space left in @fil and @len, the length required + * for the prospective contents. + */ +void +_fil_extend (Filament *fil, size_t len, bool copy) +{ + /* Usually we will simply double the amount of space previously + allocated, but if the extra data is larger than the current + size it *still* won't fit, so in that case we allocate enough + room plus some we leave the current free space to expand into. */ + fil->size += MAX (len, fil->size); + + if (fil->value == fil->buffer) + { + fil->value = snv_new (char, fil->size); + if (copy) + memcpy (fil->value, fil->buffer, fil->length); + } + else + fil->value = snv_renew (char, fil->value, fil->size); +} + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/filament.c */ diff --git a/snprintfv/filament.h b/snprintfv/filament.h new file mode 100644 index 0000000..c28be6b --- /dev/null +++ b/snprintfv/filament.h @@ -0,0 +1,257 @@ +/* -*- Mode: C -*- */ + +/* filament.h --- a bit like a string but different =)O| + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef FILAMENT_H +#define FILAMENT_H 1 + +#include <snprintfv/compat.h> + +#ifdef __cplusplus +extern "C" +{ +#if 0 +/* This brace is so that emacs can still indent properly: */ } +#endif +#endif /* __cplusplus */ + +#define FILAMENT_BUFSIZ (512 - sizeof(char *) - (2 * sizeof(size_t))) + +/** + * Filament: + * Opaque data type used to hold 8-bit clean dynamic strings which know + * their own length and resize themselves to avoid buffer overruns. + **/ +typedef struct filament Filament; + +struct filament +{ + char *value; /* pointer to the start of the string */ + size_t length; /* length of the string */ + size_t size; /* total memory allocated */ + char buffer[FILAMENT_BUFSIZ]; /* usually string == &buffer[0] */ +}; + +/** + * filnew: constructor + * @init: address of the first byte to copy into the new object. + * @len: the number of bytes to copy into the new object. + * + * Create a new Filament object, initialised to hold a copy of the + * first @len bytes starting at address @init. If @init is NULL, or + * @len is 0 (or less), then the initialised Filament will return the + * empty string, "", if its value is queried. + * + * Return value: + * A newly created Filament object is returned. + **/ +extern Filament * +filnew (const char *const init, size_t len); + +/** + * filinit: + * @fil: The Filament object to initialise. + * @init: address of the first byte to copy into the new object. + * @len: the number of bytes to copy into the new object. + * + * Initialise a Filament object to hold a copy of the first @len bytes + * starting at address @init. If @init is NULL, or @len is 0 (or less), + * then the Filament will be reset to hold the empty string, "". + * + * Return value: + * The initialised Filament object is returned. + **/ +extern Filament * +filinit (Filament *fil, const char *const init, size_t len); + +/** + * fildelete: destructor + * @fil: The Filament object for recycling. + * + * The memory being used by @fil is recycled. + * + * Return value: + * The original contents of @fil are converted to a null terminated + * string which is returned, either to be freed itself or else used + * as a normal C string. The entire Filament contents are copied into + * this string including any embedded nulls. + **/ +extern char * +fildelete (Filament *fil); + +/** + * _fil_extend: + * @fil: The Filament object which may need more string space. + * @len: The length of the data to be stored in @fil. + * @copy: whether to copy data from the static buffer on reallocation. + * + * This function will will assign a bigger block of memory to @fil + * considering the space left in @fil and @len, the length required + * for the prospective contents. + */ +extern void +_fil_extend (Filament *fil, size_t len, bool copy); + +#line 61 "filament.in" + +/* Save the overhead of a function call in the great majority of cases. */ +#define fil_maybe_extend(fil, len, copy) \ + (((len)>=(fil)->size) ? _fil_extend((fil), (len), (copy)) : (void)0) + +/** + * filval: + * @fil: The Filament object being queried. + * + * Return value: + * A pointer to the null terminated string held by the Filament + * object is returned. Since the @fil may contain embedded nulls, it + * is not entirely safe to use the strfoo() API to examine the contents + * of the return value. + **/ +SNV_INLINE char * +filval (Filament *fil) +{ + /* Because we have been careful to ensure there is always at least + one spare byte of allocated memory, it is safe to set it here. */ + fil->value[fil->length] = '\0'; + return (char *) (fil->value); +} + +/** + * fillen: + * @fil: The Filament object being queried. + * + * Return value: + * The length of @fil, including any embedded nulls, but excluding the + * terminating null, is returned. + **/ +SNV_INLINE size_t +fillen (Filament *fil) +{ + return fil->length; +} + +/** + * filelt: + * @fil: The Filament being queried. + * @n: A zero based index into @fil. + * + * This function looks for the @n'th element of @fil. + * + * Return value: + * If @n is an index inside the Filament @fil, then the character stored + * at that index cast to an int is returned, otherwise @n is outside + * this range and -1 is returned. + **/ +SNV_INLINE int +filelt (Filament *fil, ssize_t n) +{ + if ((n >= 0) && (n < (ssize_t)fil->length)) + return (int) fil->value[n]; + else + return -1; +} + +/** + * filncat: + * @fil: The destination Filament of the concatenation. + * @str: The address of the source bytes for concatenation. + * @n: The number of bytes to be copied from @str. + * + * @n bytes starting with the byte at address @str are destructively + * concatenated to @fil. If necessary, @fil is dynamically reallocated + * to make room for this operation. + * + * Return value: + * A pointer to the (not null terminated) string which is the result + * of this concatenation is returned. + **/ +SNV_INLINE char * +filncat (Filament *fil, const char *str, size_t n) +{ + fil_maybe_extend (fil, n + fil->length, true); + memcpy (fil->value + fil->length, str, n); + fil->length += n; + return fil->value; +} + +/** + * filcat: + * @fil: The destination Filament of the concatenation. + * @str: The address of the source bytes for concatenation. + * + * The bytes starting at address @str upto and including the first null + * byte encountered are destructively concatenated to @fil. If + * necessary @fil is dynamically reallocated to make room for this + * operation. + * + * Return value: + * A pointer to the (not null terminated) string which is the result + * of this concatenation is returned. + **/ +SNV_INLINE char * +filcat (Filament *fil, const char *str) +{ + size_t length = strlen (str); + return filncat (fil, str, length); +} + +/** + * filccat: + * @fil: The destination Filament of the concatenation. + * @c: The character to append to @fil. + * + * @c is destructively concatenated to @fil. If necessary, @fil is + * dynamically reallocated to make room for this operation. When used + * repeatedly this function is less efficient than %filncat, + * since it must check whether to extend the filament before each + * character is appended. + * + * Return value: + * A pointer to the (not null terminated) string which is the result + * of this concatenation is returned. + **/ +SNV_INLINE char * +filccat (Filament *fil, int c) +{ + fil_maybe_extend (fil, 1 + fil->length, true); + fil->value[fil->length++] = c; + return fil->value; +} + +#ifdef __cplusplus +#if 0 +/* This brace is so that emacs can still indent properly: */ +{ +#endif +} +#endif /* __cplusplus */ + +#endif /* FILAMENT_H */ + +/* filament.h ends here */ diff --git a/snprintfv/filament.in b/snprintfv/filament.in new file mode 100644 index 0000000..0fb8923 --- /dev/null +++ b/snprintfv/filament.in @@ -0,0 +1,197 @@ +/* -*- Mode: C -*- */ + +/* filament.h --- a bit like a string but different =)O| + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef FILAMENT_H +#define FILAMENT_H 1 + +#include <snprintfv/compat.h> + +#ifdef __cplusplus +extern "C" +{ +#if 0 +/* This brace is so that emacs can still indent properly: */ } +#endif +#endif /* __cplusplus */ + +#define FILAMENT_BUFSIZ (512 - sizeof(char *) - (2 * sizeof(size_t))) + +/** + * Filament: + * Opaque data type used to hold 8-bit clean dynamic strings which know + * their own length and resize themselves to avoid buffer overruns. + **/ +typedef struct filament Filament; + +struct filament +{ + char *value; /* pointer to the start of the string */ + size_t length; /* length of the string */ + size_t size; /* total memory allocated */ + char buffer[FILAMENT_BUFSIZ]; /* usually string == &buffer[0] */ +}; + +@protos filament.c + +/* Save the overhead of a function call in the great majority of cases. */ +#define fil_maybe_extend(fil, len, copy) \ + (((len)>=(fil)->size) ? _fil_extend((fil), (len), (copy)) : (void)0) + +/** + * filval: + * @fil: The Filament object being queried. + * + * Return value: + * A pointer to the null terminated string held by the Filament + * object is returned. Since the @fil may contain embedded nulls, it + * is not entirely safe to use the strfoo() API to examine the contents + * of the return value. + **/ +SNV_INLINE char * +filval (Filament *fil) +{ + /* Because we have been careful to ensure there is always at least + one spare byte of allocated memory, it is safe to set it here. */ + fil->value[fil->length] = '\0'; + return (char *) (fil->value); +} + +/** + * fillen: + * @fil: The Filament object being queried. + * + * Return value: + * The length of @fil, including any embedded nulls, but excluding the + * terminating null, is returned. + **/ +SNV_INLINE size_t +fillen (Filament *fil) +{ + return fil->length; +} + +/** + * filelt: + * @fil: The Filament being queried. + * @n: A zero based index into @fil. + * + * This function looks for the @n'th element of @fil. + * + * Return value: + * If @n is an index inside the Filament @fil, then the character stored + * at that index cast to an int is returned, otherwise @n is outside + * this range and -1 is returned. + **/ +SNV_INLINE int +filelt (Filament *fil, ssize_t n) +{ + if ((n >= 0) && (n < (ssize_t)fil->length)) + return (int) fil->value[n]; + else + return -1; +} + +/** + * filncat: + * @fil: The destination Filament of the concatenation. + * @str: The address of the source bytes for concatenation. + * @n: The number of bytes to be copied from @str. + * + * @n bytes starting with the byte at address @str are destructively + * concatenated to @fil. If necessary, @fil is dynamically reallocated + * to make room for this operation. + * + * Return value: + * A pointer to the (not null terminated) string which is the result + * of this concatenation is returned. + **/ +SNV_INLINE char * +filncat (Filament *fil, const char *str, size_t n) +{ + fil_maybe_extend (fil, n + fil->length, true); + memcpy (fil->value + fil->length, str, n); + fil->length += n; + return fil->value; +} + +/** + * filcat: + * @fil: The destination Filament of the concatenation. + * @str: The address of the source bytes for concatenation. + * + * The bytes starting at address @str upto and including the first null + * byte encountered are destructively concatenated to @fil. If + * necessary @fil is dynamically reallocated to make room for this + * operation. + * + * Return value: + * A pointer to the (not null terminated) string which is the result + * of this concatenation is returned. + **/ +SNV_INLINE char * +filcat (Filament *fil, const char *str) +{ + size_t length = strlen (str); + return filncat (fil, str, length); +} + +/** + * filccat: + * @fil: The destination Filament of the concatenation. + * @c: The character to append to @fil. + * + * @c is destructively concatenated to @fil. If necessary, @fil is + * dynamically reallocated to make room for this operation. When used + * repeatedly this function is less efficient than %filncat, + * since it must check whether to extend the filament before each + * character is appended. + * + * Return value: + * A pointer to the (not null terminated) string which is the result + * of this concatenation is returned. + **/ +SNV_INLINE char * +filccat (Filament *fil, int c) +{ + fil_maybe_extend (fil, 1 + fil->length, true); + fil->value[fil->length++] = c; + return fil->value; +} + +#ifdef __cplusplus +#if 0 +/* This brace is so that emacs can still indent properly: */ +{ +#endif +} +#endif /* __cplusplus */ + +#endif /* FILAMENT_H */ + +/* filament.h ends here */ diff --git a/snprintfv/filament.stamp b/snprintfv/filament.stamp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/snprintfv/filament.stamp diff --git a/snprintfv/format.c b/snprintfv/format.c new file mode 100644 index 0000000..bed8d17 --- /dev/null +++ b/snprintfv/format.c @@ -0,0 +1,1275 @@ +/* -*- Mode: C -*- */ + +/* format.c --- printf clone for argv arrays + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#include "compat.h" + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#else +#include <sys/limits.h> +#endif + +#ifdef WITH_DMALLOC +# include <dmalloc.h> +#endif + +#include <float.h> +#include <math.h> +#include <stddef.h> + +#if HAVE_RUNETYPE_H +# include <runetype.h> +#endif + +#ifdef HAVE_WCHAR_H +# include <wchar.h> +#endif + +#include "printf.h" + +#ifndef NO_FLOAT_PRINTING +# ifdef HAVE_LONG_DOUBLE +# ifndef HAVE_ISNANL +# define isnanl(x) ((x) != (x)) +# endif +# ifndef HAVE_ISINFL +# define isinfl(x) isnanl ((x) - (x)) +# endif +# ifndef HAVE_MODFL +static snv_long_double modfl (long double x, long double *exp); +# endif +# ifndef HAVE_COPYSIGNL +static snv_long_double copysignl (long double x, long double y); +# endif +# else +# ifdef HAVE_ISNAN +# define isnanl isnan +# else +# define isnanl(x) ((x) != (x)) +# endif +# ifdef HAVE_ISINF +# define isinfl isinf +# else +# define isinfl(x) isnanl ((x) - (x)) +# endif +# ifdef HAVE_COPYSIGN +# define copysignl copysign +# else +# define copysign(x, y) (((x) < 0.0 ^ (y) < 0.0) ? (x) * -1.0 : (x)); +# endif +# define modfl modf +# endif +#endif + + +static uintmax_t +fetch_uintmax (struct printf_info *pinfo, union printf_arg const *arg) +{ + if (pinfo->is_long_double) + return (uintmax_t) arg->pa_u_long_long_int; + + if (pinfo->is_long) + return (uintmax_t) arg->pa_u_long_int; + + if (pinfo->is_short) + return (uintmax_t) arg->pa_u_short_int; + + if (pinfo->is_char) + return (uintmax_t) arg->pa_char; + + return (uintmax_t) arg->pa_u_int; +} + +static intmax_t +fetch_intmax (struct printf_info *pinfo, union printf_arg const *arg) +{ + if (pinfo->is_long_double) + return (intmax_t) (signed long long) arg->pa_long_long_int; + + if (pinfo->is_long) + return (intmax_t) (signed long) arg->pa_long_int; + + if (pinfo->is_short) + return (intmax_t) (signed short) arg->pa_short_int; + + if (pinfo->is_char) + return (intmax_t) (signed char) arg->pa_char; + + return (intmax_t) (signed int) arg->pa_int; +} + +#ifndef NO_FLOAT_PRINTING +static snv_long_double +fetch_double (struct printf_info *pinfo, union printf_arg const *arg) +{ + if (pinfo->is_long_double) + return (snv_long_double) arg->pa_long_double; + else + return (snv_long_double) (arg->pa_double); +} +#endif + + +#ifndef NO_FLOAT_PRINTING + +/* These two routines are cleaned up version of the code in libio 2.95.3 + (actually I got it from the Attic, not from the released tarball). + The changes were mainly to share code between %f and %g (libio did + share some code between %e and %g), and to share code between the + %e and %f when invoked by %g. Support from infinities and NaNs comes + from the old snprintfv code. */ + +typedef struct { + int pfs_prec; + int fmtch; + int expcnt; + int gformat; + char * scan_back_pz; + char * out_pz; + char * start_pz; + char * pfs_end; + snv_long_double fract; + snv_long_double integer; + snv_long_double tmp; +} print_float_status_t; + +static char * +print_float_round (snv_long_double fract, int *exp, char *start, char *end, + char ch, int *signp) +{ + snv_long_double tmp; + if (fract) + (void) modfl (fract * 10, &tmp); + else + tmp = ch - '0'; + + if (tmp > 4) + for (;; --end) + { + if (*end == '.') + --end; + if (end == start) + { + if (exp) /* e/E; increment exponent */ + ++end, ++*exp; + + *end = '1'; + break; + } + if (++*end <= '9') + break; + *end = '0'; + } + + /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ + else if (*signp == '-') + for (;; --end) + { + if (*end == '.') + --end; + if (*end != '0') + break; + if (end == start) + *signp = 0; + } + return (start); +} + +static void +fiddle_precision (print_float_status_t * pfs) +{ + /* %e/%f/%#g add 0's for precision, others trim 0's */ + if (pfs->gformat && !pinfo->alt) + { + while (pfs->out_pz > pfs->start_pz && *--pfs->out_pz == '0'); + if (*pfs->out_pz != '.') + ++pfs->out_pz; + } + else + for (; pfs->pfs_prec--; *pfs->out_pz++ = '0'); +} + +static void +do_fformat (print_float_status_t * pfs) +{ + /* reverse integer into beginning of buffer */ + if (pfs->expcnt) + for (; ++pfs->scn_bk_pz < pfs->pfs_end; *pfs->out_pz++ = *pfs->scn_bk_pz); + else + *pfs->out_pz++ = '0'; + + /* If precision required or alternate flag set, add in a + decimal point. */ + if (pinfo->prec || pinfo->alt) + *pfs->out_pz++ = '.'; + + /* if requires more precision and some fraction left */ + if (pfs->fract) + { + if (pfs->pfs_prec) + { + /* For %g, if no integer part, don't count initial + zeros as significant digits. */ + do + { + pfs->fract = modfl (pfs->fract * 10, &pfs->tmp); + *pfs->out_pz++ = '0' + ((int) pfs->tmp); + } + while (!pfs->tmp && !pfs->expcnt && pfs->gformat); + + while (--pfs->pfs_prec && pfs->fract) + { + pfs->fract = modfl (pfs->fract * 10, &pfs->tmp); + *pfs->out_pz++ = '0' + ((int) pfs->tmp); + } + } + + if (pfs->fract) + pfs->start_pz = + print_float_round (pfs->fract, (int *) NULL, pfs->start_pz, + pfs->out_pz - 1, (char) 0, signp); + } + + fiddle_precision (pfp); +} + +static void +do_eformat (print_float_status_t * pfs) +{ + if (pfs->expcnt) + { + *pfs->out_pz++ = *++pfs->scn_bk_pz; + if (pinfo->prec || pinfo->alt) + *pfs->out_pz++ = '.'; + + /* if requires more precision and some integer left */ + for (; pfs->pfs_prec && ++pfs->scn_bk_pz < pfs->pfs_end; --pfs->pfs_prec) + *pfs->out_pz++ = *pfs->scn_bk_pz; + + /* if done precision and more of the integer component, + round using it; adjust fract so we don'pfs->out_pz re-round + later. */ + if (!pfs->pfs_prec && ++pfs->scn_bk_pz < pfs->pfs_end) + { + pfs->fract = 0; + pfs->start_pz = print_float_round ( + (snv_long_double) 0, &pfs->expcnt, pfs->start_pz, pfs->out_pz - 1, + *pfs->scn_bk_pz, signp); + } + + /* adjust expcnt for digit in front of decimal */ + --pfs->expcnt; + } + + /* until first fractional digit, decrement exponent */ + else if (pfs->fract) + { + /* adjust expcnt for digit in front of decimal */ + for (pfs->expcnt = -1;; --pfs->expcnt) + { + pfs->fract = modfl (pfs->fract * 10, &pfs->tmp); + if (pfs->tmp) + break; + } + *pfs->out_pz++ = '0' + ((int) pfs->tmp); + if (pinfo->prec || pinfo->alt) + *pfs->out_pz++ = '.'; + } + + else + { + *pfs->out_pz++ = '0'; + if (pinfo->prec || pinfo->alt) + *pfs->out_pz++ = '.'; + } + + /* if requires more precision and some fraction left */ + if (pfs->fract) + { + if (pfs->pfs_prec) + do + { + pfs->fract = modfl (pfs->fract * 10, &pfs->tmp); + *pfs->out_pz++ = '0' + ((int) pfs->tmp); + } + while (--pfs->pfs_prec && pfs->fract); + + if (pfs->fract) + pfs->start_pz = print_float_round ( + pfs->fract, &pfs->expcnt, pfs->start_pz, pfs->out_pz - 1, + (char) 0, signp); + } + + fiddle_precision (pfp); + + if (pfs.fmtch != 'e' && pfs.fmtch != 'E') + return; + + { + char expbuf[10]; + *pfs.out_pz++ = pfs.fmtch; + if (pfs.expcnt < 0) + { + pfs.expcnt = -pfs.expcnt; + *pfs.out_pz++ = '-'; + } + else + *pfs.out_pz++ = '+'; + + pfs.scn_bk_pz = expbuf; + do + *pfs.scn_bk_pz++ = '0' + (pfs.expcnt % 10); + while ((pfs.expcnt /= 10) > 9); + *pfs.scn_bk_pz++ = '0' + pfs.expcnt; + while (pfs.scn_bk_pz > expbuf) + *pfs.out_pz++ = *--pfs.scn_bk_pz; + } +} + +static void +do_gformat (print_float_status_t * pfs) +{ + pfs->gformat = 1; + + /* a precision of 0 is treated as a precision of 1. */ + if (!pfs->pfs_prec) + pinfo->prec = ++pfs->pfs_prec; + + /* ``The style used depends on the value converted; style e + will be used only if the exponent resulting from the + conversion is less than -4 or greater than the precision.'' + -- ANSI X3J11 */ + if ( (pfs->expcnt > pfs->pfs_prec) + || (!pfs->expcnt && pfs->fract && pfs->fract < .0001L)) + { + /* g/G format counts "significant digits, not digits of + precision; for the e/E format, this just causes an + off-by-one problem, i.e. g/G considers the digit + before the decimal point significant and e/E doesn't + count it as precision. */ + --pfs->pfs_prec; + pfs->fmtch -= 2; /* G->E, g->e */ + do_eformat (pfs); + } + else + { + /* Decrement precision */ + if (fnum != 0.0L) + pfs->pfs_prec -= (pfs->pfs_end - pfs->scn_bk_pz) - 1; + else + pfs->pfs_prec--; + + do_fformat (pfs); + } +} + +static int +print_float (struct printf_info *pinfo, char *startp, char *endp, int *signp, + snv_long_double fnum) +{ + print_float_status_t pfs = { + .pfs_prec = pinfo->prec, + .pfs_end = endp, + .fmtch = pinfo->spec, + .out_pz = startp, + .start_pz = startp, + .gformat = 0 + }; + + *signp = 0; + + /* Do the special cases: nans, infinities, zero, and negative numbers. */ + if (isnanl (fnum)) + { + /* Not-a-numbers are printed as a simple string. */ + *pfs.out_pz++ = pfs.fmtch < 'a' ? 'N' : 'n'; + *pfs.out_pz++ = pfs.fmtch < 'a' ? 'A' : 'a'; + *pfs.out_pz++ = pfs.fmtch < 'a' ? 'N' : 'n'; + return pfs.out_pz - pfs.start_pz; + } + + /* Zero and infinity also can have a sign in front of them. */ + if (copysignl (1.0, fnum) < 0.0) + { + fnum = -1.0 * fnum; + *signp = '-'; + } + + if (isinfl (fnum)) + { + /* Infinities are printed as a simple string. */ + *pfs.out_pz++ = pfs.fmtch < 'a' ? 'I' : 'i'; + *pfs.out_pz++ = pfs.fmtch < 'a' ? 'N' : 'n'; + *pfs.out_pz++ = pfs.fmtch < 'a' ? 'F' : 'f'; + goto set_signp; + } + + pfs.expcnt = 0; + pfs.fract = modfl (fnum, &pfs.integer); + + /* get an extra slot for rounding. */ + *pfs.out_pz++ = '0'; + + /* get integer portion of number; put into the end of the buffer; the + .01 is added for modfl (356.0 / 10, &integer) returning .59999999... */ + for (pfs.scn_bk_pz = pfs.pfs_end - 1; + pfs.scn_bk_pz >= pfs.start_pz && pfs.integer; + ++pfs.expcnt) + { + pfs.tmp = modfl (pfs.integer / 10, &pfs.integer); + *pfs.scn_bk_pz-- = '0' + ((int) ((pfs.tmp + .01L) * 10)); + } + + switch (pfs.fmtch) + { + case 'g': + case 'G': + do_gformat (&pfs); + break; + + case 'f': + case 'F': + do_fformat (&pfs); + break; + + case 'e': + case 'E': + do_eformat (&pfs); + break; + + default: + abort (); + } + +set_signp: + if (!*signp) + { + if (pinfo->showsign) + *signp = '+'; + else if (pinfo->space) + *signp = ' '; + } + + return (pfs.out_pz - pfs.start_pz); +} +#endif + + +static int +printf_flag_info (struct printf_info *const pinfo, size_t n, int *argtypes) +{ + return_val_if_fail (pinfo != NULL, SNV_ERROR); + (void)n; + (void)argtypes; + + if (!(pinfo->state & (SNV_STATE_BEGIN | SNV_STATE_FLAG))) + { + PRINTF_ERROR (pinfo, "invalid specifier"); + return -1; + } + + pinfo->state = SNV_STATE_FLAG; + + while (pinfo->state & SNV_STATE_FLAG) + { + switch (*pinfo->format) + { + case '#': + pinfo->alt = true; + pinfo->format++; + break; + + case '0': + if (!pinfo->left) + pinfo->pad = '0'; + pinfo->format++; + break; + + case '-': + pinfo->pad = ' '; + pinfo->left = true; + pinfo->format++; + break; + + case ' ': + pinfo->space = true; + pinfo->format++; + break; + + case '+': + pinfo->showsign = true; + pinfo->format++; + break; + + case '\'': + pinfo->group = true; + pinfo->format++; + break; + + default: + pinfo->state = ~(SNV_STATE_BEGIN | SNV_STATE_FLAG); + break; + } + } + + pinfo->format--; + + /* Return the number of characters emitted. */ + return 0; +} + +/* This function has considerably more freedom than the others in + playing with pinfo; in particular, it modifies argindex and can + return completely bogus values whose only purpose is to extend + the argtypes vector so that it has enough items for the positional + parameter of the width (in the *n$ case). It also expects that + argtypes = (base of argtypes vector) + pinfo->argindex. + + This is messy, suggestion for simplifying it are gladly accepted. */ +static int +printf_numeric_param_info (struct printf_info *const pinfo, size_t n, int *argtypes) +{ + const char *pEnd = NULL; + int found = 0, allowed_states, new_state; + int position = 0, skipped_args = 0; + long value; + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* If we are looking at a ``.'', then this is a precision parameter. */ + if (*pinfo->format == '.') + { + pinfo->format++; + found |= 1; + } + + /* First we might have a ``*''. */ + if (*pinfo->format == '*') + { + pinfo->format++; + found |= 2; + } + + /* Parse the number. */ + for (pEnd = pinfo->format, value = 0; *pEnd >= '0' && *pEnd <= '9'; pEnd++) + value = value * 10 + (*pEnd - '0'); + + if (pEnd > pinfo->format) + { + pinfo->format = pEnd; + found |= 4; + } + + if (value > INT_MAX) + { + PRINTF_ERROR (pinfo, "out of range"); + return -1; + } + + /* And finally a dollar sign. */ + if (*pinfo->format == '$') + { + if (value == 0) + { + PRINTF_ERROR (pinfo, "invalid position specifier"); + return -1; + } + + position = value; + pinfo->format++; + found |= 8; + } + + switch (found & 14) + { + /* We found a * specification */ + case 2: + if (pinfo->args) + value = pinfo->args[pinfo->argindex].pa_int; + if (n) + argtypes[0] = PA_INT; + pinfo->argindex++; + skipped_args = 1; + found ^= 6; + break; + + /* We found a *n$ specification */ + case 14: + if (n + pinfo->argindex > (unsigned)position - 1) + argtypes[position - 1 - pinfo->argindex] = PA_INT; + + /* Else there is not enough space, reallocate and retry please... + ... but we must say how much to skip. */ + if (position >= pinfo->argindex) + skipped_args = position - pinfo->argindex; + + if (pinfo->args) + value = pinfo->args[position - 1].pa_int; + found ^= 10; + break; + } + + switch (found) + { + /* We must have read a width specification. */ + case 4: + allowed_states = SNV_STATE_BEGIN | SNV_STATE_WIDTH; + new_state = ~(SNV_STATE_BEGIN | SNV_STATE_FLAG | SNV_STATE_WIDTH); + + /* How awful... */ + if (value < 0) + { + pinfo->pad = ' '; + pinfo->left = true; + value = -value; + } + + pinfo->width = value; + break; + + /* We must have read a precision specification. */ + case 5: + allowed_states = SNV_STATE_PRECISION | SNV_STATE_BEGIN; + new_state = SNV_STATE_MODIFIER | SNV_STATE_SPECIFIER; + pinfo->prec = value; + break; + + /* We must have read a position specification. */ + case 12: + allowed_states = SNV_STATE_BEGIN; + new_state = ~SNV_STATE_BEGIN; + pinfo->dollar = position; + break; + + /* We must have read something bogus. */ + default: + PRINTF_ERROR (pinfo, "invalid specifier"); + return -1; + } + + if (!(pinfo->state & allowed_states)) + { + PRINTF_ERROR (pinfo, "invalid specifier"); + return -1; + } + + pinfo->state = new_state; + pinfo->format--; + return skipped_args; +} + +static int +printf_modifier_info (struct printf_info *const pinfo, size_t n, int *argtypes) +{ + return_val_if_fail (pinfo != NULL, SNV_ERROR); + (void)n; + (void)argtypes; + + /* Check for valid pre-state. */ + if (!(pinfo->state & (SNV_STATE_BEGIN | SNV_STATE_MODIFIER))) + { + PRINTF_ERROR (pinfo, "out of range"); + return -1; + } + + while (pinfo->state != SNV_STATE_SPECIFIER) + { + switch (*pinfo->format) + { + case 'h': + if (*++pinfo->format != 'h') + { + pinfo->is_short = true; + break; + } + + pinfo->is_char = true; + pinfo->format++; + break; + + case 'z': + if (sizeof (size_t) > sizeof (char *)) + pinfo->is_long_double = true; + else + pinfo->is_long = true; + + pinfo->format++; + break; + + case 't': + if (sizeof (ptrdiff_t) > sizeof (char *)) + pinfo->is_long_double = true; + else + pinfo->is_long = true; + + pinfo->format++; + break; + + case 'l': + if (*++pinfo->format != 'l') + { + pinfo->is_long = true; + break; + } + /*FALLTHROUGH*/ + + case 'j': + case 'q': + case 'L': + pinfo->is_long_double = true; + pinfo->format++; + break; + + default: + pinfo->state = SNV_STATE_SPECIFIER; + pinfo->format--; + break; + } + } + + /* Return the number of characters emitted. */ + return 0; +} + + +static int +printf_char (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args) +{ + int count_or_errorcode = SNV_OK; + char ch = '\0'; + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* Check for valid pre-state. */ + if (pinfo->prec != -1 + || pinfo->is_char || pinfo->is_short || pinfo->is_long + || pinfo->is_long_double || pinfo->pad == '0' + || pinfo->alt || pinfo->space || pinfo->showsign) + { + PRINTF_ERROR (pinfo, "invalid flags"); + return -1; + } + + /* Extract the correct argument from the arg vector. */ + ch = args->pa_char; + + /* Left pad to the width if the supplied argument is less than + the width specifier. */ + if ((pinfo->width > 1) && !pinfo->left) + { + int padwidth = pinfo->width - 1; + + while ((count_or_errorcode >= 0) && (count_or_errorcode < padwidth)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + } + + /* Emit the character argument. */ + SNV_EMIT (ch, stream, count_or_errorcode); + + /* Right pad to the width if we still didn't reach the specified + width and the left justify flag was set. */ + if ((count_or_errorcode < pinfo->width) && pinfo->left) + while ((count_or_errorcode >= 0) + && (count_or_errorcode < pinfo->width)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Return the number of characters emitted. */ + return count_or_errorcode; +} + +#ifndef NO_FLOAT_PRINTING + +static int +printf_float (STREAM *stream, + struct printf_info *const pinfo, + union printf_arg const *args) +{ + snv_long_double value = 0.0; + int sign, len, count_or_errorcode = SNV_OK; +#ifdef HAVE_LONG_DOUBLE + char buffer[LDBL_MAX_10_EXP * 2 + 20], *p = buffer; +#else + char buffer[DBL_MAX_10_EXP * 2 + 20], *p = buffer; +#endif + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* Check for valid pre-state */ + if (pinfo->prec == -1) + pinfo->prec = SNV_POINTER_TO_LONG (pinfo->extra); + + /* Check for valid pre-state. */ + if (pinfo->prec <= -1 + || pinfo->is_char || pinfo->is_short || pinfo->is_long) + { + PRINTF_ERROR (pinfo, "invalid flags"); + return -1; + } + + /* Extract the correct argument from the arg vector. */ + value = fetch_double (pinfo, args); + + /* Convert the number into a string. */ + len = print_float (pinfo, buffer, buffer + sizeof (buffer), &sign, value); + if (*buffer == '0') + p++, len--; + + /* Compute the size of the padding. */ + pinfo->width -= len; + if (sign) + pinfo->width--; + + /* Left pad to the remaining width if the supplied argument is less + than the width specifier, and the padding character is ' '. */ + if (pinfo->pad == ' ' && !pinfo->left) + while ((count_or_errorcode >= 0) && (pinfo->width-- > 0)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Display any sign character. */ + if (count_or_errorcode >= 0 && sign) + SNV_EMIT (sign, stream, count_or_errorcode); + + /* Left pad to the remaining width if the supplied argument is less + than the width specifier, and the padding character is not ' '. */ + if (pinfo->pad != ' ' && !pinfo->left) + while ((count_or_errorcode >= 0) && (pinfo->width-- > 0)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Fill the stream buffer with as many characters from the number + buffer as possible without overflowing. */ + while ((count_or_errorcode >= 0) && (len-- > 0)) + SNV_EMIT (*p++, stream, count_or_errorcode); + + /* Right pad to the width if we still didn't reach the specified + width and the left justify flag was set. */ + if (pinfo->left) + while ((count_or_errorcode >= 0) && (pinfo->width-- > 0)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Return the number of characters emitted. */ + return count_or_errorcode; +} +#endif + +static int +printf_count (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args) +{ + (void)stream; + + if (pinfo->is_char) + *(char *) (args->pa_pointer) = pinfo->count; + + else if (pinfo->is_short) + *(short *) (args->pa_pointer) = pinfo->count; + + else if (pinfo->is_long) + *(long *) (args->pa_pointer) = pinfo->count; + + else if (pinfo->is_long_double) + *(intmax_t *) (args->pa_pointer) = pinfo->count; + + else + *(int *) (args->pa_pointer) = pinfo->count; + + return 0; +} + +static int +printf_integer (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args) +{ + static const char digits_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static const char digits_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *digits; + + unsigned base = SNV_POINTER_TO_ULONG (pinfo->extra); + uintmax_t value = 0L; + int type, count_or_errorcode = SNV_OK; + char buffer[256], *p, *end; + bool is_negative = false; + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* Check for valid pre-state. */ + if (!(pinfo->state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (pinfo, "out of range"); + return -1; + } + + /* Upper or lower-case hex conversion? */ + digits = ((pinfo->spec >= 'a') && (pinfo->spec <= 'z')) + ? digits_lower : digits_upper; + + if (pinfo->prec == -1) + pinfo->prec = 0; + + /* Check for valid pre-state. */ + if (pinfo->prec < 0) + { + PRINTF_ERROR (pinfo, "invalid precision"); + return -1; + } + + type = pinfo->type; + + /* Extract the correct argument from the arg vector. */ + if (type & PA_FLAG_UNSIGNED) + { + value = fetch_uintmax (pinfo, args); + is_negative = false; + pinfo->showsign = pinfo->space = false; + } + else + { + intmax_t svalue = 0L; + svalue = fetch_intmax (pinfo, args); + is_negative = (svalue < 0); + value = (uintmax_t) ABS (svalue); + } + + /* Convert the number into a string. */ + p = end = &buffer[sizeof (buffer) - 1]; + + if (value == 0) + *p-- = '0'; + + else + while (value > 0) + { + *p-- = digits[value % base]; + value /= base; + } + + pinfo->width -= end - p; + pinfo->prec -= end - p; + + /* Octal numbers have a leading zero in alterate form. */ + if (pinfo->alt && base == 8) + { + *p-- = '0'; + --pinfo->width; + } + + /* Left pad with zeros to make up the precision. */ + if (pinfo->prec > 0) + { + pinfo->width -= pinfo->prec; + while (pinfo->prec-- > 0) + *p-- = '0'; + } + + /* Reserve room for leading `0x' for hexadecimal. */ + if (pinfo->alt && base == 16) + pinfo->width -= 2; + + /* Reserve room for a sign character. */ + if (is_negative || pinfo->showsign || pinfo->space) + --pinfo->width; + + /* Left pad to the remaining width if the supplied argument is less + * than the width specifier, and the padding character is ' '. + */ + if (pinfo->pad == ' ' && !pinfo->left) + while ((count_or_errorcode >= 0) && (pinfo->width-- > 0)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Display any sign character. */ + if (count_or_errorcode >= 0) + { + if (is_negative) + SNV_EMIT ('-', stream, count_or_errorcode); + else if (pinfo->showsign) + SNV_EMIT ('+', stream, count_or_errorcode); + else if (pinfo->space) + SNV_EMIT (' ', stream, count_or_errorcode); + } + + /* Display `0x' for alternate hexadecimal specifier. */ + if ((count_or_errorcode >= 0) && (base == 16) && pinfo->alt) + { + SNV_EMIT ('0', stream, count_or_errorcode); + SNV_EMIT (digits['X' - 'A' + 10], stream, count_or_errorcode); + } + + /* Left pad to the remaining width if the supplied argument is less + * than the width specifier, and the padding character is not ' '. + */ + if (pinfo->pad != ' ' && !pinfo->left) + while ((count_or_errorcode >= 0) && (pinfo->width-- > 0)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Fill the stream buffer with as many characters from the number + * buffer as possible without overflowing. + */ + while ((count_or_errorcode >= 0) && (++p < &buffer[sizeof (buffer)])) + SNV_EMIT (*p, stream, count_or_errorcode); + + /* Right pad to the width if we still didn't reach the specified + * width and the left justify flag was set. + */ + if (pinfo->left) + while ((count_or_errorcode >= 0) && (pinfo->width-- > 0)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Return the number of characters emitted. */ + return count_or_errorcode; +} + +static int +printf_pointer (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args) +{ + int count_or_errorcode = SNV_OK; + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* Read these now to advance the argument pointer appropriately */ + if (pinfo->prec == -1) + pinfo->prec = 0; + + /* Check for valid pre-state. */ + if (pinfo->prec <= -1 + || pinfo->is_char || pinfo->is_short || pinfo->is_long + || pinfo->is_long_double) + { + PRINTF_ERROR (pinfo, "invalid flags"); + return -1; + } + + /* Always print 0x. */ + pinfo->alt = 1; + pinfo->is_long = sizeof(long) == sizeof (char *); + pinfo->is_long_double = sizeof(intmax_t) == sizeof (char *); + + /* Use the standard routine for numbers for the printing call, + if the pointer is not NULL. */ + + if (args->pa_pointer != NULL) + return printf_integer (stream, pinfo, args); + + /* Print a NULL pointer as (nil), appropriately padded. */ + if ((pinfo->width > 5) && !pinfo->left) + { + int padwidth = pinfo->width - 5; + while ((count_or_errorcode >= 0) && (count_or_errorcode < padwidth)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + } + + SNV_EMIT ('(', stream, count_or_errorcode); + SNV_EMIT ('n', stream, count_or_errorcode); + SNV_EMIT ('i', stream, count_or_errorcode); + SNV_EMIT ('l', stream, count_or_errorcode); + SNV_EMIT (')', stream, count_or_errorcode); + + if ((pinfo->width > 5) && pinfo->left) + while ((count_or_errorcode >= 0) + && (count_or_errorcode < pinfo->width)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + return count_or_errorcode; +} + +static int +printf_string (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args) +{ + int len = 0, count_or_errorcode = SNV_OK; + const char *p = NULL; + + return_val_if_fail (pinfo != NULL, SNV_ERROR); + + /* Read these now to advance the argument pointer appropriately */ + if (pinfo->prec == -1) + pinfo->prec = 0; + + /* Check for valid pre-state. */ + if (pinfo->prec <= -1 + || pinfo->is_char || pinfo->is_short || pinfo->is_long + || pinfo->is_long_double) + { + PRINTF_ERROR (pinfo, "invalid flags"); + return -1; + } + + /* Extract the correct argument from the arg vector. */ + p = args->pa_string; + + /* Left pad to the width if the supplied argument is less than + the width specifier. */ + if (p != NULL) + { + len = strlen (p); + if (pinfo->prec && pinfo->prec < len) + len = pinfo->prec; + } + + if ((len < pinfo->width) && !pinfo->left) + { + int padwidth = pinfo->width - len; + while ((count_or_errorcode >= 0) && (count_or_errorcode < padwidth)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + } + + /* Fill the buffer with as many characters from the format argument + as possible without overflowing or exceeding the precision. */ + if ((count_or_errorcode >= 0) && (p != NULL)) + { + int mark = count_or_errorcode; + while ((count_or_errorcode >= 0) && *p != '\0' + && ((pinfo->prec == 0) || (count_or_errorcode - mark < len))) + SNV_EMIT (*p++, stream, count_or_errorcode); + } + + /* Right pad to the width if we still didn't reach the specified + width and the left justify flag was set. */ + if ((count_or_errorcode < pinfo->width) && pinfo->left) + while ((count_or_errorcode >= 0) + && (count_or_errorcode < pinfo->width)) + SNV_EMIT (pinfo->pad, stream, count_or_errorcode); + + /* Return the number of characters emitted. */ + return count_or_errorcode; +} + + + +/* replacements for modfl and copysignl follow. */ + +#if !defined NO_FLOAT_PRINTING && defined HAVE_LONG_DOUBLE +# ifndef HAVE_MODFL +static long double modfl (long double x, long double *exp) +{ + /* To compute the integer part of a positive integer (in this case + abs(X)), sum a big enough integer to the absolute value, so that + the precision of the floating point number is exactly 1. Then + we round towards zero. + + The code in the two branches is the same but it considers -x + if x is negative. */ + + long double z; + if (x < 0.0L) + { + z = 1.0L / LDBL_EPSILON - x - 1.0 / LDBL_EPSILON; + if (z + x > 0.0L) + z = z - 1.0L; + + return (*exp = -z) + x; + } + else + { + z = 1.0L / LDBL_EPSILON + x - 1.0 / LDBL_EPSILON; + if (z > x) + z = z - 1.0L; + + return x - (*exp = z); + } +} +# endif /* !HAVE_MODFL */ + +# ifndef HAVE_COPYSIGNL +long double +copysignl (long double x, long double y) +{ +# ifdef HAVE_COPYSIGN + return x * (long double) copysign (1.0, x * y); +# else /* !HAVE_COPYSIGN */ + /* If we do not have copysign, assume zero is unsigned (too risky to + assume we have infinities, which would allow to test with + (x < 0.0 && 1.0 / x < 0.0). */ + return (x < 0.0 ^ y < 0.0) ? x * -1.0 : x; +# endif /* !HAVE_COPYSIGN */ +} +# endif /* !HAVE_COPYSIGNL */ +#endif /* !NO_FLOAT_PRINTING && HAVE_LONG_DOUBLE) */ + + + +/* This is where the parsing of FORMAT strings is handled: + + Each of these functions should inspect PPARSER for parser + state information; update PPARSER as necessary based on + the state discovered; possibly put some characters in STREAM, in + which case that number of characters must be returned. If the + handler detects that parsing (of the current specifier) is complete, + then it must set pinfo->state to SNV_STATE_END. The library will then + copy characters from the format string to STREAM until another unescaped + SNV_CHAR_SPEC is detected when the handlers will be called again. */ + +spec_entry snv_default_spec_table[] = { + /* ch type function */ + {' ', 0, 0, NULL, printf_flag_info, NULL}, + {'#', 0, 0, NULL, printf_flag_info, NULL}, + {'+', 0, 0, NULL, printf_flag_info, NULL}, + {'-', 0, 0, NULL, printf_flag_info, NULL}, + {'\'', 0, 0, NULL, printf_flag_info, NULL}, + {'*', 0, PA_INT, NULL, printf_numeric_param_info, NULL}, + {'$', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'.', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'0', 0, 0, NULL, printf_flag_info, NULL}, + {'1', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'2', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'3', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'4', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'5', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'6', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'7', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'8', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'9', 0, 0, NULL, printf_numeric_param_info, NULL}, + {'c', 0, PA_CHAR, printf_char, NULL, NULL}, + {'d', 0, PA_INT, printf_integer, printf_generic_info, (snv_pointer) 10}, +#ifndef NO_FLOAT_PRINTING + {'e', 0, PA_DOUBLE, printf_float, printf_generic_info, (snv_pointer) 6}, + {'E', 0, PA_DOUBLE, printf_float, printf_generic_info, (snv_pointer) 6}, + {'f', 0, PA_DOUBLE, printf_float, printf_generic_info, (snv_pointer) 6}, + {'F', 0, PA_DOUBLE, printf_float, printf_generic_info, (snv_pointer) 6}, + {'g', 0, PA_DOUBLE, printf_float, printf_generic_info, (snv_pointer) 6}, + {'G', 0, PA_DOUBLE, printf_float, printf_generic_info, (snv_pointer) 6}, +#endif + {'h', 0, 0, NULL, printf_modifier_info, NULL}, + {'i', 0, PA_INT, printf_integer, printf_generic_info, (snv_pointer) 10}, + {'j', 0, 0, NULL, printf_modifier_info, NULL}, + {'l', 0, 0, NULL, printf_modifier_info, NULL}, + {'L', 0, 0, NULL, printf_modifier_info, NULL}, + {'n', 0, PA_INT | PA_FLAG_PTR, printf_count, printf_generic_info, NULL}, + {'o', 0, PA_INT | PA_FLAG_UNSIGNED, + printf_integer, printf_generic_info, (snv_pointer) 8}, + {'p', 0, PA_POINTER, printf_pointer, NULL, (snv_pointer) 16}, + {'q', 0, 0, NULL, printf_modifier_info, NULL}, + {'s', 0, PA_STRING, printf_string, NULL, NULL}, + {'t', 0, 0, NULL, printf_modifier_info, NULL}, + {'u', 0, PA_INT | PA_FLAG_UNSIGNED, + printf_integer, printf_generic_info, (snv_pointer) 10}, + {'x', 0, PA_INT | PA_FLAG_UNSIGNED, + printf_integer, printf_generic_info, (snv_pointer) 16}, + {'X', 0, PA_INT | PA_FLAG_UNSIGNED, + printf_integer, printf_generic_info, (snv_pointer) 16}, + {'z', 0, 0, NULL, printf_modifier_info, NULL}, + {'\0', 0, PA_LAST, NULL, NULL, NULL} +}; + +/* format.c ends here */ diff --git a/snprintfv/mem.c b/snprintfv/mem.c new file mode 100644 index 0000000..553492f --- /dev/null +++ b/snprintfv/mem.c @@ -0,0 +1,82 @@ +/* -*- Mode: C -*- */ + +/* mem.c --- memory management routines + * Copyright (C) 2002 Gary V. Vaughan + * Originally by Paolo Bonzini, 2002 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_DMALLOC +# include <dmalloc.h> +#endif + +#include "mem.h" + + +/* We deliberately don't prototype the malloc functions; they are cast + to match the function pointers we expose to avoid compiler warnings + from mismatched prototypes (if we find a host implementation. + + Not also that if this file is compiled -DWITH_DMALLOC, the inclusion + in mem.h will cause the malloc references below to be redirected + correctly. */ +malloc_proc_t* snv_malloc = (malloc_proc_t*)malloc; +realloc_proc_t* snv_realloc = (realloc_proc_t*)realloc; +free_proc_t* snv_free = (free_proc_t*)free; + +/* Unportable memory management functions are reimplemented tout court. */ +snv_pointer +snv_xrealloc (snv_pointer old, size_t count) +{ + if (count < 1) + { + snv_free (old); + return NULL; + } + if (!old) + return snv_malloc (count); + else + return snv_realloc (old, count); +} + +char * +snv_strdup (const char *str) +{ + size_t len = strlen (str); + char *result = snv_malloc (len + 1); + memcpy (result, str, len + 1); + return result; +} + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/mem.c */ diff --git a/snprintfv/mem.h b/snprintfv/mem.h new file mode 100644 index 0000000..496cf53 --- /dev/null +++ b/snprintfv/mem.h @@ -0,0 +1,120 @@ +/* -*- Mode: C -*- */ + +/* mem.h --- memory handling macros + * Copyright (C) 1999 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1999 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef SNPRINTFV_MEM_H +#define SNPRINTFV_MEM_H 1 + +#include <snprintfv/compat.h> + +#ifdef WITH_DMALLOC +# include <dmalloc.h> +#endif + +#ifdef _WIN32 +# ifdef DLL_EXPORT +# define SNV_SCOPE extern __declspec(dllexport) +# else +# ifdef LIBSNPRINTFV_DLL_IMPORT +# define SNV_SCOPE extern __declspec(dllimport) +# endif +# endif +#else +# define SNV_SCOPE extern +#endif + +/* This is the API we use throughout libsnprintfv. */ +#define snv_new(type, count) \ + ((type*)snv_malloc((size_t)sizeof(type) * (count))) +#define snv_renew(type, ptr, count) \ + ((type*)snv_xrealloc((ptr), (size_t)sizeof(type) * (count))) +#define snv_delete(old) snv_free(old) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef void* (malloc_proc_t )(size_t); +typedef void* (realloc_proc_t)(snv_pointer, size_t); +typedef void* (free_proc_t )(snv_pointer); + +/* These function pointers are exposed through the API incase a user + of this library needs to map our memory management routines to + their own (e.g. xmalloc). */ + +/** + * snv_malloc: + * @count: The number of bytes to allocate. + * + * Allocates a fresh block of memory whose size is @count bytes. + * + * Return value: + * The pointer to the newly-allocated memory area. + */ +SNV_SCOPE malloc_proc_t *snv_malloc; + +/** + * snv_realloc: + * @old: The pointer to the block whose size must be changed. + * @count: The number of bytes to allocate. + * + * Reallocates a fresh block of memory pointed to by @old + * so that its size becomes @count bytes. + * + * Return value: + * The pointer to the newly-allocated memory area, possibly + * the same as @old. + */ +SNV_SCOPE realloc_proc_t *snv_realloc; + +/** + * snv_free: + * @old: The pointer to the block that must freed. + * + * Frees a block of memory pointed to by @old. + */ +SNV_SCOPE free_proc_t *snv_free; + +/* And these are reimplemented tout court because they are + not fully portable. */ +extern realloc_proc_t snv_xrealloc; +extern char* snv_strdup (const char *str); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SNPRINTFV_MEM_H */ + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/mem.h */ diff --git a/snprintfv/printf.c b/snprintfv/printf.c new file mode 100644 index 0000000..58927a9 --- /dev/null +++ b/snprintfv/printf.c @@ -0,0 +1,1568 @@ +/* -*- Mode: C -*- */ + +/* printf.c --- printf clone for argv arrays + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <unistd.h> /* for the write(2) call */ + +#ifdef HAVE_ASSERT_H +# include <assert.h> +#else +# define assert(_e) +#endif + +#define COMPILING_PRINTF_C +#include "printf.h" + +#ifdef WITH_DMALLOC +#include <dmalloc.h> +#endif + +#include "filament.h" +#include "stream.h" +#include "mem.h" + +#ifdef SNV_LIBRARY_BUILD +# include "dl.h" +#else + +# ifndef HAVE_STRTOUL +# include "strtoul.c" +# endif +#endif /* SNV_LIBRARY_BUILD */ + +#define EOS '\0' +#define SNV_CHAR_SPEC '%' +#define SNV_ESC_SPEC '\\' + +/* Functions to manage mapping of spec chars to handlers. */ +SNV_INLINE unsigned spec_hash (unsigned spec); +SNV_INLINE void spec_init (void); +SNV_INLINE spec_entry *spec_lookup (unsigned spec); +static void spec_insert (spec_entry * pentry); +static int do_printfv (STREAM *stream, const char *format, + union printf_arg const args[]); + +/* FIXME: We are assuming an ASCII character set where all the + printable characters are between SPACE and DEL. */ +#define ASCII_DEL (int)'\177' +#define ASCII_SPACE (int)' ' + +#define IS_MODIFIER(spec) (!((spec)->fmt)) + +/* TODO: This is not thread-safe. Change the API to pass the spec_table + in as the first parameter to the functions which use it? */ +static spec_entry *spec_table[ASCII_DEL - ASCII_SPACE]; + +/* TODO: This is not thread-safe as well. */ +static char *printf_last_error; + +SNV_INLINE unsigned +spec_hash (unsigned spec) +{ + return (spec & ASCII_DEL) - ASCII_SPACE; +} + +/* Register all of the functions in INIT_SPEC_TABLE. */ +static void +spec_init (void) +{ + static bool is_init = false; + + if (!is_init) + { + extern spec_entry snv_default_spec_table[]; + unsigned ix; + + memset (spec_table, 0, sizeof (spec_table)); + for (ix = 0; snv_default_spec_table[ix].spec_key != EOS; ix++) + { + unsigned hash = spec_hash (snv_default_spec_table[ix].spec_key); + spec_table[hash] = snv_default_spec_table + ix; + } + + is_init = true; + } +} + +/* Insert PENTRY, a new handler, into SPEC_TABLE. */ +SNV_INLINE void +spec_insert (spec_entry *pentry) +{ + unsigned hash = spec_hash (pentry->spec_key); + spec_init (); + spec_table[hash] = pentry; +} + +/* Lookup and return the SPEC_TABLE entry for SPEC. */ +SNV_INLINE spec_entry * +spec_lookup (unsigned spec) +{ + unsigned hash = spec_hash (spec); + spec_init (); + return spec_table[hash]; +} + +/** + * register_printf_function: printf.h + * @spec: the character which will trigger @func, cast to an unsigned int. + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments + * to the specifier + * + * Register the pair made of @fmt and @arg, so that it is called + * when @spec is encountered in a format string. + * + * Return value: + * Returns %NULL if @func was not successfully registered, a + * %spec_entry with the information on the function if it was. + **/ +spec_entry * +register_printf_function (unsigned spec, printf_function *fmt, + printf_arginfo_function *arg) +{ + spec_entry *new, *old; + old = spec_lookup (spec); + if (old && IS_MODIFIER (old)) + return NULL; + + if (!fmt || !spec) + return NULL; + + new = snv_new (spec_entry, 1); + new->spec_key = spec; + new->fmt = fmt; + new->arg = arg; + new->user = NULL; + + spec_insert (new); + + return new; +} + +static int +call_argtype_function ( + struct printf_info *const pinfo, + int **argtypes, + spec_entry *spec) +{ + int n; + int argindex = (pinfo->dollar && !IS_MODIFIER (spec)) + ? pinfo->dollar - 1 + : pinfo->argindex; + + int save_argindex = pinfo->argindex; + int save_state = pinfo->state; + char const *save_format = pinfo->format; + + if (!spec->arg) + { + n = 1; + if (pinfo->argc <= argindex) + { + /* + * "argtypes" points to a pointer of an array of int values. + * Here, we ensure that there are "argindex + 1" entries in + * that array. + */ + *argtypes = snv_renew (int, *argtypes, argindex + 1); + + /* + * IF there are more entries that follow the current argument + * index, then we will clobber all the entries that follow. + * The size of these entries is the size of the array elements, + * not the size of the pointer to the array elements. + */ + if (pinfo->argc < argindex) + memset(*argtypes + pinfo->argc, PA_UNKNOWN, + (argindex - pinfo->argc) * sizeof(**argtypes)); + + pinfo->argc = argindex + 1; + } + + (*argtypes) [argindex] = spec->type; + } + + else + { + pinfo->spec = (unsigned)*(pinfo->format); + pinfo->extra = spec->user; + pinfo->type = spec->type; + + if (pinfo->argc > argindex) + n = spec->arg(pinfo, (size_t) (pinfo->argc - argindex), + *argtypes + argindex); + else + n = spec->arg(pinfo, (size_t)0, NULL); + + if (n < 0) + return n; + if (argindex + n > pinfo->argc) + { + int new_ct = argindex + n; + *argtypes = snv_renew (int, *argtypes, new_ct); + memset(*argtypes + pinfo->argc, PA_UNKNOWN, + (new_ct - pinfo->argc) * sizeof(**argtypes)); + pinfo->argc = argindex + n; + /* Call again... */ + pinfo->argindex = save_argindex; + pinfo->format = save_format; + pinfo->state = save_state; + pinfo->spec = (unsigned)*(pinfo->format); + pinfo->extra = spec->user; + pinfo->type = spec->type; + n = spec->arg(pinfo, (size_t)n, *argtypes + argindex); + } + } + + if (!pinfo->dollar || !IS_MODIFIER (spec)) + pinfo->argindex += n; + + return n; +} + + +/** + * printf_strerror: printf.h + * + * Communicate information on the last error in a printf + * format string. + * + * Return value: + * A string describing the last error which occurred during the + * parsing of a printf format string. It is the responsibility + * of the caller to free the string. + */ +char * +printf_strerror (void) +{ + return snv_strdup(printf_last_error); +} + +/* (re)initialise the memory used by PPARSER. */ +static inline void +parser_init ( + struct printf_info *pinfo, + const char *format, + const union printf_arg *args) +{ + memset (pinfo, 0, sizeof (struct printf_info)); + pinfo->format = format; + pinfo->args = args; +} + +static inline struct printf_info * +parser_reset (struct printf_info *pinfo) +{ + pinfo->is_long_double = pinfo->is_char = pinfo->is_short = + pinfo->is_long = pinfo->alt = pinfo->space = pinfo->left = + pinfo->showsign = pinfo->group = pinfo->wide = + pinfo->width = pinfo->spec = 0; + + pinfo->state = SNV_STATE_BEGIN; + pinfo->prec = -1; + pinfo->dollar = 0; + pinfo->pad = ' '; + + return pinfo; +} + + +/** + * printf_error: printf.h + * @pinfo: pointer to the current parser state. + * @file: file where error was detected. + * @line: line where error was detected. + * @func1: " (" if function is supplied by compiler. + * @func2: function where error was detected, if supplied by compiler. + * @func3: ")" if function is supplied by compiler. + * @error_message: new error message to append to @pinfo. + * + * The contents of @error_message are appended to the @pinfo internal + * error string, so it is safe to pass static strings or recycle the + * original when this function returns. + * + * Return value: + * The address of the full accumulated error message in @pinfo is + * returned. + **/ +char * +printf_error (struct printf_info *pinfo, const char *file, int line, + const char *func1, const char *func2, const char *func3, + const char *error_message) +{ + int i; + char *result; + if (pinfo->error == NULL) + pinfo->error = filnew (NULL, (size_t)0); + else + filccat (pinfo->error, '\n'); + + /* Cannot use printf because a bug in it might trigger another + printf_error! */ + result = filcat (pinfo->error, "file "); + filcat (pinfo->error, file); + filcat (pinfo->error, ": line "); + for (i = 10; i <= line; i *= 10); + for (i /= 10; i >= 1; i /= 10) + filccat (pinfo->error, '0' + (line / i) % 10); + + filcat (pinfo->error, func1); + filcat (pinfo->error, func2); + filcat (pinfo->error, func3); + filcat (pinfo->error, ": "); + filcat (pinfo->error, error_message); + return result; +} + + + +/** + * parse_printf_format: printf.h + * @format: a % delimited format string. + * @n: the size of the @argtypes vector + * @argtypes: a vector of ints, to be filled with the argument types from @format + * + * Returns information about the number and types of + * arguments expected by the template string @format. + * The argument @n specifies the number of elements in the array + * @argtypes. This is the maximum number of elements that + * the function will try to write. + * + * Return value: + * The total number of arguments required by @format. If this + * number is greater than @n, then the information returned + * describes only the first @n arguments. If you want information + * about additional arguments, allocate a bigger array and call + * this function again. If there is an error, then %SNV_ERROR + * is returned instead. + **/ +size_t +parse_printf_format (const char *format, int n, int *argtypes) +{ + struct printf_info info; + + return_val_if_fail (format != NULL, (size_t)-1); + + parser_init (&info, format, NULL); + + while (*info.format != EOS) + { + int ch = (int) *info.format++; + spec_entry *spec; + int status; + int argindex; + + if (ch != SNV_CHAR_SPEC) + continue; + + if (*info.format == SNV_CHAR_SPEC) + { + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + continue; + } + + /* We found the start of a format specifier! */ + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + argindex = info.dollar && !IS_MODIFIER (spec) + ? info.dollar - 1 : info.argindex; + + /* ...and call the relevant handler. */ + if (spec->arg) + { + info.spec = (unsigned)*(info.format); + info.extra = spec->user; + info.type = spec->type; + status = (*spec->arg) (&info, (size_t) (n - argindex), + argtypes + argindex); + } + else + { + status = 1; + if (n > argindex) + argtypes[argindex] = spec->type; + } + + if (status < 0) + goto error; + + info.argc = MAX (info.argc, argindex + status); + if (!info.dollar || !IS_MODIFIER (spec)) + info.argindex += status; + + info.format++; + } + while (IS_MODIFIER (spec)); + + continue; + + error: + /* Get here on error */ + info.argc = -1; + break; + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + return info.argc; +} + +static int +do_printfv (STREAM *stream, const char *format, union printf_arg const args[]) +{ + struct printf_info info; + + /* This is the parser driver. + + Here we scan through the format string and move bytes into the + stream and call handlers based on the parser state. */ + + parser_init (&info, format, args); + + /* Keep going until the format string runs out! */ + while (*info.format != EOS) + { + int ch = (int) *info.format++; + + switch (ch) + { + case SNV_CHAR_SPEC: + if (*info.format != SNV_CHAR_SPEC) + { + /* We found the start of a format specifier! */ + spec_entry *spec; + int status; + int argindex; + + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + /* ...and call the relevant handler. */ + info.spec = (unsigned)*(info.format); + info.extra = spec->user; + info.type = spec->type; + + status = spec->arg ? (*spec->arg) (&info, (size_t)0, NULL) : 1; + + if (status < 0) + goto error; + + argindex = info.dollar && !IS_MODIFIER (spec) + ? info.dollar - 1 : info.argindex; + + info.format++; + info.argc = MAX (info.argc, argindex + status); + if (!info.dollar && !IS_MODIFIER (spec)) + info.argindex += status; + } + while (info.count >= 0 && IS_MODIFIER (spec)); + + status = (*spec->fmt) (stream, &info, args + argindex); + + if (status < 0) + goto error; + + info.count += status; + continue; + } + + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + + /*FALLTHROUGH*/ + + default: + /* Just a character: copy it. */ + SNV_EMIT (ch, stream, info.count); + continue; + } + + error: + /* Get here on error */ + info.count = -1; + break; + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + return info.count; +} + +/** + * stream_printfv: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +stream_printfv (STREAM *stream, const char *format, snv_constpointer const *ap) +{ + union printf_arg *args; + struct printf_info info; + int count_or_errorcode; + int *argtypes = NULL; + + return_val_if_fail (format != NULL, SNV_ERROR); + + parser_init (&info, format, NULL); + + /* Keep going until the format string runs out! */ + while (*info.format != EOS) + { + int ch = (int) *info.format++; + + switch (ch) + { + case SNV_CHAR_SPEC: + if (*info.format != SNV_CHAR_SPEC) + { + /* We found the start of a format specifier! */ + spec_entry *spec; + + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + /* ...and call the relevant handler. */ + if (call_argtype_function (&info, &argtypes, spec) < 0) + goto error; + + info.format++; + } + while (info.count >= 0 && IS_MODIFIER (spec)); + continue; + } + + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + + /*FALLTHROUGH*/ + + default: /* Just a character: ignore it. */ + continue; + } + + error: + /* Get here on error */ + info.argc = -1; + break; + } + + if (info.argc == 0) + { + args = NULL; + } + else + { + int idx; + + assert(argtypes != NULL); + args = snv_new (union printf_arg, info.argc); + + /* We scanned the format string to find the type of the arguments, + so we can now cast it and store it correctly. */ + for (idx = 0; idx < info.argc; idx++) + { + int tp = argtypes[idx]; + if ((tp & PA_TYPE_MASK) == PA_TYPE_MASK) + { + if (idx + 1 == info.argc) + { + info.argc--; + break; + } + continue; /* Ignore it. We allow skipping args, but the + * user is responsible for ensuring a void* sized + * spacer in the argument list. + */ + } + + switch (tp & ~PA_FLAG_UNSIGNED) + { + case PA_CHAR: + args[idx].pa_char = (char) *(const long int *)(ap + idx); + break; + + case PA_WCHAR: + args[idx].pa_wchar = + (snv_wchar_t) *(const long int *)(ap + idx); + break; + + case PA_INT|PA_FLAG_SHORT: + args[idx].pa_short_int = + (short int) *(const long int *)(ap + idx); + break; + + case PA_INT: + args[idx].pa_int = (int) *(const long int *)(ap + idx); + break; + + case PA_INT|PA_FLAG_LONG: + args[idx].pa_long_int = *(const long int *)(ap + idx); + break; + + case PA_INT|PA_FLAG_LONG_LONG: + args[idx].pa_long_long_int = **(const intmax_t **)(ap + idx); + break; + + case PA_FLOAT: + args[idx].pa_float = **(const float **)(ap + idx); + break; + + case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: +#ifdef HAVE_LONG_DOUBLE + args[idx].pa_long_double = **(const long double **)(ap + idx); + break; +#endif + /* else fall through */ + + case PA_DOUBLE: + args[idx].pa_double = **(const double **)(ap + idx); + break; + + /* Note that pointer types are dereferenced just once! */ + case PA_STRING: + args[idx].pa_string = *(const char **)(ap + idx); + break; + + case PA_WSTRING: + args[idx].pa_wstring = *(const snv_wchar_t **)(ap + idx); + break; + + case PA_POINTER: + args[idx].pa_pointer = *(snv_constpointer *)(ap + idx); + break; + + default: + if (argtypes[idx] & PA_FLAG_PTR) + args[idx].pa_pointer = *(snv_constpointer *)(ap + idx); + else + args[idx].pa_long_double = 0.0; + break; + } + } + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + count_or_errorcode = do_printfv (stream, format, args); + + snv_delete (argtypes); + if (info.argc > 0) + snv_delete (args); + + return count_or_errorcode; +} + + +/** + * stream_vprintf: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +stream_vprintf (STREAM *stream, const char *format, va_list ap) +{ + union printf_arg *args = NULL; + struct printf_info info; + int count_or_errorcode; + int *argtypes = NULL; + + return_val_if_fail (format != NULL, SNV_ERROR); + + parser_init (&info, format, NULL); + + /* Keep going until the format string runs out! */ + while (*info.format != EOS) + { + int ch = (int) *info.format++; + + switch (ch) + { + case SNV_CHAR_SPEC: + if (*info.format != SNV_CHAR_SPEC) + { + /* We found the start of a format specifier! */ + spec_entry *spec; + + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + /* ...and call the relevant handler. */ + if (call_argtype_function (&info, &argtypes, spec) < 0) + goto error; + + info.format++; + } + while (info.count >= 0 && IS_MODIFIER (spec)); + continue; + } + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + + /*FALLTHROUGH*/ + + default: /* Just a character: ignore it. */ + continue; + } + + error: + /* Get here on error */ + info.argc = -1; + break; + } + + if (info.argc > 0) + { + int idx; + + assert(argtypes != NULL); + args = snv_new (union printf_arg, info.argc); + + /* Scan the format string to find the type of the argument + so we can cast it and store it correctly. + + Note that according to the ISO C standards, standard + type promotion takes place on any variadic arguments as + they are aligned on the call stack, and so it is these + promoted types that we must extract with the va_arg() + macro, or the alignment gets all messed up. + + Thanks to Robert Lipe <robertlipe@usa.net> for explaining all + this to me. */ + for (idx = 0; idx < info.argc; idx++) + switch (argtypes[idx] & ~PA_FLAG_UNSIGNED) + { + case PA_CHAR: + args[idx].pa_char = va_arg (ap, int); /* Promoted. */ + break; + + case PA_WCHAR: + args[idx].pa_wchar = va_arg (ap, snv_wint_t); /* Promoted. */ + break; + + case PA_INT|PA_FLAG_SHORT: + args[idx].pa_short_int = va_arg (ap, int); /* Promoted. */ + break; + + case PA_INT: + args[idx].pa_int = va_arg (ap, int); + break; + + case PA_INT|PA_FLAG_LONG: + args[idx].pa_long_int = va_arg (ap, long int); + break; + + case PA_INT|PA_FLAG_LONG_LONG: + args[idx].pa_long_long_int = va_arg (ap, intmax_t); + break; + + case PA_FLOAT: + args[idx].pa_float = va_arg (ap, double); /* Promoted. */ + break; + + case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: + args[idx].pa_long_double = va_arg (ap, long double); + break; + + case PA_DOUBLE: + args[idx].pa_double = va_arg (ap, double); + break; + + case PA_STRING: + args[idx].pa_string = va_arg (ap, const char *); + break; + + case PA_WSTRING: + args[idx].pa_wstring = va_arg (ap, const snv_wchar_t *); + break; + + case PA_POINTER: + args[idx].pa_pointer = va_arg (ap, void *); + break; + + default: + if (argtypes[idx] & PA_FLAG_PTR) + args[idx].pa_pointer = va_arg (ap, void *); + else + args[idx].pa_long_double = 0.0; + break; + } + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + count_or_errorcode = do_printfv (stream, format, args); + + snv_delete (argtypes); + snv_delete (args); + return count_or_errorcode; +} + +/** + * stream_printf: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +stream_printf (STREAM * stream, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = stream_vprintf (stream, format, ap); + va_end (ap); + + return count_or_errorcode; +} + + +/* Finally... the main API implementation: */ + +/** + * snv_fdputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a file descriptor. + * + * Return value: + * The value of @ch that has been put in @stream, or -1 in case of + * an error (errno will be set to indicate the type of error). + **/ +int +snv_fdputc (int ch, STREAM *stream) +{ + static char buf[1] = { 0 }; + buf[0] = (char) ch; + return + write ((int) SNV_POINTER_TO_LONG (stream_details (stream)), buf, (size_t) 1) + ? ch : -1; +} + +/** + * snv_dprintf: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_dprintf (int fd, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vdprintf (fd, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vdprintf: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vdprintf (int fd, const char *format, va_list ap) +{ + int result; + STREAM *out = stream_new (SNV_LONG_TO_POINTER (fd), + SNV_UNLIMITED, NULL, snv_fdputc); + + result = stream_vprintf (out, format, ap); + stream_delete (out); + return result; +} + +/** + * snv_dprintfv: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_dprintfv (int fd, const char *format, snv_constpointer const args[]) +{ + int result; + STREAM *out = stream_new (SNV_LONG_TO_POINTER (fd), + SNV_UNLIMITED, NULL, snv_fdputc); + + result = stream_printfv (out, format, args); + stream_delete (out); + return result; +} + + +/** + * snv_fileputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a FILE*. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +int +snv_fileputc (int ch, STREAM *stream) +{ + FILE *fp = (FILE *) stream_details (stream); + return putc (ch, fp); +} + +static int +snv_fileputc_unlocked (int ch, STREAM *stream) +{ + FILE *fp = (FILE *) stream_details (stream); + return SNV_PUTC_UNLOCKED (ch, fp); +} + +/** + * snv_printf: printf.h + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_printf (const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vprintf (format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vprintf: printf.h + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vprintf (const char *format, va_list ap) +{ + int result; + STREAM *out = stream_new (stdout, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (stdout, tmp) + result = stream_vprintf (out, format, ap); + + stream_delete (out); + return result; +} + +/** + * snv_printfv: printf.h + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to the string @format, + * and write the result to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_printfv (const char *format, snv_constpointer const args[]) +{ + int result; + STREAM *out = stream_new (stdout, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (stdout, tmp) + result = stream_printfv (out, format, args); + stream_delete (out); + return result; +} + +/** + * snv_fprintf: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the @file stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_fprintf (FILE * file, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vfprintf (file, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vfprintf: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the @file stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vfprintf (FILE *file, const char *format, va_list ap) +{ + int result; + STREAM *out = stream_new (file, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (file, tmp) + result = stream_vprintf (out, format, ap); + stream_delete (out); + return result; +} + +/** + * snv_fprintfv: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to @file. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_fprintfv (FILE *file, const char *format, snv_constpointer const args[]) +{ + int result; + STREAM *out = stream_new (file, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (file, tmp) + result = stream_printfv (out, format, args); + + stream_delete (out); + return result; +} + + +/** + * snv_bufputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a char buffer. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +int +snv_bufputc (int ch, STREAM * stream) +{ + char **ppbuffer = (char **) stream_details (stream); + **ppbuffer = (char) ch; + (*ppbuffer)++; + return ch; +} + +/** + * snv_sprintf: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_sprintf (char buffer[], const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vsprintf (buffer, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vsprintf: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vsprintf (char buffer[], const char *format, va_list ap) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, SNV_UNLIMITED, NULL, snv_bufputc); + count_or_errorcode = stream_vprintf (out, format, ap); + + /* Terminate with an EOS without incrementing the counter. */ + stream_put (EOS, out); + + stream_delete (out); + return count_or_errorcode; +} + +/** + * snv_sprintfv: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_sprintfv (char buffer[], const char *format, snv_constpointer const args[]) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, SNV_UNLIMITED, NULL, snv_bufputc); + count_or_errorcode = stream_printfv (out, format, args); + + /* Terminate with an EOS without incrementing the counter. */ + stream_put (EOS, out); + + stream_delete (out); + return count_or_errorcode; +} + +/** + * snv_snprintf: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_snprintf (char buffer[], unsigned long limit, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vsnprintf (buffer, limit, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vsnprintf: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vsnprintf (char buffer[], unsigned long limit, const char *format, va_list ap) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, limit - 1, NULL, snv_bufputc); + count_or_errorcode = stream_vprintf (out, format, ap); + *buffer = EOS; + + stream_delete (out); + return count_or_errorcode; +} + +/** + * snv_snprintfv: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_snprintfv (char buffer[], unsigned long limit, const char *format, + snv_constpointer const args[]) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, limit - 1, NULL, snv_bufputc); + count_or_errorcode = stream_printfv (out, format, args); + *buffer = EOS; + + stream_delete (out); + return count_or_errorcode; +} + + +/** + * snv_filputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a Filament*. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +int +snv_filputc (int ch, STREAM * stream) +{ + return filccat ((Filament *) stream_details (stream), ch), ch; +} + +/** + * snv_asprintf: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Yes, this interface is cumbersome and totally useless. It would + * have been better to simply return the allocated address, but + * it turns out that somebody wasn't thinking much when adding + * asprintf to libiberty a few years ago. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_asprintf (char **result, const char *format, ...) +{ + int count; + va_list ap; + + va_start (ap, format); + count = snv_vasprintf (result, format, ap); + va_end (ap); + + return count; +} + +/** + * snv_vasprintf: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Above moaning for asprintf applies here too. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vasprintf (char **result, const char *format, va_list ap) +{ + int count_or_errorcode; + char *base; + Filament *fil = filnew (NULL, (size_t)0); + STREAM *out = stream_new (fil, SNV_UNLIMITED, NULL, snv_filputc); + count_or_errorcode = stream_vprintf (out, format, ap); + + base = fildelete (fil); + stream_delete (out); + + *result = (count_or_errorcode < 0) ? NULL : base; + return count_or_errorcode; +} + +/** + * snv_asprintfv: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Above moaning for asprintf applies here too. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_asprintfv (char **result, const char *format, snv_constpointer const args[]) +{ + int count_or_errorcode; + char *base; + Filament *fil = filnew (NULL, (size_t)0); + STREAM *out = stream_new (fil, SNV_UNLIMITED, NULL, snv_filputc); + count_or_errorcode = stream_printfv (out, format, args); + + base = fildelete (fil); + stream_delete (out); + + *result = (count_or_errorcode < 0) ? NULL : base; + return count_or_errorcode; +} + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/printfv.c */ diff --git a/snprintfv/printf.h b/snprintfv/printf.h new file mode 100644 index 0000000..7e53fd1 --- /dev/null +++ b/snprintfv/printf.h @@ -0,0 +1,846 @@ +/* -*- Mode: C -*- */ + +/* printf.in --- printf clone for argv arrays + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef SNPRINTFV_SNPRINTFV_H +#define SNPRINTFV_SNPRINTFV_H 1 + +#include <snprintfv/compat.h> +#include <snprintfv/filament.h> +#include <snprintfv/stream.h> +#include <snprintfv/mem.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* The type of each element in the table of printf specifiers. */ +struct spec_entry; + +typedef enum +{ + SNV_ERROR = -1, + SNV_OK +} +snv_status; + +/* Basic states required by the parser. On initialisation the parser + will be in SNV_STATE_BEGIN, and tokens will be parsed by the registered + functions until the parser reached SNV_STATE_END. */ +#define SNV_STATE_BEGIN 1 +#define SNV_STATE_END 0 + +/* States needed to support: + %[<number>$]<flags>[<width>|\*][.<precision>|\*]<modifiers><specifier> */ +#define SNV_STATE_FLAG (1 << 1) +#define SNV_STATE_WIDTH (1 << 2) +#define SNV_STATE_PRECISION (1 << 3) +#define SNV_STATE_MODIFIER (1 << 4) +#define SNV_STATE_SPECIFIER (1 << 5) + +/* First state available to the user */ +#define SNV_STATE_USER_FIRST (1 << 8) + +/* Mask for states available to the user */ +#define SNV_STATE_USER_MASK ~(SNV_STATE_USER_FIRST - 1) + +typedef struct printf_info +{ + int count; /* accumulated count, or SNV_ERROR */ + int state; /* one of the defines above */ + Filament *error; /* accumulated error details */ + + const char *format; /* pointer to format string */ + int argc; /* number of arguments used by format */ + int argindex; /* number of non-dollar arguments used so far */ + + int dollar; /* standard parser state, as in glibc */ + int prec; /* from this field on, as in glibc */ + int width; + + snv_pointer extra; + int type; + + char spec; + char pad; + unsigned is_long_double:1; + unsigned is_char:1; + unsigned is_short:1; + unsigned is_long:1; + unsigned alt:1; + unsigned space:1; + unsigned left:1; + unsigned showsign:1; + unsigned group:1; + unsigned wide:1; + + const union printf_arg *args; +} printf_info; + +/** + * printf_arg: + * @pa_char: an unsigned %char + * @pa_wchar: a %wchar_t + * @pa_short_int: a %short integer + * @pa_int: an %int + * @pa_long_int: a %long integer + * @pa_long_long_int: the widest signed integer type in use on the host + * @pa_u_short_int: an unsigned %short integer + * @pa_u_int: an unsigned %int + * @pa_u_long_int: an unsigned %long integer + * @pa_u_long_long_int: the widest unsigned integer type in use on the host + * @pa_float: a %float + * @pa_double: a %double + * @pa_long_double: a long %double, or a simple %double if it is the widest floating-point type in use on the host + * @pa_string: a %const pointer to %char + * @pa_wstring: a %const pointer to %wchar_t + * @pa_pointer: a generic pointer + * + * The various kinds of arguments that can be passed to printf. + */ +typedef union printf_arg +{ + unsigned char pa_char; + snv_wchar_t pa_wchar; + short int pa_short_int; + int pa_int; + long int pa_long_int; + intmax_t pa_long_long_int; + unsigned short int pa_u_short_int; + unsigned int pa_u_int; + unsigned long int pa_u_long_int; + uintmax_t pa_u_long_long_int; + float pa_float; + double pa_double; + long double pa_long_double; + const char *pa_string; + const snv_wchar_t *pa_wstring; + snv_constpointer pa_pointer; +} printf_arg; + +/** + * PRINTF_ERROR: + * @pi: A pointer to the current state for the parser + * @str: The error message + * + * Append an error that will be returned by printf_strerror. + */ +#define PRINTF_ERROR(pi, str) \ + printf_error(pi, __FILE__, __LINE__, SNV_ASSERT_FCN, str); + +typedef int printf_function (STREAM *stream, struct printf_info *pparser, + union printf_arg const * args); + +typedef int printf_arginfo_function (struct printf_info *pparser, size_t n, + int *argtypes); + +/** + * spec_entry: + * @spec: the specifier character that was matched + * @type: when @arg is NULL, the type of the only argument to the specifier + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments to the specifier + * @user: the user data for the specifier, accessible to the handler function + * + * This is returned by register_printf_function. + */ +typedef struct spec_entry +{ + unsigned int spec_key; + int unused; /* for binary compatibility */ + int type; + printf_function *fmt; + printf_arginfo_function *arg; + snv_pointer user; +} +spec_entry; + +/** + * register_callback_function: printf.h + * @spec: the character which will trigger the functions, cast to an unsigned int. + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments to the specifier + * + * Register the pair made of @fmt and @arg, so that it is called + * when @spec is encountered in a format string. If you create + * a shared library with an entry point named + * %snv_register_printf_funcs, and put the library in the + * search path given by the environment library %LTDL_LIBRARY_PATH, + * that entry point will be called when %libsnprintfv is initialized, + * passing a pointer to this kind of function (actually, a pointer + * to %register_printf_function) to it. This functionality is only + * present when the library is installed, not when it is built as + * a convenience library. + * + * Return value: + * Returns %NULL if @func was not successfully registered, a + * %spec_entry with the information on the function if it was. + **/ +typedef spec_entry *register_callback_function (unsigned spec, printf_function *func, printf_arginfo_function *arginfo); + +/* Codes to determine basic types. + + These values cover all the standard format specifications. + Users can add new values after PA_LAST for their own types. */ + +enum +{ + PA_INT, /* int */ + PA_CHAR, /* int, cast to char */ + PA_WCHAR, /* wide char */ + PA_STRING, /* const char *, a '\0'-terminated string */ + PA_WSTRING, /* const wchar_t *, wide character string */ + PA_POINTER, /* void * */ + PA_FLOAT, /* float */ + PA_DOUBLE, /* double */ + PA_LAST, + PA_UNKNOWN = -1 +}; + +/* Flag bits that can be set in a type. */ +#define PA_TYPE_MASK 0x00ff +#define PA_FLAG_MASK ~SNV_TYPE_MASK + +#define PA_FLAG_LONG_LONG (1 << 8) +#define PA_FLAG_LONG_DOUBLE PA_FLAG_LONG_LONG +#define PA_FLAG_LONG (1 << 9) +#define PA_FLAG_SHORT (1 << 10) +#define PA_FLAG_UNSIGNED (1 << 11) +#define PA_FLAG_CHAR (1 << 12) +#define PA_FLAG_PTR (1 << 13) + +/** + * SNV_EMIT: + * @ch: the character to be printed + * @stream: the stream on which to print + * @count: a variable to be updated with the count of printed + * characters + * + * Maintain the count while putting @ch in @stream, also be careful about + * handling %NULL stream if the handler is being called purely to count + * output size. + **/ +#define SNV_EMIT(ch, stream, count) \ + SNV_STMT_START { \ + if ((stream)) \ + { \ + if ((count) >= 0) \ + { \ + int m_status = stream_put((unsigned char) (ch), (stream)); \ + (count) = m_status < 0 ? m_status : (count) + m_status; \ + } \ + } \ + else \ + { \ + (void)(ch); \ + (count)++; \ + } \ + } SNV_STMT_END + +#line 267 "printf.in" +/** + * printf_generic_info: + * @pinfo: the current state information for the format + * string parser. + * @n: the number of available slots in the @argtypes array + * @argtypes: the pointer to the first slot to be filled by the + * function + * + * An example implementation of a %printf_arginfo_function, which + * takes the basic type from the type given in the %spec_entry + * and adds flags depending on what was parsed (e.g. %PA_FLAG_SHORT + * is %pparser->is_short and so on). + * + * Return value: + * Always 1. + */ +extern int +printf_generic_info (struct printf_info *const pinfo, size_t n, int *argtypes); + +/** + * printf_generic: + * @stream: the stream (possibly a struct printfv_stream appropriately + * cast) on which to write output. + * @pinfo: the current state information for the format string parser. + * @args: the pointer to the first argument to be read by the handler + * + * An example implementation of a %printf_function, used to provide easy + * access to justification, width and precision options. + * + * Return value: + * The number of characters output. + **/ +extern int +printf_generic (STREAM *stream, struct printf_info *const pinfo, union printf_arg const *args); + +#line 268 "printf.in" +/** + * register_printf_function: printf.h + * @spec: the character which will trigger @func, cast to an unsigned int. + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments + * to the specifier + * + * Register the pair made of @fmt and @arg, so that it is called + * when @spec is encountered in a format string. + * + * Return value: + * Returns %NULL if @func was not successfully registered, a + * %spec_entry with the information on the function if it was. + **/ +extern spec_entry * +register_printf_function (unsigned spec, printf_function *fmt, + printf_arginfo_function *arg); + +/** + * printf_strerror: printf.h + * + * Communicate information on the last error in a printf + * format string. + * + * Return value: + * A string describing the last error which occurred during the + * parsing of a printf format string. It is the responsibility + * of the caller to free the string. + */ +extern char * +printf_strerror (void); + +/** + * printf_error: printf.h + * @pinfo: pointer to the current parser state. + * @file: file where error was detected. + * @line: line where error was detected. + * @func1: " (" if function is supplied by compiler. + * @func2: function where error was detected, if supplied by compiler. + * @func3: ")" if function is supplied by compiler. + * @error_message: new error message to append to @pinfo. + * + * The contents of @error_message are appended to the @pinfo internal + * error string, so it is safe to pass static strings or recycle the + * original when this function returns. + * + * Return value: + * The address of the full accumulated error message in @pinfo is + * returned. + **/ +extern char * +printf_error (struct printf_info *pinfo, const char *file, int line, + const char *func1, const char *func2, const char *func3, + const char *error_message); + +/** + * parse_printf_format: printf.h + * @format: a % delimited format string. + * @n: the size of the @argtypes vector + * @argtypes: a vector of ints, to be filled with the argument types from @format + * + * Returns information about the number and types of + * arguments expected by the template string @format. + * The argument @n specifies the number of elements in the array + * @argtypes. This is the maximum number of elements that + * the function will try to write. + * + * Return value: + * The total number of arguments required by @format. If this + * number is greater than @n, then the information returned + * describes only the first @n arguments. If you want information + * about additional arguments, allocate a bigger array and call + * this function again. If there is an error, then %SNV_ERROR + * is returned instead. + **/ +extern size_t +parse_printf_format (const char *format, int n, int *argtypes); + +/** + * stream_printfv: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +stream_printfv (STREAM *stream, const char *format, snv_constpointer const *ap); + +/** + * stream_vprintf: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +stream_vprintf (STREAM *stream, const char *format, va_list ap); + +/** + * stream_printf: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +stream_printf (STREAM * stream, const char *format, ...); + +/** + * snv_fdputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a file descriptor. + * + * Return value: + * The value of @ch that has been put in @stream, or -1 in case of + * an error (errno will be set to indicate the type of error). + **/ +extern int +snv_fdputc (int ch, STREAM *stream); + +/** + * snv_dprintf: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_dprintf (int fd, const char *format, ...); + +/** + * snv_vdprintf: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_vdprintf (int fd, const char *format, va_list ap); + +/** + * snv_dprintfv: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_dprintfv (int fd, const char *format, snv_constpointer const args[]); + +/** + * snv_fileputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a FILE*. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +extern int +snv_fileputc (int ch, STREAM *stream); + +/** + * snv_printf: printf.h + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_printf (const char *format, ...); + +/** + * snv_vprintf: printf.h + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_vprintf (const char *format, va_list ap); + +/** + * snv_printfv: printf.h + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to the string @format, + * and write the result to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_printfv (const char *format, snv_constpointer const args[]); + +/** + * snv_fprintf: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the @file stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_fprintf (FILE * file, const char *format, ...); + +/** + * snv_vfprintf: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the @file stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_vfprintf (FILE *file, const char *format, va_list ap); + +/** + * snv_fprintfv: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to @file. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_fprintfv (FILE *file, const char *format, snv_constpointer const args[]); + +/** + * snv_bufputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a char buffer. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +extern int +snv_bufputc (int ch, STREAM * stream); + +/** + * snv_sprintf: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_sprintf (char buffer[], const char *format, ...); + +/** + * snv_vsprintf: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_vsprintf (char buffer[], const char *format, va_list ap); + +/** + * snv_sprintfv: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_sprintfv (char buffer[], const char *format, snv_constpointer const args[]); + +/** + * snv_snprintf: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_snprintf (char buffer[], unsigned long limit, const char *format, ...); + +/** + * snv_vsnprintf: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_vsnprintf (char buffer[], unsigned long limit, const char *format, va_list ap); + +/** + * snv_snprintfv: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_snprintfv (char buffer[], unsigned long limit, const char *format, + snv_constpointer const args[]); + +/** + * snv_filputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a Filament*. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +extern int +snv_filputc (int ch, STREAM * stream); + +/** + * snv_asprintf: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Yes, this interface is cumbersome and totally useless. It would + * have been better to simply return the allocated address, but + * it turns out that somebody wasn't thinking much when adding + * asprintf to libiberty a few years ago. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_asprintf (char **result, const char *format, ...); + +/** + * snv_vasprintf: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Above moaning for asprintf applies here too. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_vasprintf (char **result, const char *format, va_list ap); + +/** + * snv_asprintfv: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Above moaning for asprintf applies here too. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +extern int +snv_asprintfv (char **result, const char *format, snv_constpointer const args[]); + +#line 269 "printf.in" + +/* If you don't want to use snprintfv functions for *all* of your string + formatting API, then define COMPILING_SNPRINTFV_C and use the snv_ + prefix for the entry points below. */ +#ifndef COMPILING_PRINTF_C +#undef printf +#undef vprintf +#undef dprintf +#undef vdprintf +#undef fprintf +#undef vfprintf +#undef sprintf +#undef vsprintf +#undef snprintf +#undef vsnprintf +#undef asprintf +#undef vasprintf +#undef asprintfv +#undef dprintfv +#undef fprintfv +#undef sprintfv +#undef printfv +#undef snprintfv + +#define printf snv_printf +#define vprintf snv_vprintf +#define dprintf snv_dprintf +#define vdprintf snv_vdprintf +#define fprintf snv_fprintf +#define vfprintf snv_vfprintf +#define sprintf snv_sprintf +#define vsprintf snv_vsprintf +#define snprintf snv_snprintf +#define vsnprintf snv_vsnprintf +#define asprintf snv_asprintf +#define vasprintf snv_vasprintf +#define asprintfv snv_asprintfv +#define dprintfv snv_dprintfv +#define fprintfv snv_fprintfv +#define sprintfv snv_sprintfv +#define printfv snv_printfv +#define snprintfv snv_snprintfv +#endif /* !COMPILING_SNPRINTFV_C */ +#ifdef __cplusplus +} +#endif + +#endif /* SNPRINTFV_SNPRINTFV_H */ + +/* snprintfv.h ends here */ diff --git a/snprintfv/printf.in b/snprintfv/printf.in new file mode 100644 index 0000000..98ba995 --- /dev/null +++ b/snprintfv/printf.in @@ -0,0 +1,318 @@ +/* -*- Mode: C -*- */ + +/* printf.in --- printf clone for argv arrays + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef SNPRINTFV_SNPRINTFV_H +#define SNPRINTFV_SNPRINTFV_H 1 + +#include <snprintfv/compat.h> +#include <snprintfv/filament.h> +#include <snprintfv/stream.h> +#include <snprintfv/mem.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* The type of each element in the table of printf specifiers. */ +struct spec_entry; + +typedef enum +{ + SNV_ERROR = -1, + SNV_OK +} +snv_status; + +/* Basic states required by the parser. On initialisation the parser + will be in SNV_STATE_BEGIN, and tokens will be parsed by the registered + functions until the parser reached SNV_STATE_END. */ +#define SNV_STATE_BEGIN 1 +#define SNV_STATE_END 0 + +/* States needed to support: + %[<number>$]<flags>[<width>|\*][.<precision>|\*]<modifiers><specifier> */ +#define SNV_STATE_FLAG (1 << 1) +#define SNV_STATE_WIDTH (1 << 2) +#define SNV_STATE_PRECISION (1 << 3) +#define SNV_STATE_MODIFIER (1 << 4) +#define SNV_STATE_SPECIFIER (1 << 5) + +/* First state available to the user */ +#define SNV_STATE_USER_FIRST (1 << 8) + +/* Mask for states available to the user */ +#define SNV_STATE_USER_MASK ~(SNV_STATE_USER_FIRST - 1) + +typedef struct printf_info +{ + int count; /* accumulated count, or SNV_ERROR */ + int state; /* one of the defines above */ + Filament *error; /* accumulated error details */ + + const char *format; /* pointer to format string */ + int argc; /* number of arguments used by format */ + int argindex; /* number of non-dollar arguments used so far */ + + int dollar; /* standard parser state, as in glibc */ + int prec; /* from this field on, as in glibc */ + int width; + + snv_pointer extra; + int type; + + char spec; + char pad; + unsigned is_long_double:1; + unsigned is_char:1; + unsigned is_short:1; + unsigned is_long:1; + unsigned alt:1; + unsigned space:1; + unsigned left:1; + unsigned showsign:1; + unsigned group:1; + unsigned wide:1; + + const union printf_arg *args; +} printf_info; + +/** + * printf_arg: + * @pa_char: an unsigned %char + * @pa_wchar: a %wchar_t + * @pa_short_int: a %short integer + * @pa_int: an %int + * @pa_long_int: a %long integer + * @pa_long_long_int: the widest signed integer type in use on the host + * @pa_u_short_int: an unsigned %short integer + * @pa_u_int: an unsigned %int + * @pa_u_long_int: an unsigned %long integer + * @pa_u_long_long_int: the widest unsigned integer type in use on the host + * @pa_float: a %float + * @pa_double: a %double + * @pa_long_double: a long %double, or a simple %double if it is the widest floating-point type in use on the host + * @pa_string: a %const pointer to %char + * @pa_wstring: a %const pointer to %wchar_t + * @pa_pointer: a generic pointer + * + * The various kinds of arguments that can be passed to printf. + */ +typedef union printf_arg +{ + unsigned char pa_char; + snv_wchar_t pa_wchar; + short int pa_short_int; + int pa_int; + long int pa_long_int; + intmax_t pa_long_long_int; + unsigned short int pa_u_short_int; + unsigned int pa_u_int; + unsigned long int pa_u_long_int; + uintmax_t pa_u_long_long_int; + float pa_float; + double pa_double; + long double pa_long_double; + const char *pa_string; + const snv_wchar_t *pa_wstring; + snv_constpointer pa_pointer; +} printf_arg; + +/** + * PRINTF_ERROR: + * @pi: A pointer to the current state for the parser + * @str: The error message + * + * Append an error that will be returned by printf_strerror. + */ +#define PRINTF_ERROR(pi, str) \ + printf_error(pi, __FILE__, __LINE__, SNV_ASSERT_FCN, str); + +typedef int printf_function (STREAM *stream, struct printf_info *pparser, + union printf_arg const * args); + +typedef int printf_arginfo_function (struct printf_info *pparser, size_t n, + int *argtypes); + +/** + * spec_entry: + * @spec: the specifier character that was matched + * @type: when @arg is NULL, the type of the only argument to the specifier + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments to the specifier + * @user: the user data for the specifier, accessible to the handler function + * + * This is returned by register_printf_function. + */ +typedef struct spec_entry +{ + unsigned int spec_key; + int unused; /* for binary compatibility */ + int type; + printf_function *fmt; + printf_arginfo_function *arg; + snv_pointer user; +} +spec_entry; + +/** + * register_callback_function: printf.h + * @spec: the character which will trigger the functions, cast to an unsigned int. + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments to the specifier + * + * Register the pair made of @fmt and @arg, so that it is called + * when @spec is encountered in a format string. If you create + * a shared library with an entry point named + * %snv_register_printf_funcs, and put the library in the + * search path given by the environment library %LTDL_LIBRARY_PATH, + * that entry point will be called when %libsnprintfv is initialized, + * passing a pointer to this kind of function (actually, a pointer + * to %register_printf_function) to it. This functionality is only + * present when the library is installed, not when it is built as + * a convenience library. + * + * Return value: + * Returns %NULL if @func was not successfully registered, a + * %spec_entry with the information on the function if it was. + **/ +typedef spec_entry *register_callback_function (unsigned spec, printf_function *func, printf_arginfo_function *arginfo); + +/* Codes to determine basic types. + + These values cover all the standard format specifications. + Users can add new values after PA_LAST for their own types. */ + +enum +{ + PA_INT, /* int */ + PA_CHAR, /* int, cast to char */ + PA_WCHAR, /* wide char */ + PA_STRING, /* const char *, a '\0'-terminated string */ + PA_WSTRING, /* const wchar_t *, wide character string */ + PA_POINTER, /* void * */ + PA_FLOAT, /* float */ + PA_DOUBLE, /* double */ + PA_LAST, + PA_UNKNOWN = -1 +}; + +/* Flag bits that can be set in a type. */ +#define PA_TYPE_MASK 0x00ff +#define PA_FLAG_MASK ~SNV_TYPE_MASK + +#define PA_FLAG_LONG_LONG (1 << 8) +#define PA_FLAG_LONG_DOUBLE PA_FLAG_LONG_LONG +#define PA_FLAG_LONG (1 << 9) +#define PA_FLAG_SHORT (1 << 10) +#define PA_FLAG_UNSIGNED (1 << 11) +#define PA_FLAG_CHAR (1 << 12) +#define PA_FLAG_PTR (1 << 13) + +/** + * SNV_EMIT: + * @ch: the character to be printed + * @stream: the stream on which to print + * @count: a variable to be updated with the count of printed + * characters + * + * Maintain the count while putting @ch in @stream, also be careful about + * handling %NULL stream if the handler is being called purely to count + * output size. + **/ +#define SNV_EMIT(ch, stream, count) \ + SNV_STMT_START { \ + if ((stream)) \ + { \ + if ((count) >= 0) \ + { \ + int m_status = stream_put((unsigned char) (ch), (stream)); \ + (count) = m_status < 0 ? m_status : (count) + m_status; \ + } \ + } \ + else \ + { \ + (void)(ch); \ + (count)++; \ + } \ + } SNV_STMT_END + +@protos format.c +@protos custom.c +@protos printf.c + +/* If you don't want to use snprintfv functions for *all* of your string + formatting API, then define COMPILING_SNPRINTFV_C and use the snv_ + prefix for the entry points below. */ +#ifndef COMPILING_PRINTF_C +#undef printf +#undef vprintf +#undef dprintf +#undef vdprintf +#undef fprintf +#undef vfprintf +#undef sprintf +#undef vsprintf +#undef snprintf +#undef vsnprintf +#undef asprintf +#undef vasprintf +#undef asprintfv +#undef dprintfv +#undef fprintfv +#undef sprintfv +#undef printfv +#undef snprintfv + +#define printf snv_printf +#define vprintf snv_vprintf +#define dprintf snv_dprintf +#define vdprintf snv_vdprintf +#define fprintf snv_fprintf +#define vfprintf snv_vfprintf +#define sprintf snv_sprintf +#define vsprintf snv_vsprintf +#define snprintf snv_snprintf +#define vsnprintf snv_vsnprintf +#define asprintf snv_asprintf +#define vasprintf snv_vasprintf +#define asprintfv snv_asprintfv +#define dprintfv snv_dprintfv +#define fprintfv snv_fprintfv +#define sprintfv snv_sprintfv +#define printfv snv_printfv +#define snprintfv snv_snprintfv +#endif /* !COMPILING_SNPRINTFV_C */ +#ifdef __cplusplus +} +#endif + +#endif /* SNPRINTFV_SNPRINTFV_H */ + +/* snprintfv.h ends here */ diff --git a/snprintfv/printf.stamp b/snprintfv/printf.stamp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/snprintfv/printf.stamp diff --git a/snprintfv/stream.c b/snprintfv/stream.c new file mode 100644 index 0000000..a979f33 --- /dev/null +++ b/snprintfv/stream.c @@ -0,0 +1,226 @@ +/* -*- Mode: C -*- */ + +/* stream.c --- customizable stream routines + * Copyright (C) 1998, 1999, 2000, 2002, 2003 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef WITH_DMALLOC +# include <dmalloc.h> +#endif + +#include "compat.h" +#include "stream.h" +#include "mem.h" + +struct stream +{ + snv_pointer stream; + unsigned long limit; + + StreamGet get_func; + StreamPut put_func; +}; + +static int +stream_not_readable (STREAM *stream) +{ + (void)stream; + return -1; +} + +static int +stream_not_writable (int ch, STREAM *stream) +{ + (void)stream; + (void)ch; + return -1; +} + +/** + * stream_new: constructor + * @dets: user supplied stream details to be passed into the various funcs. + * @limit: the maximum number of consecutive bytes to fit in @dets. + * @get_func: function to get a character from @dets stream. + * @put_func: function to put a character in @dets stream. + * + * Allocate and initialize a new %STREAM data type. The @get_func + * and @put_func can be NULL if you intend to create a non-readable + * or non-writable stream, respectively. + * + * Return value: + * The address of the newly allocated and initialised stream is returned. + **/ +STREAM * +stream_new (snv_pointer dets, unsigned long limit, StreamGet get_func, StreamPut put_func) +{ + STREAM *new = snv_new (STREAM, 1); + + new->stream = dets; + new->limit = limit; + + new->get_func = get_func ? get_func : stream_not_readable; + new->put_func = put_func ? put_func : stream_not_writable; + + return new; +} + + +/** + * stream_delete: destructor + * @stream: The stream pending deletion + * + * The memory associated with @stream is recycled. + + * Return value: + * The %dets supplied by the user when the stream was created are + * returned for handling by the calling function. + **/ +snv_pointer +stream_delete (STREAM *stream) +{ + snv_pointer dets = stream->stream; + snv_delete (stream); + return dets; +} + +/** + * stream_details: + * @stream: the stream being queried. + * + * The finalization function specified when @stream was created (if any) + * is called, and then the memory associated with @stream is recycled. + * It is the responsibility of the finalization function to recycle, or + * otherwise manage, any memory associated with the user supplied %dets. + * Return value: + * This function returns the stream details associated with @stream + * when it was originally created. + **/ +snv_pointer +stream_details (STREAM *stream) +{ + return stream ? stream->stream : NULL; +} + +/** + * stream_put: + * @ch: A single character to be placed in @stream. + * @stream: The stream to be written to. + * + * This function will @ch in @stream if that stream's output limit will + * not be exceeded. + * + * Return value: + * If @stream is full, return 1. Otherwise, if any other error occurs, + * that error code is returned unchanged. This is of course dependant + * on what the handler function uses to indicate an error. If the stream + * is not full and the stream's writing function succeeds, 1 (the number of + * characters emitted!) is returned. + **/ +int +stream_put (int ch, STREAM *stream) +{ + int ch_or_errorcode; + + if (!stream) + return -1; + + if (stream->limit < 1) + return 1; + + stream->limit -= 1; + ch_or_errorcode = (*stream->put_func) ((unsigned char) ch, stream); + + return (ch_or_errorcode < 0) ? ch_or_errorcode : 1; +} + +/** + * stream_puts: + * @s: A string to be placed in @stream. + * @stream: The stream to be written to. + * + * This function will @ch in @stream if that stream's output limit will + * not be exceeded. + * + * Return value: + * If any other error occurs, that error code is returned unchanged. + * This is of course dependant on what the handler function uses to + * indicate an error. If the stream becomes full, the remaining + * characters are not printed. If the stream's writing function + * always succeeds, the number of characters emitted or skipped is + * returned. + **/ +int +stream_puts (char *s, STREAM *stream) +{ + int ch_or_errorcode; + int num; + + if (!stream) + return -1; + + for (num = 0; *s; num++, s++) + { + if (stream->limit < 1) + return num + strlen (s); + + stream->limit -= 1; + ch_or_errorcode = (*stream->put_func) ((unsigned char) *s, stream); + + if (ch_or_errorcode < 0) + return ch_or_errorcode; + } + + return num; +} + +/** + * stream_get: + * @stream: The stream to be read from. + * + * This function will try to read a single character from @stream. + * + * Return value: + * If an error occurs or the end of @stream is reached, -1 is returned. + * Under normal circumstances the value if the character read (cast to + * an int) is returned. + **/ +int +stream_get (STREAM *stream) +{ + return (*stream->get_func) (stream); +} + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/stream.c */ diff --git a/snprintfv/stream.h b/snprintfv/stream.h new file mode 100644 index 0000000..0f47855 --- /dev/null +++ b/snprintfv/stream.h @@ -0,0 +1,192 @@ +/* -*- Mode: C -*- */ + +/* stream.h --- customizable stream routines + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef STREAM_H +#define STREAM_H 1 + +#define STREAM_READABLE (1 << 0) +#define STREAM_WRITABLE (1 << 1) + +/** + * SNV_UNLIMITED: + * Used to denote that there is no upper limit to the number of characters + * that can safely be written to a stream. + **/ +#define SNV_UNLIMITED (~0UL) + +#ifdef __cplusplus +extern "C" +{ +#if 0 +/* This brace is so that emacs can still indent properly: */ } +#endif +#endif /* __cplusplus */ + +/** + * STREAM: + * Data type used to pass details of streams between functions, + * much like stdio's %FILE, but more flexible. A %STREAM can be uni- or + * bi-directional depending on how it is initialised. + **/ +typedef struct stream STREAM; + +/** + * StreamPut: + * @ch: The character to write to @stream cast to an int. + * @stream: The stream being written to. + * + * Type of the function to put a character in a writeable stream. + * + * Return value: + * The function should return the character written to the + * stream, cast to an int if it was written successfully, or + * else %EOF, if the write failed. + **/ +typedef int (*StreamPut) (int ch, STREAM * stream); + +/** + * StreamGet: + * @stream: The stream being read from. + * + * Type of the function to get a character from a readable stream. + * + * Return value: + * The function should return the character read from the + * stream, cast to an int if it was read successfully, or + * else %EOF, if the read failed. + **/ +typedef int (*StreamGet) (STREAM * stream); + + +/** + * stream_new: constructor + * @dets: user supplied stream details to be passed into the various funcs. + * @limit: the maximum number of consecutive bytes to fit in @dets. + * @get_func: function to get a character from @dets stream. + * @put_func: function to put a character in @dets stream. + * + * Allocate and initialize a new %STREAM data type. The @get_func + * and @put_func can be NULL if you intend to create a non-readable + * or non-writable stream, respectively. + * + * Return value: + * The address of the newly allocated and initialised stream is returned. + **/ +extern STREAM * +stream_new (snv_pointer dets, unsigned long limit, StreamGet get_func, StreamPut put_func); + +/** + * stream_delete: destructor + * @stream: The stream pending deletion + * + * The memory associated with @stream is recycled. + + * Return value: + * The %dets supplied by the user when the stream was created are + * returned for handling by the calling function. + **/ +extern snv_pointer +stream_delete (STREAM *stream); + +/** + * stream_details: + * @stream: the stream being queried. + * + * The finalization function specified when @stream was created (if any) + * is called, and then the memory associated with @stream is recycled. + * It is the responsibility of the finalization function to recycle, or + * otherwise manage, any memory associated with the user supplied %dets. + * Return value: + * This function returns the stream details associated with @stream + * when it was originally created. + **/ +extern snv_pointer +stream_details (STREAM *stream); + +/** + * stream_put: + * @ch: A single character to be placed in @stream. + * @stream: The stream to be written to. + * + * This function will @ch in @stream if that stream's output limit will + * not be exceeded. + * + * Return value: + * If @stream is full, return 1. Otherwise, if any other error occurs, + * that error code is returned unchanged. This is of course dependant + * on what the handler function uses to indicate an error. If the stream + * is not full and the stream's writing function succeeds, 1 (the number of + * characters emitted!) is returned. + **/ +extern int +stream_put (int ch, STREAM *stream); + +/** + * stream_puts: + * @s: A string to be placed in @stream. + * @stream: The stream to be written to. + * + * This function will @ch in @stream if that stream's output limit will + * not be exceeded. + * + * Return value: + * If any other error occurs, that error code is returned unchanged. + * This is of course dependant on what the handler function uses to + * indicate an error. If the stream becomes full, the remaining + * characters are not printed. If the stream's writing function + * always succeeds, the number of characters emitted or skipped is + * returned. + **/ +extern int +stream_puts (char *s, STREAM *stream); + +/** + * stream_get: + * @stream: The stream to be read from. + * + * This function will try to read a single character from @stream. + * + * Return value: + * If an error occurs or the end of @stream is reached, -1 is returned. + * Under normal circumstances the value if the character read (cast to + * an int) is returned. + **/ +extern int +stream_get (STREAM *stream); + +#line 88 "stream.in" +#ifdef __cplusplus +#if 0 +/* This brace is so that emacs can still indent properly: */ +{ +#endif +} +#endif /* __cplusplus */ + +#endif /* STREAM_H */ diff --git a/snprintfv/stream.in b/snprintfv/stream.in new file mode 100644 index 0000000..fbcf431 --- /dev/null +++ b/snprintfv/stream.in @@ -0,0 +1,96 @@ +/* -*- Mode: C -*- */ + +/* stream.h --- customizable stream routines + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifndef STREAM_H +#define STREAM_H 1 + +#define STREAM_READABLE (1 << 0) +#define STREAM_WRITABLE (1 << 1) + +/** + * SNV_UNLIMITED: + * Used to denote that there is no upper limit to the number of characters + * that can safely be written to a stream. + **/ +#define SNV_UNLIMITED (~0UL) + +#ifdef __cplusplus +extern "C" +{ +#if 0 +/* This brace is so that emacs can still indent properly: */ } +#endif +#endif /* __cplusplus */ + +/** + * STREAM: + * Data type used to pass details of streams between functions, + * much like stdio's %FILE, but more flexible. A %STREAM can be uni- or + * bi-directional depending on how it is initialised. + **/ +typedef struct stream STREAM; + +/** + * StreamPut: + * @ch: The character to write to @stream cast to an int. + * @stream: The stream being written to. + * + * Type of the function to put a character in a writeable stream. + * + * Return value: + * The function should return the character written to the + * stream, cast to an int if it was written successfully, or + * else %EOF, if the write failed. + **/ +typedef int (*StreamPut) (int ch, STREAM * stream); + +/** + * StreamGet: + * @stream: The stream being read from. + * + * Type of the function to get a character from a readable stream. + * + * Return value: + * The function should return the character read from the + * stream, cast to an int if it was read successfully, or + * else %EOF, if the read failed. + **/ +typedef int (*StreamGet) (STREAM * stream); + + +@protos stream.c +#ifdef __cplusplus +#if 0 +/* This brace is so that emacs can still indent properly: */ +{ +#endif +} +#endif /* __cplusplus */ + +#endif /* STREAM_H */ diff --git a/snprintfv/stream.stamp b/snprintfv/stream.stamp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/snprintfv/stream.stamp |