From 52210232ca59f86d354e3995c0862e4037b857bb Mon Sep 17 00:00:00 2001 From: Arnout Engelen Date: Sun, 5 Jan 2014 16:55:54 +0100 Subject: [PATCH] Import libtu instead of using submodules --- .gitmodules | 3 - libtu | 1 - libtu/.gitignore | 2 + libtu/Makefile | 43 ++ libtu/build/rules.mk | 207 ++++++++ libtu/build/system-inc.mk | 18 + libtu/debug.h | 19 + libtu/dlist.h | 124 +++++ libtu/errorlog.c | 127 +++++ libtu/errorlog.h | 38 ++ libtu/install-sh | 251 +++++++++ libtu/iterable.c | 51 ++ libtu/iterable.h | 29 + libtu/locale.h | 27 + libtu/map.c | 83 +++ libtu/map.h | 54 ++ libtu/minmax.h | 27 + libtu/misc.c | 211 ++++++++ libtu/misc.h | 43 ++ libtu/np/np-conv.h | 121 +++++ libtu/np/numparser2.h | 272 ++++++++++ libtu/obj.c | 297 +++++++++++ libtu/obj.h | 69 +++ libtu/objlist.c | 332 ++++++++++++ libtu/objlist.h | 61 +++ libtu/objp.h | 72 +++ libtu/optparser.c | 471 ++++++++++++++++ libtu/optparser.h | 78 +++ libtu/output.c | 405 ++++++++++++++ libtu/output.h | 76 +++ libtu/parser.c | 717 +++++++++++++++++++++++++ libtu/parser.h | 54 ++ libtu/pointer.h | 16 + libtu/prefix.c | 61 +++ libtu/prefix.h | 17 + libtu/private.h | 27 + libtu/ptrlist.c | 208 ++++++++ libtu/ptrlist.h | 58 ++ libtu/rb-test.c | 98 ++++ libtu/rb.c | 626 ++++++++++++++++++++++ libtu/rb.h | 143 +++++ libtu/setparam.c | 60 +++ libtu/setparam.h | 27 + libtu/snprintf_2.2/INSTALL | 24 + libtu/snprintf_2.2/LICENSE.txt | 121 +++++ libtu/snprintf_2.2/Makefile.unused | 43 ++ libtu/snprintf_2.2/README | 283 ++++++++++ libtu/snprintf_2.2/README.html | 382 +++++++++++++ libtu/snprintf_2.2/snprintf-orig.c | 1025 +++++++++++++++++++++++++++++++++++ libtu/snprintf_2.2/snprintf.c | 1032 ++++++++++++++++++++++++++++++++++++ libtu/snprintf_2.2/snprintf.h | 26 + libtu/snprintf_2.2/test.c | 689 ++++++++++++++++++++++++ libtu/stringstore.c | 144 +++++ libtu/stringstore.h | 25 + libtu/system-autodetect.mk | 88 +++ libtu/tester.c | 54 ++ libtu/tester2.c | 69 +++ libtu/tester3.c | 72 +++ libtu/tokenizer.c | 958 +++++++++++++++++++++++++++++++++ libtu/tokenizer.h | 212 ++++++++ libtu/types.h | 86 +++ libtu/util.c | 45 ++ libtu/util.h | 39 ++ 63 files changed, 11137 insertions(+), 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 libtu create mode 100644 libtu/.gitignore create mode 100644 libtu/Makefile create mode 100644 libtu/build/rules.mk create mode 100644 libtu/build/system-inc.mk create mode 100644 libtu/debug.h create mode 100644 libtu/dlist.h create mode 100644 libtu/errorlog.c create mode 100644 libtu/errorlog.h create mode 100644 libtu/install-sh create mode 100644 libtu/iterable.c create mode 100644 libtu/iterable.h create mode 100644 libtu/locale.h create mode 100644 libtu/map.c create mode 100644 libtu/map.h create mode 100644 libtu/minmax.h create mode 100644 libtu/misc.c create mode 100644 libtu/misc.h create mode 100644 libtu/np/np-conv.h create mode 100644 libtu/np/numparser2.h create mode 100644 libtu/obj.c create mode 100644 libtu/obj.h create mode 100644 libtu/objlist.c create mode 100644 libtu/objlist.h create mode 100644 libtu/objp.h create mode 100644 libtu/optparser.c create mode 100644 libtu/optparser.h create mode 100644 libtu/output.c create mode 100644 libtu/output.h create mode 100644 libtu/parser.c create mode 100644 libtu/parser.h create mode 100644 libtu/pointer.h create mode 100644 libtu/prefix.c create mode 100644 libtu/prefix.h create mode 100644 libtu/private.h create mode 100644 libtu/ptrlist.c create mode 100644 libtu/ptrlist.h create mode 100644 libtu/rb-test.c create mode 100644 libtu/rb.c create mode 100644 libtu/rb.h create mode 100644 libtu/setparam.c create mode 100644 libtu/setparam.h create mode 100644 libtu/snprintf_2.2/INSTALL create mode 100644 libtu/snprintf_2.2/LICENSE.txt create mode 100644 libtu/snprintf_2.2/Makefile.unused create mode 100644 libtu/snprintf_2.2/README create mode 100644 libtu/snprintf_2.2/README.html create mode 100644 libtu/snprintf_2.2/snprintf-orig.c create mode 100644 libtu/snprintf_2.2/snprintf.c create mode 100644 libtu/snprintf_2.2/snprintf.h create mode 100644 libtu/snprintf_2.2/test.c create mode 100644 libtu/stringstore.c create mode 100644 libtu/stringstore.h create mode 100644 libtu/system-autodetect.mk create mode 100644 libtu/tester.c create mode 100644 libtu/tester2.c create mode 100644 libtu/tester3.c create mode 100644 libtu/tokenizer.c create mode 100644 libtu/tokenizer.h create mode 100644 libtu/types.h create mode 100644 libtu/util.c create mode 100644 libtu/util.h diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 15e2ab91..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "libtu"] - path = libtu - url = ../libtu diff --git a/libtu b/libtu deleted file mode 160000 index 0b9269d2..00000000 --- a/libtu +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0b9269d256682d7635cf22194141509ebb12d1e0 diff --git a/libtu/.gitignore b/libtu/.gitignore new file mode 100644 index 00000000..cad55f62 --- /dev/null +++ b/libtu/.gitignore @@ -0,0 +1,2 @@ +*.o +libtu.a diff --git a/libtu/Makefile b/libtu/Makefile new file mode 100644 index 00000000..5b26fcd6 --- /dev/null +++ b/libtu/Makefile @@ -0,0 +1,43 @@ +## +## Tu Makefile +## + +# System-specific configuration is in system.mk +ifeq ($(MAKELEVEL),0) +TOPDIR=. +else +TOPDIR=.. +endif + +# System-specific configuration +include $(TOPDIR)/build/system-inc.mk + +###################################### + +#INCLUDES += $(LIBTU_INCLUDES) $(LUA_INCLUDES) + +CFLAGS += $(C98_SOURCE) $(POSIX_SOURCE) $(WARN) + +SOURCES=iterable.c map.c misc.c obj.c objlist.c optparser.c output.c parser.c prefix.c ptrlist.c rb.c setparam.c stringstore.c tokenizer.c util.c errorlog.c + +HEADERS=debug.h errorlog.h locale.h minmax.h obj.h objp.h output.h pointer.h private.h rb.h stringstore.h types.h dlist.h iterable.h map.h misc.h objlist.h optparser.h parser.h prefix.h ptrlist.h setparam.h tokenizer.h util.h + +TARGETS=libtu.a + +###################################### + +include $(TOPDIR)/build/rules.mk + +###################################### + +libtu.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +install: + $(INSTALLDIR) $(DESTDIR)$(LIBDIR) + $(INSTALL) -m $(DATA_MODE) libtu.a $(DESTDIR)$(LIBDIR) + $(INSTALLDIR) $(DESTDIR)$(INCDIR)/libtu + for h in $(HEADERS); do \ + $(INSTALL) -m $(DATA_MODE) $$h $(DESTDIR)$(INCDIR)/libtu; \ + done diff --git a/libtu/build/rules.mk b/libtu/build/rules.mk new file mode 100644 index 00000000..3d5eda75 --- /dev/null +++ b/libtu/build/rules.mk @@ -0,0 +1,207 @@ +## +## Some make rules +## + +# Beware: in releases, the Notion rules.mk is used to build both libtu, +# libextl and notion - so structural changes to this file should also be +# carried out on the Notion rules.mk + +ifdef MODULE +ifeq ($(PRELOAD_MODULES),1) +MODULE_TARGETS := $(MODULE).a $(MODULE).lc +else +MODULE_TARGETS := $(MODULE).so $(MODULE).lc +endif +TARGETS := $(TARGETS) $(MODULE_TARGETS) +endif + +ifdef LUA_SOURCES +LUA_COMPILED := $(subst .lua,.lc, $(LUA_SOURCES)) +TARGETS := $(TARGETS) $(LUA_COMPILED) +endif + + +# Main targets +###################################### + +.PHONY: subdirs +.PHONY: subdirs-clean +.PHONY: subdirs-realclean +.PHONY: subdirs-depend +.PHONY: subdirs-install +.PHONY: _install +.PHONY: _depend +.PHONY: _exports + +all: subdirs _exports $(TARGETS) + +clean: subdirs-clean _clean + +realclean: subdirs-realclean _clean _realclean + +depend: subdirs-depend _depend + +install: subdirs-install _install + + +# Exports +###################################### + +ifdef MAKE_EXPORTS + +EXPORTS_C = exports.c +EXPORTS_H = exports.h + +DEPEND_DEPENDS += $(EXPORTS_H) + +TO_CLEAN := $(TO_CLEAN) $(EXPORTS_C) $(EXPORTS_H) + +_exports: $(EXPORTS_C) + +$(EXPORTS_H): $(EXPORTS_C) + +$(EXPORTS_C): + $(MKEXPORTS) -module $(MAKE_EXPORTS) -o $(EXPORTS_C) -h $(EXPORTS_H) $(SOURCES) + +else # !MAKE_EXPORTS + +EXPORTS_C = +EXPORTS_H = + +endif # !MAKE_EXPORTS + + +# Compilation and linking +###################################### + +OBJS=$(subst .c,.o,$(SOURCES) $(EXPORTS_C)) + +ifdef MODULE + +ifneq ($(PRELOAD_MODULES),1) + +CC_PICFLAGS=-fPIC -DPIC +LD_SHAREDFLAGS=-shared + +%.o: %.c + $(CC) $(CC_PICFLAGS) $(CFLAGS) -c $< -o $@ + +$(MODULE).so: $(OBJS) $(EXT_OBJS) + $(CC) $(LD_SHAREDFLAGS) $(LDFLAGS) $(OBJS) $(EXT_OBJS) -o $@ + + +module_install: module_stub_install + $(INSTALLDIR) $(MODULEDIR) + $(INSTALL) -m $(BIN_MODE) $(MODULE).so $(MODULEDIR) + +else # PRELOAD_MODULES + +PICOPT=-fPIC -DPIC +LINKOPT=-shared + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(MODULE).a: $(OBJS) $(EXT_OBJS) + $(AR) $(ARFLAGS) $@ $+ + $(RANLIB) $@ + +module_install: module_stub_install + +endif # PRELOAD_MODULES + +module_stub_install: + $(INSTALLDIR) $(LCDIR) + $(INSTALL) -m $(DATA_MODE) $(MODULE).lc $(LCDIR) + +ifndef MODULE_STUB + +$(MODULE).lc: + echo "ioncore.load_module('$(MODULE)')" | $(LUAC) -o $@ - +else + +LUA_SOURCES += $(MODULE_STUB) + +endif #MODULE_STUB + +else # !MODULE + + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + + +endif# !MODULE + + +# Clean rules +###################################### + +_clean: + $(RM) -f $(TO_CLEAN) core $(DEPEND_FILE) $(OBJS) + +_realclean: + $(RM) -f $(TO_REALCLEAN) $(TARGETS) + +# Lua rules +###################################### + +%.lc: %.lua + $(LUAC) -o $@ $< + +lc_install: + $(INSTALLDIR) $(LCDIR) + for i in $(LUA_COMPILED); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(LCDIR); \ + done + +etc_install: + $(INSTALLDIR) $(ETCDIR) + for i in $(ETC); do \ + $(INSTALL) -m $(DATA_MODE) $$i $(ETCDIR); \ + done + +# Dependencies +###################################### + +ifdef SOURCES + +_depend: $(DEPEND_DEPENDS) + $(MAKE_DEPEND) + +ifeq ($(DEPEND_FILE),$(wildcard $(DEPEND_FILE))) +include $(DEPEND_FILE) +endif + +endif + +# Subdirectories +###################################### + +ifdef SUBDIRS + +subdirs: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done + +subdirs-depend: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i depend; done + +subdirs-clean: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i clean; done + +subdirs-realclean: + set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i realclean; done + +subdirs-install: + set -e; for i in $(INSTALL_SUBDIRS); do $(MAKE) -C $$i install; done + +endif + +# Localisation +###################################### + +TO_CLEAN += potfiles_c potfiles_lua + +_potfiles: + echo "$(SOURCES)"|tr ' ' '\n' > potfiles_c + echo "$(LUA_SOURCES) $(ETC)"|tr ' ' '\n' > potfiles_lua diff --git a/libtu/build/system-inc.mk b/libtu/build/system-inc.mk new file mode 100644 index 00000000..2875cce8 --- /dev/null +++ b/libtu/build/system-inc.mk @@ -0,0 +1,18 @@ +# Use system-ac.mk if it exist, system.mk otherwise. + +ifndef TOPDIR + TOPDIR=. +endif + +SYSTEM_MK = $(TOPDIR)/system-autodetect.mk +AC_SYSTEM_MK = $(TOPDIR)/build/ac/system-ac.mk + +ifeq ($(AC_SYSTEM_MK),$(wildcard $(AC_SYSTEM_MK))) + # Using system-ac.mk + include $(AC_SYSTEM_MK) +else + # Not using system-ac.mk + include $(SYSTEM_MK) +endif + +-include $(TOPDIR)/system-local.mk diff --git a/libtu/debug.h b/libtu/debug.h new file mode 100644 index 00000000..729222d5 --- /dev/null +++ b/libtu/debug.h @@ -0,0 +1,19 @@ +/* + * libtu/debug.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_DEBUG_H +#define LIBTU_DEBUG_H + +#ifdef CF_DEBUG +#define D(X) X +#else +#define D(X) +#endif + +#endif /* LIBTU_DEBUG_H */ diff --git a/libtu/dlist.h b/libtu/dlist.h new file mode 100644 index 00000000..9576771d --- /dev/null +++ b/libtu/dlist.h @@ -0,0 +1,124 @@ +/* + * libtu/common.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_DLIST_H +#define LIBTU_DLIST_H + + +/*{{{ Linking */ + + +#define LINK_ITEM(LIST, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=NULL; \ + if((LIST)==NULL){ \ + (LIST)=(ITEM); \ + (ITEM)->PREV=(ITEM); \ + }else{ \ + (ITEM)->PREV=(LIST)->PREV; \ + (ITEM)->PREV->NEXT=(ITEM); \ + (LIST)->PREV=(ITEM); \ + } + + +#define LINK_ITEM_FIRST(LIST, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=(LIST); \ + if((LIST)==NULL){ \ + (ITEM)->PREV=(ITEM); \ + }else{ \ + (ITEM)->PREV=(LIST)->PREV; \ + (LIST)->PREV=(ITEM); \ + } \ + (LIST)=(ITEM); + + +#define LINK_ITEM_LAST LINK_ITEM + + +#define LINK_ITEM_BEFORE(LIST, BEFORE, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=(BEFORE); \ + (ITEM)->PREV=(BEFORE)->PREV; \ + (BEFORE)->PREV=(ITEM); \ + if((BEFORE)==(LIST)) \ + (LIST)=(ITEM); \ + else \ + (ITEM)->PREV->NEXT=(ITEM) + + +#define LINK_ITEM_AFTER(LIST, AFTER, ITEM, NEXT, PREV) \ + (ITEM)->NEXT=(AFTER)->NEXT; \ + (ITEM)->PREV=(AFTER); \ + (AFTER)->NEXT=(ITEM); \ + if((ITEM)->NEXT==NULL) \ + (LIST)->PREV=(ITEM); \ + else \ + (ITEM)->NEXT->PREV=ITEM; + + +#define UNLINK_ITEM(LIST, ITEM, NEXT, PREV) \ + if((ITEM)->PREV!=NULL){ \ + if((ITEM)==(LIST)){ \ + (LIST)=(ITEM)->NEXT; \ + if((LIST)!=NULL) \ + (LIST)->PREV=(ITEM)->PREV; \ + }else if((ITEM)->NEXT==NULL){ \ + (ITEM)->PREV->NEXT=NULL; \ + (LIST)->PREV=(ITEM)->PREV; \ + }else{ \ + (ITEM)->PREV->NEXT=(ITEM)->NEXT; \ + (ITEM)->NEXT->PREV=(ITEM)->PREV; \ + } \ + } \ + (ITEM)->NEXT=NULL; \ + (ITEM)->PREV=NULL; + + +/*}}}*/ + + +/*{{{ Iteration */ + + +#define LIST_FIRST(LIST, NEXT, PREV) \ + (LIST) +#define LIST_LAST(LIST, NEXT, PREV) \ + ((LIST)==NULL ? NULL : LIST_PREV_WRAP(LIST, LIST, NEXT, PREV)) +#define LIST_NEXT(LIST, REG, NEXT, PREV) \ + ((REG)->NEXT) +#define LIST_PREV(LIST, REG, NEXT, PREV) \ + ((REG)->PREV->NEXT ? (REG)->PREV : NULL) +#define LIST_NEXT_WRAP(LIST, REG, NEXT, PREV) \ + (((REG) && (REG)->NEXT) ? (REG)->NEXT : (LIST)) +#define LIST_PREV_WRAP(LIST, REG, NEXT, PREV) \ + ((REG) ? (REG)->PREV : (LIST)) + +#define LIST_FOR_ALL(LIST, NODE, NEXT, PREV) \ + for(NODE=LIST; NODE!=NULL; NODE=(NODE)->NEXT) + +#define LIST_FOR_ALL_REV(LIST, NODE, NEXT, PREV) \ + for(NODE=((LIST)==NULL ? NULL : (LIST)->PREV); \ + NODE!=NULL; \ + NODE=((NODE)==(LIST) ? NULL : (NODE)->PREV)) + +#define LIST_FOR_ALL_W_NEXT(LIST, NODE, NXT, NEXT, PREV) \ + for(NODE=LL, NXT=(NODE==NULL ? NULL : (NODE)->NEXT); \ + NODE!=NULL; \ + NODE=NXT, NXT=(NODE==NULL ? NULL : (NODE)->NEXT)) + +#define LIST_FOR_ALL_W_NEXT_REV(LIST, NODE, NXT, NEXT, PREV) \ + for(NODE=((LIST)==NULL ? NULL : (LIST)->PREV), \ + NXT=((NODE)==(LIST) ? NULL : (NODE)->PREV); \ + NODE!=NULL; \ + NODE=NXT, \ + NXT=((NODE)==(LIST) ? NULL : (NODE)->PREV)) + + +/*}}}*/ + + +#endif /* LIBTU_DLIST_H */ diff --git a/libtu/errorlog.c b/libtu/errorlog.c new file mode 100644 index 00000000..f2b787aa --- /dev/null +++ b/libtu/errorlog.c @@ -0,0 +1,127 @@ +/* + * libtu/errorlog.c + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include + +#include "util.h" +#include "types.h" +#include "output.h" +#include "misc.h" +#include "errorlog.h" + +static ErrorLog *current_log=NULL; + +static void add_to_log(ErrorLog *el, const char *message, int l) +{ + if(message==NULL) + return; + + /* Also write to stderr */ + fwrite(message, sizeof(char), l, stderr); + + if(el==NULL) + return; + + if(el->file!=NULL){ + el->errors=TRUE; + fwrite(message, sizeof(char), l, el->file); + return; + } + + if(el->msgs==NULL){ + el->msgs=ALLOC_N(char, ERRORLOG_MAX_SIZE); + if(el->msgs==NULL){ + fprintf(stderr, "%s: %s\n", libtu_progname(), strerror(errno)); + return; + } + el->msgs[0]=0; + el->msgs_len=0; + } + + el->errors=TRUE; + + if(l+el->msgs_len>ERRORLOG_MAX_SIZE-1){ + int n=0; + if(lmsgs, el->msgs+el->msgs_len-n, n); + } + memcpy(el->msgs+n, message+l-(ERRORLOG_MAX_SIZE-1-n), + ERRORLOG_MAX_SIZE-1-n); + el->msgs[ERRORLOG_MAX_SIZE]='\0'; + el->msgs_len=ERRORLOG_MAX_SIZE-1; + }else{ + memcpy(el->msgs+el->msgs_len, message, l); + el->msgs[el->msgs_len+l]='\0'; + el->msgs_len+=l; + } +} + + +static void log_warn_handler(const char *message) +{ + const char *p=strchr(message, '\n'); + static int lineno=0; + int alternat=0; + + add_to_log(current_log, lineno==0 ? ">> " : " ", 3); + + if(p!=NULL){ + add_to_log(current_log, message, p-message+1); + lineno++; + log_warn_handler(p+1); + lineno--; + return; + } + + add_to_log(current_log, message, strlen(message)); + add_to_log(current_log, "\n", 1); +} + + +void errorlog_begin_file(ErrorLog *el, FILE *file) +{ + el->msgs=NULL; + el->msgs_len=0; + el->file=file; + el->prev=current_log; + el->errors=FALSE; + el->old_handler=set_warn_handler(log_warn_handler); + current_log=el; +} + + +void errorlog_begin(ErrorLog *el) +{ + errorlog_begin_file(el, NULL); +} + + +bool errorlog_end(ErrorLog *el) +{ + current_log=el->prev; + set_warn_handler(el->old_handler); + el->prev=NULL; + el->old_handler=NULL; + return el->errors; +} + + +void errorlog_deinit(ErrorLog *el) +{ + if(el->msgs!=NULL) + free(el->msgs); + el->msgs=NULL; + el->msgs_len=0; + el->file=NULL; + el->errors=FALSE; +} + diff --git a/libtu/errorlog.h b/libtu/errorlog.h new file mode 100644 index 00000000..93da8baa --- /dev/null +++ b/libtu/errorlog.h @@ -0,0 +1,38 @@ +/* + * libtu/errorlog.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_ERRORLOG_H +#define LIBTU_ERRORLOG_H + +#include + +#include "types.h" +#include "obj.h" +#include "output.h" + +#define ERRORLOG_MAX_SIZE (1024*4) + +INTRSTRUCT(ErrorLog); +DECLSTRUCT(ErrorLog){ + char *msgs; + int msgs_len; + FILE *file; + bool errors; + ErrorLog *prev; + WarnHandler *old_handler; +}; + +/* el is assumed to be uninitialised */ +extern void errorlog_begin(ErrorLog *el); +extern void errorlog_begin_file(ErrorLog *el, FILE *file); +/* For errorlog_end el Must be the one errorlog_begin was last called with */ +extern bool errorlog_end(ErrorLog *el); +extern void errorlog_deinit(ErrorLog *el); + +#endif /* LIBTU_ERRORLOG_H */ diff --git a/libtu/install-sh b/libtu/install-sh new file mode 100644 index 00000000..e9de2384 --- /dev/null +++ b/libtu/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/libtu/iterable.c b/libtu/iterable.c new file mode 100644 index 00000000..63b00c18 --- /dev/null +++ b/libtu/iterable.c @@ -0,0 +1,51 @@ +/* + * libtu/iterable.c + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include "iterable.h" + + +void *iterable_nth(uint n, VoidIterator *iter, void *st) +{ + void *p; + + while(1){ + p=iter(st); + if(p==NULL || n==0) + break; + n--; + } + + return p; +} + + +bool iterable_is_on(void *p, VoidIterator *iter, void *st) +{ + while(1){ + void *p2=iter(st); + if(p2==NULL) + return FALSE; + if(p==p2) + return TRUE; + } +} + + +void *iterable_find(BoolFilter *f, void *fparam, + VoidIterator *iter, void *st) +{ + while(1){ + void *p=iter(st); + if(p==NULL) + return NULL; + if(f(p, fparam)) + return p; + } +} + diff --git a/libtu/iterable.h b/libtu/iterable.h new file mode 100644 index 00000000..8b70b09d --- /dev/null +++ b/libtu/iterable.h @@ -0,0 +1,29 @@ +/* + * libtu/iterable.h + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_ITERABLE_H +#define LIBTU_ITERABLE_H + +#include "types.h" +#include "obj.h" + +typedef void *VoidIterator(void *); +typedef Obj *ObjIterator(void *); + +typedef bool BoolFilter(void *p, void *param); + +#define FOR_ALL_ITER(INIT, ITER, VAR, LL, TMP) \ + for(INIT(TMP, LL), (VAR)=ITER(TMP); (VAR)!=NULL; VAR=ITER(TMP)) + +extern void *iterable_nth(uint n, VoidIterator *iter, void *st); +extern bool iterable_is_on(void *p, VoidIterator *iter, void *st); +extern void *iterable_find(BoolFilter *f, void *fparam, + VoidIterator *iter, void *st); + +#endif /* LIBTU_ITERABLE_H */ diff --git a/libtu/locale.h b/libtu/locale.h new file mode 100644 index 00000000..826dd142 --- /dev/null +++ b/libtu/locale.h @@ -0,0 +1,27 @@ +/* + * libtu/locale.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_LOCALE_H +#define LIBTU_LOCALE_H + +#ifdef CF_NO_GETTEXT + +#define TR(X) X +#define DUMMY_TR(X) X + +#else + +#include + +#define TR(X) gettext(X) +#define DUMMY_TR(X) X + +#endif + +#endif /* LIBTU_LOCALE_H */ diff --git a/libtu/map.c b/libtu/map.c new file mode 100644 index 00000000..98f0dd60 --- /dev/null +++ b/libtu/map.c @@ -0,0 +1,83 @@ +/* + * libtu/map.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include "map.h" + + +int stringintmap_ndx(const StringIntMap *map, const char *str) +{ + int i; + + for(i=0; map[i].string!=NULL; i++){ + if(strcmp(str, map[i].string)==0) + return i; + } + + return -1; +} + + +int stringintmap_value(const StringIntMap *map, const char *str, int dflt) +{ + int i=stringintmap_ndx(map, str); + return (i==-1 ? dflt : map[i].value); +} + + +const char *stringintmap_key(const StringIntMap *map, int value, + const char *dflt) +{ + int i; + + for(i=0; map[i].string!=NULL; ++i){ + if(map[i].value==value){ + return map[i].string; + } + } + + return dflt; + +} + + + +int stringfunptrmap_ndx(const StringFunPtrMap *map, const char *str) +{ + int i; + + for(i=0; map[i].string!=NULL; i++){ + if(strcmp(str, map[i].string)==0) + return i; + } + + return -1; +} + + +FunPtr stringfunptrmap_value(const StringFunPtrMap *map, const char *str, FunPtr dflt) +{ + int i=stringfunptrmap_ndx(map, str); + return (i==-1 ? dflt : map[i].value); +} + + +const char *stringfunptrmap_key(const StringFunPtrMap *map, FunPtr value, + const char *dflt) +{ + int i; + + for(i=0; map[i].string!=NULL; ++i){ + if(map[i].value==value){ + return map[i].string; + } + } + + return dflt; +} diff --git a/libtu/map.h b/libtu/map.h new file mode 100644 index 00000000..3a0db395 --- /dev/null +++ b/libtu/map.h @@ -0,0 +1,54 @@ +/* + * libtu/map.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_MAP_H +#define LIBTU_MAP_H + +typedef struct _StringIntMap{ + const char *string; + int value; +} StringIntMap; + +#define END_STRINGINTMAP {NULL, 0} + +/* Return the index of str in map or -1 if not found. */ +extern int stringintmap_ndx(const StringIntMap *map, const char *str); +extern int stringintmap_value(const StringIntMap *map, const char *str, + int dflt); +extern const char *stringintmap_key(const StringIntMap *map, + int value, const char *dflt); + + + +typedef void (*FunPtr)(void); + +#define DECLFUNPTRMAP(NAME) \ +typedef struct _String##NAME##Map{\ + const char *string;\ + NAME value;\ +} String##NAME##Map + +DECLFUNPTRMAP(FunPtr); + +#define END_STRINGPTRMAP {NULL, NULL} + +/* Return the index of str in map or -1 if not found. */ +extern int stringfunptrmap_ndx(const StringFunPtrMap *map, const char *str); +extern FunPtr stringfunptrmap_value(const StringFunPtrMap *map, const char *str, + FunPtr dflt); +extern const char *stringfunptrmap_key(const StringFunPtrMap *map, + FunPtr value, const char *dflt); + + +#define STRINGFUNPTRMAP_NDX(MAP,STR) stringfunptrmap_ndx((StringFunPtrMap *)MAP, STR) +#define STRINGFUNPTRMAP_VALUE(TYPE,MAP,STR,DFLT) (TYPE)stringfunptrmap_value((StringFunPtrMap *)MAP, STR, (FunPtr)DFLT) +#define STRINGFUNPTRMAP_KEY(MAP,VALUE,DFLT) stringfunptrmap_key((StringFunPtrMap *)MAP, (FunPtr)VALUE, DFLT) + + +#endif /* LIBTU_MAP_H */ diff --git a/libtu/minmax.h b/libtu/minmax.h new file mode 100644 index 00000000..5866d7fd --- /dev/null +++ b/libtu/minmax.h @@ -0,0 +1,27 @@ +/* + * libtu/minmax.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_MINMAX_H +#define LIBTU_MINMAX_H + + +static int minof(int x, int y) +{ + return (xy ? x : y); +} + + +#endif /* LIBTU_MINMAX_H */ + diff --git a/libtu/misc.c b/libtu/misc.c new file mode 100644 index 00000000..39137c3b --- /dev/null +++ b/libtu/misc.c @@ -0,0 +1,211 @@ +/* + * libtu/misc.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include +#include + +#include "misc.h" +#include "output.h" + + +void *malloczero(size_t size) +{ + void *p=malloc(size); + + if(p!=NULL) + memset(p, 0, size); + else + warn_err(); + + return p; +} + + +void *remalloczero(void *ptr, size_t oldsize, size_t newsize) +{ + void *p=NULL; + + if(newsize!=0){ + p=realloc(ptr, newsize); + + if(p==NULL){ + warn_err(); + return NULL; + } + + if(newsize>oldsize) + memset((char*)p+oldsize, 0, newsize-oldsize); + } + + return p; +} + + +char *scopyn(const char *p, size_t l) +{ + char *pn=ALLOC_N(char, l+1); + + if(pn==NULL) + return NULL; + + memcpy(pn, p, l); + pn[l]='\0'; + + return pn; +} + + +char *scopy(const char *p) +{ + size_t l=strlen(p); + return scopyn(p, l); +} + + +char *scat(const char *p1, const char *p2) +{ + size_t l1, l2; + char*pn; + + l1=strlen(p1); + l2=strlen(p2); + + pn=ALLOC_N(char, l1+l2+1); + + if(pn==NULL) + return NULL; + + memcpy(pn, p1, l1); + memcpy(pn+l1, p2, l2+1); + + return pn; +} + + +char *scat3(const char *p1, const char *p2, const char *p3) +{ + size_t l1, l2, l3; + char *pn; + + l1=strlen(p1); + l2=strlen(p2); + l3=strlen(p3); + + pn=ALLOC_N(char, l1+l2+l3+1); + + if(pn==NULL) + return NULL; + + memcpy(pn, p1, l1); + memcpy(pn+l1, p2, l2); + memcpy(pn+l1+l2, p3, l3+1); + + return pn; +} + + +char *scatn(const char *s1, ssize_t l1, const char *s2, ssize_t l2) +{ + size_t tlen=1; + char *s; + + if(l1<0) + l1=strlen(s1); + + if(l2<0) + l2=strlen(s2); + + tlen+=l1+l2; + + s=ALLOC_N(char, tlen); + + if(s==NULL) + return NULL; + + memcpy(s, s1, l1); + memcpy(s+l1, s2, l2); + s[l1+l2]='\0'; + + return s; +} + + +/* */ + + +const char *simple_basename(const char *name) +{ + const char *p; + + p=name+strlen(name)-1; + + /* Skip any trailing slashes */ + while(*p=='/'){ + /* root? */ + if(p==name) + return name; + p--; + } + + while(p!=name){ + if(*p=='/') + return p+1; + p--; + } + + return name; +} + + +void stripws(char *p) +{ + int l; + + l=strspn(p, " "); + if(l!=0) + strcpy(p, p+l); + l=strlen(p); + + while(--l>=0){ + if(*(p+l)!=' ') + break; + } + *(p+l+1)='\0'; +} + + +const char *libtu_strcasestr(const char *str, const char *ptn) +{ + const char *s2, *p2; + for(; *str; str++) { + for(s2=str, p2=ptn; ; s2++, p2++) { + if(!*p2) + return str; + if(toupper(*s2)!=toupper(*p2)) + break; + } + } + return NULL; +} + +/* */ + + +bool readf(FILE *f, void *buf, size_t n) +{ + return fread(buf, 1, n, f)!=1; +} + + +bool writef(FILE *f, const void *buf, size_t n) +{ + return fwrite(buf, 1, n, f)!=1; +} diff --git a/libtu/misc.h b/libtu/misc.h new file mode 100644 index 00000000..a8e6d3fd --- /dev/null +++ b/libtu/misc.h @@ -0,0 +1,43 @@ +/* + * libtu/misc.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_MISC_H +#define LIBTU_MISC_H + +#include +#include +#include +#include "types.h" + +#define ALLOC(X) (X*)malloczero(sizeof(X)) +#define ALLOC_N(X, N) (X*)malloczero(sizeof(X)*(N)) +#define REALLOC_N(PTR, X, S, N) (X*)remalloczero(PTR, sizeof(X)*(S), sizeof(X)*(N)) + +#define FREE(X) do{if(X!=NULL)free(X);}while(0) + +#define XOR(X, Y) (((X)==0) != ((Y)==0)) + +extern void* malloczero(size_t size); +extern void* remalloczero(void *ptr, size_t oldsize, size_t newsize); + +extern char* scopy(const char *p); +extern char* scopyn(const char *p, size_t n); +extern char* scat(const char *p1, const char *p2); +extern char* scatn(const char *p1, ssize_t n1, const char *p2, ssize_t n2); +extern char* scat3(const char *p1, const char *p2, const char *p3); +extern void stripws(char *p); +extern const char *libtu_strcasestr(const char *str, const char *ptn); + +extern const char* simple_basename(const char *name); + +/* I dislike fread and fwrite... */ +extern bool readf(FILE *fd, void *buf, size_t n); +extern bool writef(FILE *fd, const void *buf, size_t n); + +#endif /* LIBTU_MISC_H */ diff --git a/libtu/np/np-conv.h b/libtu/np/np-conv.h new file mode 100644 index 00000000..7a1e5378 --- /dev/null +++ b/libtu/np/np-conv.h @@ -0,0 +1,121 @@ +/* + * libtu/np-conv.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#ifdef NP_SIMPLE_IMPL + +#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \ + { \ + if(num->type!=NPNUM_INT) \ + return E_TOKZ_NOTINT; \ + \ + if(!num->negative){ \ + *ret=num->ival; \ + if(allow_uns_big?num->ival>UMAX:num->ival>MAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->ival; \ + if(num->ival>-(ulong)MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ + } + +#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \ + { \ + if(num->type!=NPNUM_INT) \ + return E_TOKZ_NOTINT; \ + \ + if(!num->negative){ \ + *ret=num->ival; \ + if(num->ival>UMAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->ival; \ + if(!allow_neg || num->ival>(ulong)-MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ + } + +#define FN_NUM_TO_FLOAT(T, POW) \ + static int num_to_##T(T *ret, const NPNum *num) \ + { \ + *ret=(num->negative?-num->fval:num->fval); \ + return 0; \ + } + +#else /* NP_SIMPLE_IMPL */ + +#define FN_NUM_TO_SIGNED(T, UMAX, MAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_uns_big) \ + { \ + if(num->exponent) \ + return E_TOKZ_NOTINT; \ + if(num->nmantissa>0) \ + return E_TOKZ_RANGE; \ + \ + if(!num->negative){ \ + *ret=num->mantissa[0]; \ + if(allow_uns_big?num->mantissa[0]>UMAX:num->mantissa[0]>MAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->mantissa[0]; \ + if(num->mantissa[0]>-(ulong)MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ +} + +#define FN_NUM_TO_UNSIGNED(T, UMAX, MIN) \ + static int num_to_##T(T *ret, const NPNum *num, bool allow_neg) \ + { \ + if(num->exponent) \ + return E_TOKZ_NOTINT; \ + if(num->nmantissa>0) \ + return E_TOKZ_RANGE; \ + \ + if(!num->negative){ \ + *ret=num->mantissa[0]; \ + if(num->mantissa[0]>UMAX) \ + return E_TOKZ_RANGE; \ + }else{ \ + *ret=-num->mantissa[0]; \ + if(!allow_neg || num->mantissa[0]>(ulong)-MIN) \ + return E_TOKZ_RANGE; \ + } \ + return 0; \ +} + + +#define FN_NUM_TO_FLOAT(T, POW) \ + static int num_to_##T(T *ret, const NPNum *num) \ + { \ + T d=0; \ + int i; \ + \ + for(i=num->nmantissa;i>=0;i--) \ + d=d*(T)(ULONG_MAX+1.0)+num->mantissa[i]; \ + \ + d*=POW(num->base, num->exponent); \ + *ret=d; \ + \ + return 0; \ + } + +#endif /* NP_SIMPLE_IMPL */ + +FN_NUM_TO_SIGNED(long, ULONG_MAX, LONG_MAX, LONG_MIN) +FN_NUM_TO_SIGNED(char, UCHAR_MAX, CHAR_MAX, CHAR_MIN) +FN_NUM_TO_FLOAT(double, pow) + +#undef NEG diff --git a/libtu/np/numparser2.h b/libtu/np/numparser2.h new file mode 100644 index 00000000..213c4e05 --- /dev/null +++ b/libtu/np/numparser2.h @@ -0,0 +1,272 @@ +/* + * libtu/numparser2.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#define MAX_MANTISSA 10 /* should be enough for our needs */ +#define ULONG_SIZE (sizeof(ulong)*8) + +enum{ + NPNUM_INT, + NPNUM_FLOAT +}; + +#ifdef NP_SIMPLE_IMPL + +typedef struct _NPNum{ + int type; + int base; + bool negative; + double fval; + ulong ival; +} NPNum; + +#define NUM_INIT {0, 0, 0, 0.0, 0} + +static int npnum_mulbase_add(NPNum *num, long base, long v) +{ + double iold=num->ival; + + num->fval=num->fval*base+(double)v; + + num->ival*=base; + + if(num->ivaltype=NPNUM_FLOAT; + + num->ival+=v; + + return 0; +} + +#else /* NP_SIMPLE_IMPL */ + +typedef struct _NPNum{ + unsigned char nmantissa; + int type; + int base; + bool negative; + ulong mantissa[MAX_MANTISSA]; + long exponent; +} NPNum; + +#define NUM_INIT {0, 0, 0, 0, {0,}, 0} + +#define ADD_EXP(NUM, X) (NUM)->exponent+=(X); + +#if defined(__GNUG__) && defined(i386) && !defined(NP_NO_I386_ASM) + #define NP_I386_ASM +#endif + +static int npnum_mulbase_add(NPNum *num, long base, long v) +{ + long i, j; + ulong overflow; +#ifndef NP_I386_ASM + ulong val; +#endif + + for(i=num->nmantissa;i>=0;i--){ +#ifdef NP_I386_ASM + __asm__("mul %4\n" + : "=a" (num->mantissa[i]), "=d" (overflow) + : "0" (num->mantissa[i]), "1" (0), "q" (base) + : "eax", "edx"); +#else + overflow=0; + val=num->mantissa[i]; + + if(valmantissa[i]=val; +#endif + if(overflow){ + if(i==num->nmantissa){ + if(num->nmantissa==MAX_MANTISSA) + return E_TOKZ_TOOBIG; + num->nmantissa++; + } + num->mantissa[i+1]+=overflow; + } + } + num->mantissa[0]+=v; + + return 0; +} + +#undef NP_I386_ASM + +#endif /* NP_SIMPLE_IMPL */ + + +/* */ + + +static bool is_end(int c) +{ + /* oops... EOF was missing */ + return (c==EOF || (c!='.' && ispunct(c)) || isspace(c) || iscntrl(c)); +} + + +/* */ + + +static int parse_exponent(NPNum *num, Tokenizer *tokz, int c) +{ + long exp=0; + bool neg=FALSE; + int err=0; + + c=GETCH(); + + if(c=='-' || c=='+'){ + if(c=='-') + neg=TRUE; + c=GETCH(); + } + + for(; 1; c=GETCH()){ + if(isdigit(c)){ + exp*=10; + exp+=c-'0'; + }else if(is_end(c)){ + UNGETCH(c); + break; + }else{ + err=E_TOKZ_NUMFMT; + } + } + + if(neg) + exp*=-1; + +#ifndef NP_SIMPLE_IMPL + ADD_EXP(num, exp); +#else + num->fval*=pow(num->base, exp); +#endif + return err; +} + + +static int parse_number(NPNum *num, Tokenizer *tokz, int c) +{ + int base=10; + int dm=1; + int err=0; + int tmp; +#ifdef NP_SIMPLE_IMPL + double divisor=base; +#endif + + if(c=='-' || c=='+'){ + if(c=='-') + num->negative=TRUE; + c=GETCH(); + if(!isdigit(c)) + err=E_TOKZ_NUMFMT; + } + + if(c=='0'){ + dm=0; + c=GETCH(); + if(c=='x' || c=='X'){ + base=16; + c=GETCH(); + }else if(c=='b' || c=='B'){ + base=2; + c=GETCH(); + }else if('0'<=c && c<='7'){ + base=8; + }else{ + dm=2; + } + } + + num->base=base; + + for(; 1; c=GETCH()){ + if((c=='e' || c=='E') && dm!=0){ + if(dm<2){ + err=E_TOKZ_NUMFMT; + continue; + } + tmp=parse_exponent(num, tokz, c); + if(err==0) + err=tmp; + break; + } + + if(isxdigit(c)){ + if('0'<=c && c<='9') + c-='0'; + else if(isupper(c)) + c-='A'-10; + else + c-='a'-10; + + if(c>=base) + err=E_TOKZ_NUMFMT; + +#ifdef NP_SIMPLE_IMPL + if(dm==3){ + num->fval+=(double)c/divisor; + divisor*=base; + }else +#endif + { + tmp=npnum_mulbase_add(num, base, c); + if(err==0) + err=tmp; + } + + if(dm==1) + dm=2; +#ifndef NP_SIMPLE_IMPL + else if(dm==3) + ADD_EXP(num, -1); +#endif + continue; + } + + if(c=='.'){ + if(dm!=2){ + err=E_TOKZ_NUMFMT; + } + dm=3; +#ifdef NP_SIMPLE_IMPL + num->type=NPNUM_FLOAT; + divisor=base; +#endif + continue; + } + + if(is_end(c)){ + UNGETCH(c); + break; + } + + err=E_TOKZ_NUMFMT; + } + +#ifndef NP_SIMPLE_IMPL + num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT); +#endif + + return err; +} diff --git a/libtu/obj.c b/libtu/obj.c new file mode 100644 index 00000000..0f4910df --- /dev/null +++ b/libtu/obj.c @@ -0,0 +1,297 @@ +/* + * libtu/obj.c + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "types.h" +#include "obj.h" +#include "objp.h" +#include "misc.h" +#include "dlist.h" + + +ClassDescr CLASSDESCR(Obj)={"Obj", NULL, 0, NULL, NULL}; + + +static void do_watches(Obj *obj, bool call); + + +/*{{{ Destroy */ + + +void destroy_obj(Obj *obj) +{ + ClassDescr *d; + + if(OBJ_IS_BEING_DESTROYED(obj)) + return; + + obj->flags|=OBJ_DEST; + + do_watches(obj, TRUE); + + d=obj->obj_type; + + while(d!=NULL){ + if(d->destroy_fn!=NULL){ + d->destroy_fn(obj); + break; + } + d=d->ancestor; + } + + do_watches(obj, FALSE); + + free(obj); +} + + +/*}}}*/ + + +/*{{{ is/cast */ + + +bool obj_is(const Obj *obj, const ClassDescr *descr) +{ + ClassDescr *d; + + if(obj==NULL) + return FALSE; + + d=obj->obj_type; + + while(d!=NULL){ + if(d==descr) + return TRUE; + d=d->ancestor; + } + return FALSE; +} + + +bool obj_is_str(const Obj *obj, const char *str) +{ + ClassDescr *d; + + if(obj==NULL || str==NULL) + return FALSE; + + d=obj->obj_type; + + while(d!=NULL){ + if(strcmp(d->name, str)==0) + return TRUE; + d=d->ancestor; + } + return FALSE; +} + + +const void *obj_cast(const Obj *obj, const ClassDescr *descr) +{ + ClassDescr *d; + + if(obj==NULL) + return NULL; + + d=obj->obj_type; + + while(d!=NULL){ + if(d==descr) + return (void*)obj; + d=d->ancestor; + } + return NULL; +} + + +/*}}}*/ + + +/*{{{ Dynamic functions */ + + +/* This function is called when no handler is found. + */ +static void dummy_dyn() +{ +} + + +static int comp_fun(const void *a, const void *b) +{ + void *af=(void*)((DynFunTab*)a)->func; + void *bf=(void*)((DynFunTab*)b)->func; + + return (afobj_type; + + for(; descr!=&Obj_classdescr; descr=descr->ancestor){ + if(descr->funtab==NULL) + continue; + + if(descr->funtab_n==-1){ + /* Need to sort the table. */ + descr->funtab_n=0; + df=descr->funtab; + while(df->func!=NULL){ + descr->funtab_n++; + df++; + } + + if(descr->funtab_n>0){ + qsort(descr->funtab, descr->funtab_n, sizeof(DynFunTab), + comp_fun); + } + } + + /* + if(descr->funtab_n==0) + continue; + + df=(DynFunTab*)bsearch(&dummy, descr->funtab, descr->funtab_n, + sizeof(DynFunTab), comp_fun); + if(df!=NULL){ + *funnotfound=FALSE; + return df->handler; + } + */ + + /* Using custom bsearch instead of the one in libc is probably + * faster as the overhead of calling a comparison function would + * be significant given that the comparisons are simple and + * funtab_n not that big. + */ + { + int min=0, max=descr->funtab_n-1; + int ndx; + df=descr->funtab; + while(max>=min){ + ndx=(max+min)/2; + if((void*)df[ndx].func==(void*)func){ + *funnotfound=FALSE; + return df[ndx].handler; + } + if((void*)df[ndx].func<(void*)func) + min=ndx+1; + else + max=ndx-1; + } + } + } + + *funnotfound=TRUE; + return dummy_dyn; +} + + +bool has_dynfun(const Obj *obj, DynFun *func) +{ + bool funnotfound; + lookup_dynfun(obj, func, &funnotfound); + return !funnotfound; +} + + +/*}}}*/ + + +/*{{{ Watches */ + + +bool watch_setup(Watch *watch, Obj *obj, WatchHandler *handler) +{ + if(OBJ_IS_BEING_DESTROYED(obj)) + return FALSE; + + watch_reset(watch); + + watch->handler=handler; + LINK_ITEM(obj->obj_watches, watch, next, prev); + watch->obj=obj; + + return TRUE; +} + +void do_watch_reset(Watch *watch, bool call) +{ + WatchHandler *handler=watch->handler; + Obj *obj=watch->obj; + + watch->handler=NULL; + + if(obj==NULL) + return; + + UNLINK_ITEM(obj->obj_watches, watch, next, prev); + watch->obj=NULL; + + if(call && handler!=NULL) + handler(watch, obj); +} + + +void watch_reset(Watch *watch) +{ + do_watch_reset(watch, FALSE); +} + + +bool watch_ok(Watch *watch) +{ + return watch->obj!=NULL; +} + + +static void do_watches(Obj *obj, bool call) +{ + Watch *watch, *next; + + watch=obj->obj_watches; + + while(watch!=NULL){ + next=watch->next; + do_watch_reset(watch, call); + watch=next; + } +} + + +void watch_call(Obj *obj) +{ + do_watches(obj, FALSE); +} + + +void watch_init(Watch *watch) +{ + watch->obj=NULL; + watch->next=NULL; + watch->prev=NULL; + watch->handler=NULL; +} + + +/*}}}*/ + diff --git a/libtu/obj.h b/libtu/obj.h new file mode 100644 index 00000000..68ee3dff --- /dev/null +++ b/libtu/obj.h @@ -0,0 +1,69 @@ +/* + * libtu/obj.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OBJ_H +#define LIBTU_OBJ_H + +#include "types.h" + +#define CLASSDESCR(TYPE) TYPE##_classdescr + +#define OBJ_IS(OBJ, TYPE) obj_is((Obj*)OBJ, &CLASSDESCR(TYPE)) +#define OBJ_CAST(OBJ, TYPE) (TYPE*)obj_cast((Obj*)OBJ, &CLASSDESCR(TYPE)) + +#define INTRSTRUCT(STRU) \ + struct STRU##_struct; typedef struct STRU##_struct STRU +#define DECLSTRUCT(STRU) \ + struct STRU##_struct + +#define INTRCLASS(OBJ) INTRSTRUCT(OBJ); extern ClassDescr CLASSDESCR(OBJ) +#define DECLCLASS(OBJ) DECLSTRUCT(OBJ) + +INTRSTRUCT(ClassDescr); +INTRCLASS(Obj); +INTRSTRUCT(Watch); + +extern bool obj_is(const Obj *obj, const ClassDescr *descr); +extern bool obj_is_str(const Obj *obj, const char *str); +extern const void *obj_cast(const Obj *obj, const ClassDescr *descr); + +extern void destroy_obj(Obj *obj); + +DECLCLASS(Obj){ + ClassDescr *obj_type; + Watch *obj_watches; + int flags; +}; + +#define OBJ_DEST 0x0001 +#define OBJ_EXTL_CACHED 0x0002 +#define OBJ_EXTL_OWNED 0x0004 + +#define OBJ_IS_BEING_DESTROYED(OBJ) (((Obj*)(OBJ))->flags&OBJ_DEST) + +#define DYNFUN + +typedef void WatchHandler(Watch *watch, Obj *obj); + +#define WATCH_INIT {NULL, NULL, NULL, NULL} + +DECLSTRUCT(Watch){ + Obj *obj; + Watch *next, *prev; + WatchHandler *handler; +}; + +extern bool watch_setup(Watch *watch, Obj *obj, + WatchHandler *handler); +extern void watch_reset(Watch *watch); +extern bool watch_ok(Watch *watch); +extern void watch_init(Watch *watch); +extern void watch_call(Obj *obj); + +#endif /* LIBTU_OBJ_H */ diff --git a/libtu/objlist.c b/libtu/objlist.c new file mode 100644 index 00000000..9d5062f4 --- /dev/null +++ b/libtu/objlist.c @@ -0,0 +1,332 @@ +/* + * libtu/objlist.c + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include "obj.h" +#include "types.h" +#include "objlist.h" +#include "dlist.h" +#include "misc.h" + + +static ObjList *reuse_first(ObjList **objlist) +{ + ObjList *node=*objlist; + + if(node!=NULL && node->watch.obj==NULL){ + UNLINK_ITEM(*objlist, node, next, prev); + return node; + } + + return NULL; +} + + +static ObjList *reuse_last(ObjList **objlist) +{ + ObjList *node=*objlist; + + if(node==NULL) + return NULL; + + node=node->prev; + + if(node!=NULL && node->watch.obj==NULL){ + UNLINK_ITEM(*objlist, node, next, prev); + return node; + } + + return NULL; +} + + +static ObjList *reuse(ObjList **objlist) +{ + ObjList *first=reuse_first(objlist); + ObjList *last=reuse_first(objlist); + + if(first==NULL){ + return last; + }else{ + if(last!=NULL) + free(last); + return first; + } +} + + +static void optimise(ObjList **objlist) +{ + ObjList *first=reuse_first(objlist); + ObjList *last=reuse_first(objlist); + + if(first!=NULL) + free(first); + if(last!=NULL) + free(last); +} + + +void watch_handler(Watch *watch, Obj *obj) +{ + ObjList *node=(ObjList*)watch; + + assert(node->prev!=NULL); + + if(node->next==NULL){ + /* Last item - can't free */ + }else if(node->prev->next==NULL){ + /* First item - can't free cheaply */ + }else{ + ObjList *tmp=node->prev; + node->next->prev=node->prev; + tmp->next=node->next; + free(node); + } +} + + +static void free_node(ObjList **objlist, ObjList *node) +{ + watch_reset(&(node->watch)); + UNLINK_ITEM(*objlist, node, next, prev); + free(node); +} + + +static ObjList *mknode(void *obj) +{ + ObjList *node; + + if(obj==NULL) + return NULL; + + node=ALLOC(ObjList); + + if(node==NULL) + return FALSE; + + watch_init(&(node->watch)); + + if(!watch_setup(&(node->watch), obj, watch_handler)){ + free(node); + return NULL; + } + + return node; +} + + +static ObjList *objlist_find_node(ObjList *objlist, Obj *obj) +{ + ObjList *node=objlist; + + while(node!=NULL){ + if(node->watch.obj==obj) + break; + node=node->next; + } + + return node; +} + + +bool objlist_contains(ObjList *objlist, Obj *obj) +{ + return (objlist_find_node(objlist, obj)!=NULL); +} + + +bool objlist_insert_last(ObjList **objlist, Obj *obj) +{ + ObjList *node=reuse(objlist); + + if(node==NULL) + node=mknode(obj); + + if(node==NULL) + return FALSE; + + LINK_ITEM_LAST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_insert_first(ObjList **objlist, Obj *obj) +{ + ObjList *node=reuse(objlist); + + if(node==NULL) + node=mknode(obj); + + if(node==NULL) + return FALSE; + + LINK_ITEM_FIRST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_reinsert_last(ObjList **objlist, Obj *obj) +{ + ObjList *node; + + optimise(objlist); + + node=objlist_find_node(*objlist, obj); + + if(node==NULL) + return objlist_insert_last(objlist, obj); + + UNLINK_ITEM(*objlist, node, next, prev); + LINK_ITEM_LAST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_reinsert_first(ObjList **objlist, Obj *obj) +{ + ObjList *node; + + optimise(objlist); + + node=objlist_find_node(*objlist, obj); + + if(node==NULL) + return objlist_insert_first(objlist, obj); + + UNLINK_ITEM(*objlist, node, next, prev); + LINK_ITEM_FIRST(*objlist, node, next, prev); + + return TRUE; +} + + +bool objlist_remove(ObjList **objlist, Obj *obj) +{ + ObjList *node=objlist_find_node(*objlist, obj); + + if(node!=NULL) + free_node(objlist, node); + + optimise(objlist); + + return (node!=NULL); +} + + +void objlist_clear(ObjList **objlist) +{ + while(*objlist!=NULL) + free_node(objlist, *objlist); +} + + +ObjListIterTmp objlist_iter_tmp=NULL; + + +void objlist_iter_init(ObjListIterTmp *state, ObjList *objlist) +{ + *state=objlist; +} + + +Obj *objlist_iter(ObjListIterTmp *state) +{ + Obj *obj=NULL; + + while(obj==NULL && *state!=NULL){ + obj=(*state)->watch.obj; + (*state)=(*state)->next; + } + + return obj; +} + + +void objlist_iter_rev_init(ObjListIterTmp *state, ObjList *objlist) +{ + *state=(objlist==NULL ? NULL : objlist->prev); +} + + +Obj *objlist_iter_rev(ObjListIterTmp *state) +{ + Obj *obj=NULL; + + while(obj==NULL && *state!=NULL){ + obj=(*state)->watch.obj; + *state=(*state)->prev; + if((*state)->next==NULL) + *state=NULL; + } + + return obj; +} + + +bool objlist_empty(ObjList *objlist) +{ + ObjListIterTmp tmp; + Obj *obj; + + FOR_ALL_ON_OBJLIST(Obj*, obj, objlist, tmp){ + return FALSE; + } + + return TRUE; +} + + +Obj *objlist_take_first(ObjList **objlist) +{ + ObjList *node; + Obj*obj; + + optimise(objlist); + + node=*objlist; + + if(node==NULL) + return NULL; + + obj=node->watch.obj; + + assert(obj!=NULL); + + free_node(objlist, node); + + return obj; +} + + +Obj *objlist_take_last(ObjList **objlist) +{ + ObjList *node; + Obj*obj; + + optimise(objlist); + + node=*objlist; + + if(node==NULL) + return NULL; + + node=node->prev; + + obj=node->watch.obj; + + assert(obj!=NULL); + + free_node(objlist, node); + + return obj; +} diff --git a/libtu/objlist.h b/libtu/objlist.h new file mode 100644 index 00000000..62533cb5 --- /dev/null +++ b/libtu/objlist.h @@ -0,0 +1,61 @@ +/* + * libtu/objlist.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OBJLIST_H +#define LIBTU_OBJLIST_H + +#include "types.h" +#include "iterable.h" +#include "obj.h" + + +INTRSTRUCT(ObjList); + + +DECLSTRUCT(ObjList){ + Watch watch; /* Must be kept at head of structure */ + ObjList *next, *prev; + ObjList **list; +}; + + +typedef ObjList* ObjListIterTmp; + +#define OBJLIST_FIRST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->watch.obj) +#define OBJLIST_LAST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->prev->watch.obj) +#define OBJLIST_EMPTY(LIST) objlist_empty(LIST) + +#define FOR_ALL_ON_OBJLIST(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(objlist_iter_init, (TYPE)objlist_iter, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_OBJLIST_REV(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(objlist_iter_rev_init, \ + (TYPE)objlist_iter_rev, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_OBJLIST_UNSAFE(TYPE, VAR, LL) \ + FOR_ALL_ON_OBJLIST(TYPE, VAR, LL, objlist_iter_tmp) + +extern ObjListIterTmp objlist_iter_tmp; + +extern bool objlist_insert_last(ObjList **objlist, Obj *obj); +extern bool objlist_insert_first(ObjList **objlist, Obj *obj); +extern bool objlist_reinsert_last(ObjList **objlist, Obj *obj); +extern bool objlist_reinsert_first(ObjList **objlist, Obj *obj); +extern bool objlist_remove(ObjList **objlist, Obj *obj); +extern bool objlist_contains(ObjList *objlist, Obj *obj); +extern void objlist_clear(ObjList **objlist); +extern void objlist_iter_init(ObjListIterTmp *state, ObjList *objlist); +extern Obj *objlist_iter(ObjListIterTmp *state); +extern void objlist_iter_rev_init(ObjListIterTmp *state, ObjList *objlist); +extern Obj *objlist_iter_rev(ObjListIterTmp *state); +extern bool objlist_empty(ObjList *objlist); +extern Obj *objlist_take_first(ObjList **objlist); +extern Obj *objlist_take_last(ObjList **objlist); + +#endif /* LIBTU_OBJLIST_H */ diff --git a/libtu/objp.h b/libtu/objp.h new file mode 100644 index 00000000..54a7b19b --- /dev/null +++ b/libtu/objp.h @@ -0,0 +1,72 @@ +/* + * libtu/objp.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OBJP_H +#define LIBTU_OBJP_H + +#include "types.h" +#include "obj.h" + +typedef void DynFun(); + +INTRSTRUCT(DynFunTab); + +DECLSTRUCT(DynFunTab){ + DynFun *func, *handler; +}; + +DECLSTRUCT(ClassDescr){ + const char *name; + ClassDescr *ancestor; + int funtab_n; + DynFunTab *funtab; + void (*destroy_fn)(); +}; + +#define OBJ_TYPESTR(OBJ) ((OBJ) ? ((Obj*)OBJ)->obj_type->name : NULL) + +#define IMPLCLASS(CLS, ANCESTOR, DFN, DYN) \ + ClassDescr CLASSDESCR(CLS)={ \ + #CLS, &CLASSDESCR(ANCESTOR), -1, DYN, (void (*)())DFN} + +#define OBJ_INIT(O, TYPE) {((Obj*)(O))->obj_type=&CLASSDESCR(TYPE); \ + ((Obj*)(O))->obj_watches=NULL; ((Obj*)(O))->flags=0;} + +#define CREATEOBJ_IMPL(OBJ, LOWOBJ, INIT_ARGS) \ + OBJ *p; p=ALLOC(OBJ); if(p==NULL){ warn_err(); return NULL; } \ + OBJ_INIT(p, OBJ); \ + if(!LOWOBJ ## _init INIT_ARGS) { free((void*)p); return NULL; } return p + +#define SIMPLECREATEOBJ_IMPL(OBJ, LOWOBJ) \ + OBJ *p; p=ALLOC(OBJ); if(p==NULL){ warn_err(); return NULL; } \ + OBJ_INIT(p, OBJ); \ + return p; + +#define END_DYNFUNTAB {NULL, NULL} + +extern DynFun *lookup_dynfun(const Obj *obj, DynFun *func, + bool *funnotfound); +extern bool has_dynfun(const Obj *obj, DynFun *func); + +#define CALL_DYN(FUNC, OBJ, ARGS) \ + bool funnotfound; \ + lookup_dynfun((Obj*)OBJ, (DynFun*)FUNC, &funnotfound) ARGS; + +#define CALL_DYN_RET(RETV, RET, FUNC, OBJ, ARGS) \ + typedef RET ThisDynFun(); \ + bool funnotfound; \ + ThisDynFun *funtmp; \ + funtmp=(ThisDynFun*)lookup_dynfun((Obj*)OBJ, (DynFun*)FUNC, \ + &funnotfound); \ + if(!funnotfound) \ + RETV=funtmp ARGS; + +#define HAS_DYN(OBJ, FUNC) has_dynfun((Obj*)OBJ, (DynFun*)FUNC) + +#endif /* LIBTU_OBJP_H */ diff --git a/libtu/optparser.c b/libtu/optparser.c new file mode 100644 index 00000000..fc65523b --- /dev/null +++ b/libtu/optparser.c @@ -0,0 +1,471 @@ +/* + * libtu/optparser.c + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include + +#include "util.h" +#include "misc.h" +#include "optparser.h" +#include "output.h" +#include "private.h" + + +#define O_ARGS(o) (o->flags&OPT_OPT_ARG) +#define O_ARG(o) (o->flasg&OPT_ARG) +#define O_OPT_ARG(o) (O_ARGS(o)==OPT_OPT_ARG) +#define O_ID(o) (o->optid) + + +static const OptParserOpt *o_opts=NULL; +static char *const *o_current=NULL; +static int o_left=0; +static const char* o_chain_ptr=NULL; +static int o_args_left=0; +static const char*o_tmp=NULL; +static int o_error=0; +static int o_mode=OPTP_CHAIN; + + +/* */ + + +void optparser_init(int argc, char *const argv[], int mode, + const OptParserOpt *opts) +{ + o_mode=mode; + o_opts=opts; + o_current=argv+1; + o_left=argc-1; + o_chain_ptr=NULL; + o_args_left=0; + o_tmp=NULL; +} + + +/* */ + + +static const OptParserOpt *find_chain_opt(char p, const OptParserOpt *o) +{ + for(;O_ID(o);o++){ + if((O_ID(o)&~OPT_ID_RESERVED_FLAG)==p) + return o; + } + return NULL; +} + + +static bool is_option(const char *p) +{ + if(p==NULL) + return FALSE; + if(*p++!='-') + return FALSE; + if(*p++!='-') + return FALSE; + if(*p=='\0') + return FALSE; + return TRUE; +} + + +/* */ + +enum{ + SHORT, MIDLONG, LONG +}; + + +int optparser_get_opt() +{ +#define RET(X) return o_tmp=p, o_error=X + const char *p, *p2=NULL; + bool dash=TRUE; + int type=SHORT; + const OptParserOpt *o; + int l; + + while(o_args_left) + optparser_get_arg(); + + o_tmp=NULL; + + /* Are we doing a chain (i.e. opt. of style 'tar xzf')? */ + if(o_chain_ptr!=NULL){ + p=o_chain_ptr++; + if(!*o_chain_ptr) + o_chain_ptr=NULL; + + o=find_chain_opt(*p, o_opts); + + if(o==NULL) + RET(E_OPT_INVALID_CHAIN_OPTION); + + goto do_arg; + } + + if(o_left<1) + return OPT_ID_END; + + o_left--; + p=*o_current++; + + if(*p!='-'){ + dash=FALSE; + if(o_mode!=OPTP_NO_DASH) + RET(OPT_ID_ARGUMENT); + p2=p; + }else if(*(p+1)=='-'){ + /* --foo */ + if(*(p+2)=='\0'){ + /* -- arguments */ + o_args_left=o_left; + RET(OPT_ID_ARGUMENT); + } + type=LONG; + p2=p+2; + }else{ + /* -foo */ + if(*(p+1)=='\0'){ + /* - */ + o_args_left=1; + RET(OPT_ID_ARGUMENT); + } + if(*(p+2)!='\0' && o_mode==OPTP_MIDLONG) + type=MIDLONG; + + p2=p+1; + } + + o=o_opts; + + for(; O_ID(o); o++){ + if(type==LONG){ + /* Do long option (--foo=bar) */ + if(o->longopt==NULL) + continue; + l=strlen(o->longopt); + if(strncmp(p2, o->longopt, l)!=0) + continue; + + if(p2[l]=='\0'){ + if(O_ARGS(o)==OPT_ARG) + RET(E_OPT_MISSING_ARGUMENT); + return O_ID(o); + }else if(p2[l]=='='){ + if(!O_ARGS(o)) + RET(E_OPT_UNEXPECTED_ARGUMENT); + if(p2[l+1]=='\0') + RET(E_OPT_MISSING_ARGUMENT); + o_tmp=p2+l+1; + o_args_left=1; + return O_ID(o); + } + continue; + }else if(type==MIDLONG){ + if(o->longopt==NULL) + continue; + + if(strcmp(p2, o->longopt)!=0) + continue; + }else{ /* type==SHORT */ + if(*p2!=(O_ID(o)&~OPT_ID_RESERVED_FLAG)) + continue; + + if(*(p2+1)!='\0'){ + if(o_mode==OPTP_CHAIN || o_mode==OPTP_NO_DASH){ + /*valid_chain(p2+1, o_opts)*/ + o_chain_ptr=p2+1; + p++; + }else if(o_mode==OPTP_IMMEDIATE){ + if(!O_ARGS(o)){ + if(*(p2+1)!='\0') + RET(E_OPT_UNEXPECTED_ARGUMENT); + }else{ + if(*(p2+1)=='\0') + RET(E_OPT_MISSING_ARGUMENT); + o_tmp=p2+1; + o_args_left=1; + } + return O_ID(o); + }else{ + RET(E_OPT_SYNTAX_ERROR); + } + } + } + + do_arg: + + if(!O_ARGS(o)) + return O_ID(o); + + if(!o_left || is_option(*o_current)){ + if(O_ARGS(o)==OPT_OPT_ARG) + return O_ID(o); + RET(E_OPT_MISSING_ARGUMENT); + } + + o_args_left=1; + return O_ID(o); + } + + if(dash) + RET(E_OPT_INVALID_OPTION); + + RET(OPT_ID_ARGUMENT); +#undef RET +} + + +/* */ + + +const char* optparser_get_arg() +{ + const char *p; + + if(o_tmp!=NULL){ + /* If o_args_left==0, then were returning an invalid option + * otherwise an immediate argument (e.g. -funsigned-char + * where '-f' is the option and 'unsigned-char' the argument) + */ + if(o_args_left>0) + o_args_left--; + p=o_tmp; + o_tmp=NULL; + return p; + } + + if(o_args_left<1 || o_left<1) + return NULL; + + o_left--; + o_args_left--; + return *o_current++; +} + + +/* */ + +static void warn_arg(const char *e) +{ + const char *p=optparser_get_arg(); + + if(p==NULL) + warn("%s (null)", e); + else + warn("%s \'%s\'", e, p); +} + + +static void warn_opt(const char *e) +{ + if(o_tmp!=NULL && o_chain_ptr!=NULL) + warn("%s \'-%c\'", e, *o_tmp); + else + warn_arg(e); +} + + +void optparser_print_error() +{ + switch(o_error){ + case E_OPT_INVALID_OPTION: + case E_OPT_INVALID_CHAIN_OPTION: + warn_opt(TR("Invalid option")); + break; + + case E_OPT_SYNTAX_ERROR: + warn_arg(TR("Syntax error while parsing")); + break; + + case E_OPT_MISSING_ARGUMENT: + warn_opt(TR("Missing argument to")); + break; + + case E_OPT_UNEXPECTED_ARGUMENT: + warn_opt(TR("No argument expected:")); + break; + + case OPT_ID_ARGUMENT: + warn(TR("Unexpected argument")); + break; + + default: + warn(TR("(unknown error)")); + } + + o_tmp=NULL; + o_error=0; +} + + +/* */ + + +static uint opt_w(const OptParserOpt *opt, bool midlong) +{ + uint w=0; + + if((opt->optid&OPT_ID_NOSHORT_FLAG)==0){ + w+=2; /* "-o" */ + if(opt->longopt!=NULL) + w+=2; /* ", " */ + } + + if(opt->longopt!=NULL) + w+=strlen(opt->longopt)+(midlong ? 1 : 2); + + if(O_ARGS(opt)){ + if(opt->argname==NULL) + w+=4; + else + w+=1+strlen(opt->argname); /* "=ARG" or " ARG" */ + + if(O_OPT_ARG(opt)) + w+=2; /* [ARG] */ + } + + return w; +} + + +#define TERM_W 80 +#define OFF1 2 +#define OFF2 2 +#define SPACER1 " " +#define SPACER2 " " + +static void print_opt(const OptParserOpt *opt, bool midlong, + uint maxw, uint tw) +{ + FILE *f=stdout; + const char *p, *p2, *p3; + uint w=0; + + fprintf(f, SPACER1); + + /* short opt */ + + if((O_ID(opt)&OPT_ID_NOSHORT_FLAG)==0){ + fprintf(f, "-%c", O_ID(opt)&~OPT_ID_RESERVED_FLAG); + w+=2; + + if(opt->longopt!=NULL){ + fprintf(f, ", "); + w+=2; + } + } + + /* long opt */ + + if(opt->longopt!=NULL){ + if(midlong){ + w++; + fprintf(f, "-%s", opt->longopt); + }else{ + w+=2; + fprintf(f, "--%s", opt->longopt); + } + w+=strlen(opt->longopt); + } + + /* arg */ + + if(O_ARGS(opt)){ + w++; + if(opt->longopt!=NULL && !midlong) + putc('=', f); + else + putc(' ', f); + + if(O_OPT_ARG(opt)){ + w+=2; + putc('[', f); + } + + if(opt->argname!=NULL){ + fprintf(f, "%s", opt->argname); + w+=strlen(opt->argname); + }else{ + w+=3; + fprintf(f, "ARG"); + } + + if(O_OPT_ARG(opt)) + putc(']', f); + } + + while(w++descr; + + if(p==NULL){ + putc('\n', f); + return; + } + + fprintf(f, SPACER2); + + maxw+=OFF1+OFF2; + tw-=maxw; + + while(strlen(p)>tw){ + p3=p2=p+tw-2; + + while(*p2!=' ' && p2!=p) + p2--; + + while(*p3!=' ' && *p3!='\0') + p3++; + + if((uint)(p3-p2)>tw){ + /* long word - just wrap */ + p2=p+tw-2; + } + + writef(f, p, p2-p); + if(*p2==' ') + putc('\n', f); + else + fprintf(f, "\\\n"); + + p=p2+1; + + w=maxw; + while(w--) + putc(' ', f); + } + + fprintf(f, "%s\n", p); +} + + +void optparser_printhelp(int mode, const OptParserOpt *opts) +{ + uint w, maxw=0; + const OptParserOpt *o; + bool midlong=mode&OPTP_MIDLONG; + + o=opts; + for(; O_ID(o); o++){ + w=opt_w(o, midlong); + if(w>maxw) + maxw=w; + } + + o=opts; + + for(; O_ID(o); o++) + print_opt(o, midlong, maxw, TERM_W); +} diff --git a/libtu/optparser.h b/libtu/optparser.h new file mode 100644 index 00000000..3b6ed5f5 --- /dev/null +++ b/libtu/optparser.h @@ -0,0 +1,78 @@ +/* + * libtu/optparser.h + * + * Copyright (c) Tuomo Valkonen 1999-2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OPTPARSER_H +#define LIBTU_OPTPARSER_H + +#include "types.h" + + +#define OPT_ID_NOSHORT_FLAG 0x10000 +#define OPT_ID_RESERVED_FLAG 0x20000 + +#define OPT_ID(X) ((X)|OPT_ID_NOSHORT_FLAG) +#define OPT_ID_RESERVED(X) ((X)|OPT_ID_RESERVED_FLAG) + +/* OPTP_CHAIN is the normal behavior, i.e. single-letter options can be + * "chained" together: 'lr -lR'. Use for normal command line programs. + * OPTP_MIDLONG allows '-display foo' -like args but disables chaining + * of single-letter options. X programs should probably use this. + * OPTP_IMMEDIATE allows immediate arguments (-I/usr/include) (and disables + * chaining and midlong options). + * OPTP_NO_DASH is the same as OPTP_CHAIN but allows the dash to be omitted + * for 'tar xzf foo' -like behavior. + * Long '--foo=bar' options are supported in all of the modes. + */ + +enum{ + OPTP_CHAIN=0, + OPTP_DEFAULT=0, + OPTP_MIDLONG=1, + OPTP_IMMEDIATE=2, + OPTP_NO_DASH=3 +}; + +enum{ + OPT_ARG=1, /* option has an argument */ + OPT_OPT_ARG=3 /* option may have an argument */ +}; + + +#define END_OPTPARSEROPTS {0, NULL, 0, NULL, NULL} + +typedef struct _OptParserOpt{ + int optid; + const char *longopt; + int flags; + const char *argname; + const char *descr; +} OptParserOpt; + +enum{ + OPT_ID_END=0, + OPT_ID_ARGUMENT=1, + + E_OPT_INVALID_OPTION=-1, + E_OPT_INVALID_CHAIN_OPTION=-2, + E_OPT_SYNTAX_ERROR=-3, + E_OPT_MISSING_ARGUMENT=-4, + E_OPT_UNEXPECTED_ARGUMENT=-5 +}; + + +extern void optparser_init(int argc, char *const argv[], int mode, + const OptParserOpt *opts); + +extern void optparser_printhelp(int mode, const OptParserOpt *opts); + +extern int optparser_get_opt(); +extern const char* optparser_get_arg(); +extern void optparser_print_error(); + +#endif /* LIBTU_OPTPARSER_H */ diff --git a/libtu/output.c b/libtu/output.c new file mode 100644 index 00000000..5b6fdc35 --- /dev/null +++ b/libtu/output.c @@ -0,0 +1,405 @@ +/* + * libtu/output.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#if HAS_SYSTEM_ASPRINTF +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include "misc.h" +#include "output.h" +#include "util.h" +#include "private.h" + +#if !HAS_SYSTEM_ASPRINTF +#include "snprintf_2.2/snprintf.c" +#endif + + +static void default_warn_handler(const char *message); + + +static bool verbose_mode=FALSE; +static int verbose_indent_lvl=0; +static bool progname_enable=TRUE; +static WarnHandler *current_warn_handler=default_warn_handler; + +#define INDENTATOR_LENGTH 4 + +static char indentator[]={' ', ' ', ' ', ' '}; + +static void do_dispatch_message(const char *message); + + +void verbose(const char *p, ...) +{ + va_list args; + + va_start(args, p); + + verbose_v(p, args); + + va_end(args); +} + + +void verbose_v(const char *p, va_list args) +{ + int i; + + if(verbose_mode){ + for(i=0; i=0) + verbose_indent_lvl=depth; + + return old; +} + + +void warn_progname_enable(bool enable) +{ + progname_enable=enable; +} + + +static void put_prog_name() +{ + const char*progname; + + if(!progname_enable) + return; + + progname=libtu_progname(); + + if(progname==NULL) + return; + + fprintf(stderr, "%s: ", (char*)progname); +} + +/* warn + */ + + +static void fallback_warn() +{ + put_prog_name(); + fprintf(stderr, TR("Oops. Error string compilation failed: %s"), + strerror(errno)); +} + + +#define CALL_V(NAME, ARGS) \ + do { va_list args; va_start(args, p); NAME ARGS; va_end(args); } while(0) + +#define DO_DISPATCH(NAME, ARGS) \ + do{ \ + char *msg; \ + if((msg=NAME ARGS)!=NULL){ \ + do_dispatch_message(msg); \ + free(msg);\ + }else{ \ + fallback_warn(); \ + } \ + }while(0) + + +void libtu_asprintf(char **ret, const char *p, ...) +{ + *ret=NULL; + CALL_V(vasprintf, (ret, p, args)); + if(*ret==NULL) + warn_err(); +} + + +void libtu_vasprintf(char **ret, const char *p, va_list args) +{ + *ret=NULL; + vasprintf(ret, p, args); + if(*ret==NULL) + warn_err(); +} + + +void warn(const char *p, ...) +{ + CALL_V(warn_v, (p, args)); +} + + +void warn_obj(const char *obj, const char *p, ...) +{ + CALL_V(warn_obj_v, (obj, p, args)); +} + + +void warn_obj_line(const char *obj, int line, const char *p, ...) +{ + CALL_V(warn_obj_line_v, (obj, line, p, args)); +} + + +void warn_obj_v(const char *obj, const char *p, va_list args) +{ + warn_obj_line_v(obj, -1, p, args); +} + + +void warn_v(const char *p, va_list args) +{ + DO_DISPATCH(errmsg_v, (p, args)); +} + + +void warn_obj_line_v(const char *obj, int line, const char *p, va_list args) +{ + DO_DISPATCH(errmsg_obj_line_v, (obj, line, p, args)); +} + + +void warn_err() +{ + DO_DISPATCH(errmsg_err, ()); +} + + +void warn_err_obj(const char *obj) +{ + DO_DISPATCH(errmsg_err_obj, (obj)); +} + +void warn_err_obj_line(const char *obj, int line) +{ + DO_DISPATCH(errmsg_err_obj_line, (obj, line)); +} + + +/* errmsg + */ + +#define CALL_V_RET(NAME, ARGS) \ + char *ret; va_list args; va_start(args, p); ret=NAME ARGS; \ + va_end(args); return ret; + + +char* errmsg(const char *p, ...) +{ + CALL_V_RET(errmsg_v, (p, args)); +} + + +char *errmsg_obj(const char *obj, const char *p, ...) +{ + CALL_V_RET(errmsg_obj_v, (obj, p, args)); +} + + +char *errmsg_obj_line(const char *obj, int line, const char *p, ...) +{ + CALL_V_RET(errmsg_obj_line_v, (obj, line, p, args)); +} + + +char* errmsg_obj_v(const char *obj, const char *p, va_list args) +{ + return errmsg_obj_line_v(obj, -1, p, args); +} + + +char *errmsg_v(const char *p, va_list args) +{ + char *res; + libtu_vasprintf(&res, p, args); + return res; +} + + +char *errmsg_obj_line_v(const char *obj, int line, const char *p, va_list args) +{ + char *res1=NULL, *res2, *res3; + + if(obj!=NULL){ + if(line>0) + libtu_asprintf(&res1, "%s:%d: ", obj, line); + else + libtu_asprintf(&res1, "%s: ", obj); + }else{ + if(line>0) + libtu_asprintf(&res1, "%d: ", line); + } + libtu_vasprintf(&res2, p, args); + if(res1!=NULL){ + if(res2==NULL) + return NULL; + res3=scat(res1, res2); + free(res1); + free(res2); + return res3; + } + return res2; +} + + +char *errmsg_err() +{ + char *res; + libtu_asprintf(&res, "%s\n", strerror(errno)); + return res; +} + + +char *errmsg_err_obj(const char *obj) +{ + char *res; + if(obj!=NULL) + libtu_asprintf(&res, "%s: %s\n", obj, strerror(errno)); + else + libtu_asprintf(&res, "%s\n", strerror(errno)); + return res; +} + + +char *errmsg_err_obj_line(const char *obj, int line) +{ + char *res; + if(obj!=NULL){ + if(line>0) + libtu_asprintf(&res, "%s:%d: %s\n", obj, line, strerror(errno)); + else + libtu_asprintf(&res, "%s: %s\n", obj, strerror(errno)); + }else{ + if(line>0) + libtu_asprintf(&res, "%d: %s\n", line, strerror(errno)); + else + libtu_asprintf(&res, "%s\n", strerror(errno)); + } + return res; +} + + +/* die + */ + + +void die(const char *p, ...) +{ + set_warn_handler(NULL); + CALL_V(die_v, (p, args)); +} + + +void die_v(const char *p, va_list args) +{ + set_warn_handler(NULL); + warn_v(p, args); + exit(EXIT_FAILURE); +} + + +void die_obj(const char *obj, const char *p, ...) +{ + set_warn_handler(NULL); + CALL_V(die_obj_v, (obj, p, args)); +} + + +void die_obj_line(const char *obj, int line, const char *p, ...) +{ + set_warn_handler(NULL); + CALL_V(die_obj_line_v, (obj, line, p, args)); +} + + +void die_obj_v(const char *obj, const char *p, va_list args) +{ + set_warn_handler(NULL); + warn_obj_v(obj, p, args); + exit(EXIT_FAILURE); +} + + +void die_obj_line_v(const char *obj, int line, const char *p, va_list args) +{ + set_warn_handler(NULL); + warn_obj_line_v(obj, line, p, args); + exit(EXIT_FAILURE); +} + + +void die_err() +{ + set_warn_handler(NULL); + warn_err(); + exit(EXIT_FAILURE); +} + + +void die_err_obj(const char *obj) +{ + set_warn_handler(NULL); + warn_err_obj(obj); + exit(EXIT_FAILURE); +} + + +void die_err_obj_line(const char *obj, int line) +{ + set_warn_handler(NULL); + warn_err_obj_line(obj, line); + exit(EXIT_FAILURE); +} + + +static void default_warn_handler(const char *message) +{ + put_prog_name(); + fprintf(stderr, "%s", message); + putc('\n', stderr); +} + + +static void do_dispatch_message(const char *message) +{ + if(current_warn_handler!=NULL) + current_warn_handler(message); + else + default_warn_handler(message); +} + + +WarnHandler *set_warn_handler(WarnHandler *handler) +{ + WarnHandler *old=current_warn_handler; + current_warn_handler=(handler!=NULL ? handler : default_warn_handler); + return old; +} diff --git a/libtu/output.h b/libtu/output.h new file mode 100644 index 00000000..8e75de91 --- /dev/null +++ b/libtu/output.h @@ -0,0 +1,76 @@ +/* + * libtu/output.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_OUTPUT_H +#define LIBTU_OUTPUT_H + +#include + +#include "types.h" + +#if __STDC_VERSION__ >= 199901L +#define WARN_FUNC(...) warn_obj(__func__, __VA_ARGS__) +#define WARN_ERR_FUNC() warn_err_obj(__func__) +#else +#define WARN_FUNC warn +#define WARN_ERR_FUNC() warn_err() +#endif + +typedef void WarnHandler(const char *); +extern WarnHandler *set_warn_handler(WarnHandler *handler); + +extern void verbose(const char *p, ...); +extern void verbose_v(const char *p, va_list args); +extern void verbose_enable(bool enable); +extern int verbose_indent(int depth); + +extern void warn_progname_enable(bool enable); + +extern void die(const char *p, ...); +extern void die_v(const char *p, va_list args); + +extern void die_obj(const char *obj, const char *p, ...); +extern void die_obj_v(const char *obj, const char *p, va_list args); +extern void die_obj_line(const char *obj, int line, const char *p, ...); +extern void die_obj_line_v(const char *obj, int line, const char *p, va_list args); + +extern void die_err(); +extern void die_err_obj(const char *obj); +extern void die_err_obj_line(const char *obj, int line); + + +extern void warn(const char *p, ...); +extern void warn_v(const char *p, va_list args); + +extern void warn_obj(const char *obj, const char *p, ...); +extern void warn_obj_v(const char *obj, const char *p, va_list args); +extern void warn_obj_line(const char *obj, int line, const char *p, ...); +extern void warn_obj_line_v(const char *obj, int line, const char *p, va_list args); + +extern void warn_err(); +extern void warn_err_obj(const char *obj); +extern void warn_err_obj_line(const char *obj, int line); + + +extern char *errmsg(const char *p, ...); +extern char *errmsg_v(const char *p, va_list args); + +extern char *errmsg_obj(const char *obj, const char *p, ...); +extern char *errmsg_obj_v(const char *obj, const char *p, va_list args); +extern char *errmsg_obj_line(const char *obj, int line, const char *p, ...); +extern char *errmsg_obj_line_v(const char *obj, int line, const char *p, va_list args); + +extern char *errmsg_err(); +extern char *errmsg_err_obj(const char *obj); +extern char *errmsg_err_obj_line(const char *obj, int line); + +extern void libtu_asprintf(char **ret, const char *fmt, ...); +extern void libtu_vasprintf(char **ret, const char *fmt, va_list args); + +#endif /* LIBTU_OUTPUT_H */ diff --git a/libtu/parser.c b/libtu/parser.c new file mode 100644 index 00000000..3a29dd3a --- /dev/null +++ b/libtu/parser.c @@ -0,0 +1,717 @@ +/* + * libtu/parser.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include + +#include "parser.h" +#include "misc.h" +#include "output.h" + +#define MAX_TOKENS 256 +#define MAX_NEST 256 + + +enum{ + P_NONE=1, + P_EOF, + P_STMT, + P_STMT_NS, + P_STMT_SECT, + P_BEG_SECT, + P_END_SECT +}; + + +/* */ + + +static bool opt_include(Tokenizer *tokz, int n, Token *toks); + + +static ConfOpt common_opts[]={ + {"include", "s", opt_include, NULL}, + {NULL, NULL, NULL, NULL} +}; + + +/* */ + + +static int read_statement(Tokenizer *tokz, Token *tokens, int *ntok_ret) +{ + int ntokens=0; + Token *tok=NULL; + int had_comma=0; /* 0 - no, 1 - yes, 2 - not had, not expected */ + int retval=0; + int e=0; + + while(1){ + tok=&tokens[ntokens]; + + if(!tokz_get_token(tokz, tok)){ + e=1; + continue; + } + + if(ntokens==MAX_TOKENS-1){ + e=E_TOKZ_TOKEN_LIMIT; + tokz_warn_error(tokz, tok->line, e); + if(!(tokz->flags&TOKZ_ERROR_TOLERANT)) + break; + }else{ + ntokens++; + } + + if(!TOK_IS_OP(tok)){ + if(ntokens==1 && !had_comma){ + /* first token */ + had_comma=2; + }else{ + if(had_comma==0) + goto syntax; + + had_comma=0; + } + continue; + } + + /* It is an operator */ + ntokens--; + + switch(TOK_OP_VAL(tok)){ + case OP_SCOLON: + retval=(ntokens==0 ? P_NONE : P_STMT_NS); + break; + + case OP_NEXTLINE: + retval=(ntokens==0 ? P_NONE : P_STMT); + break; + + case OP_L_BRC: + retval=(ntokens==0 ? P_BEG_SECT : P_STMT_SECT); + break; + + case OP_R_BRC: + if(ntokens==0){ + retval=P_END_SECT; + }else{ + tokz_unget_token(tokz, tok); + retval=P_STMT_NS; + } + break; + + case OP_EOF: + retval=(ntokens==0 ? P_EOF : P_STMT_NS); + + if(had_comma==1){ + e=E_TOKZ_UNEXPECTED_EOF; + goto handle_error; + } + + goto end; + + case OP_COMMA: + if(had_comma!=0) + goto syntax; + + had_comma=1; + continue; + + default: + goto syntax; + } + + if(had_comma!=1) + break; + + syntax: + e=E_TOKZ_SYNTAX; + handle_error: + tokz_warn_error(tokz, tok->line, e); + + if(!(tokz->flags&TOKZ_ERROR_TOLERANT) || retval!=0) + break; + } + +end: + if(e!=0) + retval=-retval; + + *ntok_ret=ntokens; + + return retval; +} + + +static bool find_beg_sect(Tokenizer *tokz) +{ + Token tok=TOK_INIT; + + while(tokz_get_token(tokz, &tok)){ + if(TOK_IS_OP(&tok)){ + if(TOK_OP_VAL(&tok)==OP_NEXTLINE) + continue; + + if(TOK_OP_VAL(&tok)==OP_SCOLON) + return FALSE; + + if(TOK_OP_VAL(&tok)==OP_L_BRC) + return TRUE; + } + + tokz_unget_token(tokz, &tok); + break; + } + return FALSE; +} + + +/* */ + + +static const ConfOpt* lookup_option(const ConfOpt *opts, const char *name) +{ + while(opts->optname!=NULL){ + if(strcmp(opts->optname, name)==0) + return opts; + opts++; + } + return NULL; +} + + +static bool call_end_sect(Tokenizer *tokz, const ConfOpt *opts) +{ + opts=lookup_option(opts, "#end"); + if(opts!=NULL) + return opts->fn(tokz, 0, NULL); + + return TRUE; +} + + +static bool call_cancel_sect(Tokenizer *tokz, const ConfOpt *opts) +{ + opts=lookup_option(opts, "#cancel"); + if(opts!=NULL) + return opts->fn(tokz, 0, NULL); + + return TRUE; +} + + +/* */ + + +bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options) +{ + Token tokens[MAX_TOKENS]; + bool alloced_optstack=FALSE; + int i, t, ntokens=0; + int init_nest_lvl; + bool had_error; + int errornest=0; + bool is_default=FALSE; + + /* Allocate tokz->optstack if it does not yet exist (if it does, + * we have been called from an option handler) + */ + if(!tokz->optstack){ + tokz->optstack=ALLOC_N(const ConfOpt*, MAX_NEST); + if(!tokz->optstack){ + warn_err(); + return FALSE; + } + + memset(tokz->optstack, 0, sizeof(ConfOpt*)*MAX_NEST); + init_nest_lvl=tokz->nest_lvl=0; + alloced_optstack=TRUE; + }else{ + init_nest_lvl=tokz->nest_lvl; + } + + tokz->optstack[init_nest_lvl]=options; + + for(i=0; iflags&TOKZ_PARSER_INDENT_MODE) + verbose_indent(tokz->nest_lvl); + + if(!TOK_IS_IDENT(tokens+0)){ + had_error=TRUE; + tokz_warn_error(tokz, tokens->line, + E_TOKZ_IDENTIFIER_EXPECTED); + } + + if(t==P_STMT){ + if(find_beg_sect(tokz)) + t=P_STMT_SECT; + } + + if(had_error) + break; + + /* Got the statement and its type */ + + options=lookup_option(tokz->optstack[tokz->nest_lvl], + TOK_IDENT_VAL(tokens+0)); + if(options==NULL) + options=lookup_option(common_opts, TOK_IDENT_VAL(tokens+0)); + if(options==NULL && (tokz->flags&TOKZ_DEFAULT_OPTION)){ + options=lookup_option(tokz->optstack[tokz->nest_lvl], "#default"); + is_default=(options!=NULL); + } + + if(options==NULL){ + had_error=TRUE; + tokz_warn_error(tokz, tokens->line, E_TOKZ_UNKNOWN_OPTION); + }else if(!is_default) { + had_error=!check_args(tokz, tokens, ntokens, options->argfmt); + } + + if(had_error) + break; + + /* Found the option and arguments are ok */ + + if(options->opts!=NULL){ + if(t!=P_STMT_SECT){ + had_error=TRUE; + tokz_warn_error(tokz, tokz->line, E_TOKZ_LBRACE_EXPECTED); + }else if(tokz->nest_lvl==MAX_NEST-1){ + tokz_warn_error(tokz, tokz->line, E_TOKZ_MAX_NEST); + had_error=TRUE; + }else{ + tokz->nest_lvl++; + tokz->optstack[tokz->nest_lvl]=options->opts; + } + }else if(t==P_STMT_SECT){ + had_error=TRUE; + tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); + } + + if(!had_error && options->fn!=NULL){ + had_error=!options->fn(tokz, ntokens, tokens); + if(t==P_STMT_SECT && had_error) + tokz->nest_lvl--; + } + break; + + case P_EOF: + if(tokz_popf(tokz)){ + break; + }else if(tokz->nest_lvl>0 || errornest>0){ + tokz_warn_error(tokz, 0, E_TOKZ_UNEXPECTED_EOF); + had_error=TRUE; + } + goto eof; + + case P_BEG_SECT: + had_error=TRUE; + errornest++; + tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); + break; + + case P_END_SECT: + if(tokz->nest_lvl+errornest==0){ + tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX); + had_error=TRUE; + } + + if(had_error) + break; + + if(errornest!=0){ + errornest--; + }else{ + had_error=!call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); + tokz->nest_lvl--; + } + + if(tokz->nest_lvlflags&TOKZ_ERROR_TOLERANT)) + break; + } + +eof: + /* free the tokens */ + while(ntokens--) + tok_free(&tokens[ntokens]); + + while(tokz->nest_lvl>=init_nest_lvl){ + if(tokz->flags&TOKZ_ERROR_TOLERANT || !had_error) + call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]); + else + call_cancel_sect(tokz, tokz->optstack[tokz->nest_lvl]); + tokz->nest_lvl--; + } + + /* Free optstack if it was alloced by this call */ + if(alloced_optstack){ + free(tokz->optstack); + tokz->optstack=NULL; + tokz->nest_lvl=0; + } + + if(tokz->flags&TOKZ_PARSER_INDENT_MODE) + verbose_indent(init_nest_lvl); + + return !had_error; +} + + +/* */ + + +bool parse_config(const char *fname, const ConfOpt *options, int flags) +{ + Tokenizer *tokz; + bool ret; + + tokz=tokz_open(fname); + + if(tokz==NULL) + return FALSE; + + tokz->flags|=flags&~TOKZ_READ_COMMENTS; + + ret=parse_config_tokz(tokz, options); + + tokz_close(tokz); + + return ret; +} + + +bool parse_config_file(FILE *file, const ConfOpt *options, int flags) +{ + Tokenizer *tokz; + bool ret; + + tokz=tokz_open_file(file, NULL); + + if(tokz==NULL) + return FALSE; + + tokz->flags|=flags&~TOKZ_READ_COMMENTS; + + ret=parse_config_tokz(tokz, options); + + tokz_close(tokz); + + return ret; +} + + +/* + * Argument validity checking stuff + */ + + +static int arg_match(Token *tok, char c, bool si) +{ + char c2=tok->type; + + if(c=='.' || c=='*') + return 0; + + if(c2==c) + return 0; + + if(si){ + if(c2=='i' && c=='s'){ + TOK_SET_IDENT(tok, TOK_STRING_VAL(tok)); + return 0; + } + + if(c2=='s' && c=='i'){ + TOK_SET_STRING(tok, TOK_IDENT_VAL(tok)); + return 0; + } + } + + if(c2=='c' && c=='l'){ + TOK_SET_LONG(tok, TOK_CHAR_VAL(tok)); + return 0; + } + + if(c2=='l' && c=='c'){ + TOK_SET_CHAR(tok, TOK_LONG_VAL(tok)); + return 0; + } + + if(c2=='l' && c=='d'){ + TOK_SET_DOUBLE(tok, TOK_LONG_VAL(tok)); + return 0; + } + + if(c=='b'){ + if(c2=='l'){ + TOK_SET_BOOL(tok, TOK_LONG_VAL(tok)); + return 0; + }else if(c2=='i'){ + if(strcmp(TOK_IDENT_VAL(tok), "TRUE")==0){ + tok_free(tok); + TOK_SET_BOOL(tok, TRUE); + return 0; + }else if(strcmp(TOK_IDENT_VAL(tok), "FALSE")==0){ + tok_free(tok); + TOK_SET_BOOL(tok, FALSE); + return 0; + } + } + } + + return E_TOKZ_INVALID_ARGUMENT; +} + + +static int check_argument(const char **pret, Token *tok, const char *p, + bool si) +{ + int mode; + int e=E_TOKZ_TOO_MANY_ARGS; + +again: + mode=0; + + if(*p=='*'){ + *pret=p; + return 0; + }else if(*p=='?'){ + mode=1; + p++; + }else if(*p==':'){ + mode=2; + p++; + }else if(*p=='+'){ + *pret=p; + return arg_match(tok, *(p-1), si); + } + + while(*p!='\0'){ + e=arg_match(tok, *p, si); + if(e==0){ + p++; + while(mode==2 && *p==':'){ + if(*++p=='\0') + break; /* Invalid argument format string, though... */ + p++; + } + *pret=p; + return 0; + } + + if(mode==0) + break; + + p++; + + if(mode==1) + goto again; + + /* mode==2 */ + + if(*p!=':') + break; + p++; + e=E_TOKZ_TOO_MANY_ARGS; + } + + *pret=p; + return e; +} + + +static bool args_at_end(const char *p) +{ + if(p==NULL) + return TRUE; + + while(*p!='\0'){ + if(*p=='*' || *p=='+') + p++; + else if(*p=='?') + p+=2; + else + return FALSE; + } + + return TRUE; +} + + +bool do_check_args(const Tokenizer *tokz, Token *tokens, int ntokens, + const char *fmt, bool si) +{ + int i; + int e; + + if(fmt==NULL){ + if(ntokens!=1) + tokz_warn_error(tokz, tokens[0].line, E_TOKZ_TOO_MANY_ARGS); + return ntokens==1; + } + + for(i=1; iname!=NULL) + lastndx=strrchr(tokz->name, '/'); + + if(lastndx==NULL) + retval=try_include(tokz, fname); + else + retval=try_include_dir(tokz, tokz->name, lastndx-tokz->name+1, fname); + + if(retval==TRUE) + return TRUE; + + e=errno; + + if(tokz->includepaths!=NULL){ + while(tokz->includepaths[i]!=NULL){ + if(try_include_dir(tokz, tokz->includepaths[i], -1, fname)) + return TRUE; + i++; + } + } + + warn_obj(fname, "%s", strerror(e)); + + return FALSE; +} + + +extern void tokz_set_includepaths(Tokenizer *tokz, char **paths) +{ + tokz->includepaths=paths; +} + + + +ConfOpt libtu_dummy_confopts[]={ + END_CONFOPTS +}; + + + +bool parse_config_tokz_skip_section(Tokenizer *tokz) +{ + return parse_config_tokz(tokz, libtu_dummy_confopts); +} diff --git a/libtu/parser.h b/libtu/parser.h new file mode 100644 index 00000000..73580533 --- /dev/null +++ b/libtu/parser.h @@ -0,0 +1,54 @@ +/* + * libtu/parser.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_PARSER_H +#define LIBTU_PARSER_H + +#include "tokenizer.h" + +/* + * format: + * l = long + * d = double + * i = identifier + * s = string + * c = char + * . = 1 times any ("l.d") + * * = 0 or more times any (must be the last, "sd*") + * ? = optional ("?c") + * : = conditional (":c:s") + * + = 1 or more times last (most be the last, "l+") + * special entries: + * + * "#end" call this handler at the end of section. + * "#cancel" call this handler when recovering from error + */ + +#define END_CONFOPTS {NULL, NULL, NULL, NULL} + +typedef struct _ConfOpt{ + const char *optname; + const char *argfmt; + bool (*fn)(Tokenizer *tokz, int n, Token *toks); + struct _ConfOpt *opts; +} ConfOpt; + +#define CONFOPTS_NOT_SET libtu_dummy_confopts +extern ConfOpt libtu_dummy_confopts[]; + +extern bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options); +extern bool parse_config_tokz_skip_section(Tokenizer *tokz); +extern bool parse_config(const char *fname, const ConfOpt *options, int flags); +extern bool parse_config_file(FILE *file, const ConfOpt *options, int flags); +extern bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens, + const char *fmt); +extern bool check_args_loose(const Tokenizer *tokz, Token *tokens, int ntokens, + const char *fmt); + +#endif /* LIBTU_PARSER_H */ diff --git a/libtu/pointer.h b/libtu/pointer.h new file mode 100644 index 00000000..f22ecf51 --- /dev/null +++ b/libtu/pointer.h @@ -0,0 +1,16 @@ +/* + * libtu/pointer.h + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_POINTER_H +#define LIBTU_POINTER_H + +#define FIELD_OFFSET(T, F) ((long)((char*)&((T*)0)->F)) +#define FIELD_TO_STRUCT(T, F, A) ((T*)(((char*)A)-FIELD_OFFSET(T, F))) + +#endif /* LIBTU_POINTER_H */ diff --git a/libtu/prefix.c b/libtu/prefix.c new file mode 100644 index 00000000..676ae2e0 --- /dev/null +++ b/libtu/prefix.c @@ -0,0 +1,61 @@ +/* + * libtu/prefix.c + * + * Copyright (c) Tuomo Valkonen 1999-2007. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include "misc.h" +#include "locale.h" +#include "output.h" + +static char *the_prefix=NULL; + +void prefix_set(const char *binloc, const char *dflt) +{ + int i=strlen(binloc); + int j=strlen(dflt); + + if(binloc[0]!='/') + die(TR("This relocatable binary should be started with an absolute path.")); + + while(i>0 && j>0){ + if(binloc[i-1]!=dflt[j-1]) + break; + i--; + j--; + } + + the_prefix=scopyn(binloc, i); + +} + + +char *prefix_add(const char *s) +{ + if(the_prefix==NULL) + return scopy(s); + else + return scat3(the_prefix, "/", s); +} + + +bool prefix_wrap_simple(bool (*fn)(const char *s), const char *s) +{ + bool ret=FALSE; + + if(the_prefix==NULL){ + ret=fn(s); + }else{ + char *s2=prefix_add(s); + if(s2!=NULL){ + ret=fn(s2); + free(s2); + } + } + + return ret; +} diff --git a/libtu/prefix.h b/libtu/prefix.h new file mode 100644 index 00000000..d9d2f210 --- /dev/null +++ b/libtu/prefix.h @@ -0,0 +1,17 @@ +/* + * libtu/prefix.h + * + * Copyright (c) Tuomo Valkonen 1999-2007. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef _LIBTU_PREFIX_H +#define _LIBTU_PREFIX_H + +extern void prefix_set(const char *binloc, const char *dflt); +extern char *prefix_add(const char *s); +extern bool prefix_wrap_simple(bool (*fn)(const char *s), const char *s); + +#endif /* _LIBTU_PREFIX_H */ diff --git a/libtu/private.h b/libtu/private.h new file mode 100644 index 00000000..470e7908 --- /dev/null +++ b/libtu/private.h @@ -0,0 +1,27 @@ +/* + * libtu/private.h + * + * Copyright (c) Tuomo Valkonen 2004. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_PRIVATE_H +#define LIBTU_PRIVATE_H + +#ifndef CF_NO_GETTEXT + +#include + +#define TR(X) dgettext("libtu", X) + +#else + +#define TR(X) (X) + +#endif + +#define DUMMY_TR(X) X + +#endif /* LIBTU_PRIVATE_H */ diff --git a/libtu/ptrlist.c b/libtu/ptrlist.c new file mode 100644 index 00000000..f86d9a4b --- /dev/null +++ b/libtu/ptrlist.c @@ -0,0 +1,208 @@ +/* + * libtu/ptrlist.c + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include "obj.h" +#include "ptrlist.h" +#include "types.h" +#include "dlist.h" +#include "misc.h" + + +static void free_node(PtrList **ptrlist, PtrList *node) +{ + UNLINK_ITEM(*ptrlist, node, next, prev); + free(node); +} + + +static PtrList *mknode(void *ptr) +{ + PtrList *node; + + if(ptr==NULL) + return NULL; + + node=ALLOC(PtrList); + + if(node==NULL) + return FALSE; + + node->ptr=ptr; + + return node; +} + + +static PtrList *ptrlist_find_node(PtrList *ptrlist, void *ptr) +{ + PtrList *node=ptrlist; + + while(node!=NULL){ + if(node->ptr==ptr) + break; + node=node->next; + } + + return node; +} + + +bool ptrlist_contains(PtrList *ptrlist, void *ptr) +{ + return (ptrlist_find_node(ptrlist, ptr)!=NULL); +} + + +bool ptrlist_insert_last(PtrList **ptrlist, void *ptr) +{ + PtrList *node=mknode(ptr); + + if(node==NULL) + return FALSE; + + LINK_ITEM_LAST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_insert_first(PtrList **ptrlist, void *ptr) +{ + PtrList *node=mknode(ptr); + + if(node==NULL) + return FALSE; + + LINK_ITEM_FIRST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_reinsert_last(PtrList **ptrlist, void *ptr) +{ + PtrList *node=ptrlist_find_node(*ptrlist, ptr); + + if(node==NULL) + return ptrlist_insert_last(ptrlist, ptr); + + UNLINK_ITEM(*ptrlist, node, next, prev); + LINK_ITEM_LAST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_reinsert_first(PtrList **ptrlist, void *ptr) +{ + PtrList *node=ptrlist_find_node(*ptrlist, ptr); + + if(node==NULL) + return ptrlist_insert_first(ptrlist, ptr); + + UNLINK_ITEM(*ptrlist, node, next, prev); + LINK_ITEM_FIRST(*ptrlist, node, next, prev); + + return TRUE; +} + + +bool ptrlist_remove(PtrList **ptrlist, void *ptr) +{ + PtrList *node=ptrlist_find_node(*ptrlist, ptr); + + if(node!=NULL) + free_node(ptrlist, node); + + return (node!=NULL); +} + + +void ptrlist_clear(PtrList **ptrlist) +{ + while(*ptrlist!=NULL) + free_node(ptrlist, *ptrlist); +} + + +PtrListIterTmp ptrlist_iter_tmp=NULL; + + +void ptrlist_iter_init(PtrListIterTmp *state, PtrList *ptrlist) +{ + *state=ptrlist; +} + + +void *ptrlist_iter(PtrListIterTmp *state) +{ + void *ptr=NULL; + + if(*state!=NULL){ + ptr=(*state)->ptr; + (*state)=(*state)->next; + } + + return ptr; +} + + +void ptrlist_iter_rev_init(PtrListIterTmp *state, PtrList *ptrlist) +{ + *state=(ptrlist==NULL ? NULL : ptrlist->prev); +} + + +void *ptrlist_iter_rev(PtrListIterTmp *state) +{ + void *ptr=NULL; + + if(*state!=NULL){ + ptr=(*state)->ptr; + *state=(*state)->prev; + if((*state)->next==NULL) + *state=NULL; + } + + return ptr; +} + + +void *ptrlist_take_first(PtrList **ptrlist) +{ + PtrList *node=*ptrlist; + void *ptr; + + if(node==NULL) + return NULL; + + ptr=node->ptr; + + free_node(ptrlist, node); + + return ptr; +} + + +void *ptrlist_take_last(PtrList **ptrlist) +{ + PtrList *node=*ptrlist; + void *ptr; + + if(node==NULL) + return NULL; + + node=node->prev; + + ptr=node->ptr; + + free_node(ptrlist, node); + + return ptr; +} diff --git a/libtu/ptrlist.h b/libtu/ptrlist.h new file mode 100644 index 00000000..70da547a --- /dev/null +++ b/libtu/ptrlist.h @@ -0,0 +1,58 @@ +/* + * libtu/ptrlist.h + * + * Copyright (c) Tuomo Valkonen 1999-2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_PTRLIST_H +#define LIBTU_PTRLIST_H + +#include "types.h" +#include "iterable.h" + + +INTRSTRUCT(PtrList); + + +DECLSTRUCT(PtrList){ + void *ptr; + PtrList *next, *prev; +}; + + +typedef PtrList* PtrListIterTmp; + +#define PTRLIST_FIRST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->ptr) +#define PTRLIST_LAST(TYPE, LL) ((LL)==NULL ? NULL : (TYPE)(LL)->prev->ptr) +#define PTRLIST_EMPTY(LIST) ((LIST)==NULL) + +#define FOR_ALL_ON_PTRLIST(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(ptrlist_iter_init, (TYPE)ptrlist_iter, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_PTRLIST_REV(TYPE, VAR, LL, TMP) \ + FOR_ALL_ITER(ptrlist_iter_rev_init, \ + (TYPE)ptrlist_iter_rev, VAR, LL, &(TMP)) + +#define FOR_ALL_ON_PTRLIST_UNSAFE(TYPE, VAR, LL) \ + FOR_ALL_ON_PTRLIST(TYPE, VAR, LL, ptrlist_iter_tmp) + +extern PtrListIterTmp ptrlist_iter_tmp; + +extern bool ptrlist_insert_last(PtrList **ptrlist, void *ptr); +extern bool ptrlist_insert_first(PtrList **ptrlist, void *ptr); +extern bool ptrlist_reinsert_last(PtrList **ptrlist, void *ptr); +extern bool ptrlist_reinsert_first(PtrList **ptrlist, void *ptr); +extern bool ptrlist_remove(PtrList **ptrlist, void *ptr); +extern bool ptrlist_contains(PtrList *ptrlist, void *ptr); +extern void ptrlist_clear(PtrList **ptrlist); +extern void ptrlist_iter_init(PtrListIterTmp *state, PtrList *ptrlist); +extern void *ptrlist_iter(PtrListIterTmp *state); +extern void ptrlist_iter_rev_init(PtrListIterTmp *state, PtrList *ptrlist); +extern void *ptrlist_iter_rev(PtrListIterTmp *state); +extern void *ptrlist_take_first(PtrList **ptrlist); +extern void *ptrlist_take_last(PtrList **ptrlist); + +#endif /* LIBTU_PTRLIST_H */ diff --git a/libtu/rb-test.c b/libtu/rb-test.c new file mode 100644 index 00000000..96ea161a --- /dev/null +++ b/libtu/rb-test.c @@ -0,0 +1,98 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Revision 1.2. Jim Plank */ + +#include "rb.h" +#include + +/* an example -- this allocates a red-black tree for integers. For a + * user-specified number of iterations, it does the following: + + * delete a random element in the tree. + * make two new random elements, and insert them into the tree + + * At the end, it prints the sorted list of elements, and then prints + * stats of the number of black nodes in any path in the tree, and + * the minimum and maximum path lengths. + + * Rb_find_ikey and rb_inserti could have been used, but instead + * rb_find_gkey and rb_insertg were used to show how they work. + + */ + +int icomp(char *i, char *j) +{ + int I, J; + + I = (int) i; + J = (int) j; + if (I == J) return 0; + if (I > J) return -1; else return 1; +} + +main(int argc, char **argv) +{ + int i, j, tb, nb, mxp, mnp, p; + int iterations; + Rb_node argt, t; + int *a; + + if (argc != 2) { + fprintf(stderr, "usage: main #iterations\n"); + exit(1); + } + argt = make_rb(); + srandom(time(0)); + iterations = atoi(argv[1]); + a = (int *) malloc (iterations*sizeof(int)); + + for (i = 0; i < atoi(argv[1]); i++) { + if (i > 0) { + j = random()%i; + + rb_delete_node(rb_find_gkey(argt, (char *) (a[j]), icomp)); + a[j] = random() % 1000; + rb_insertg(argt, (char *) (a[j]), NULL, icomp); + } + a[i] = random() % 1000; + rb_insertg(argt, (char *) (a[i]), NULL, icomp); + } + tb = 0; + mxp = 0; + mnp = 0; + rb_traverse(t, argt) { + printf("%d ", t->k.ikey); + nb = rb_nblack(t); + p = rb_plength(t); + if (tb == 0) { + tb = nb; + } else if (tb != nb) { + printf("Conflict: tb=%d, nb=%d\n", tb, nb); + exit(1); + } + mxp = (mxp == 0 || mxp < p) ? p : mxp; + mnp = (mnp == 0 || mxp > p) ? p : mnp; + } + printf("\n"); + + printf("Nblack = %d\n", tb); + printf("Max = %d\n", mxp); + printf("Min = %d\n", mnp); +} diff --git a/libtu/rb.c b/libtu/rb.c new file mode 100644 index 00000000..2e5006a1 --- /dev/null +++ b/libtu/rb.c @@ -0,0 +1,626 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank, + 2004 Tuomo Valkonen. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +#include +#include +#include +#include +#include "rb.h" + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il); +static Rb_node lprev(Rb_node n); +static Rb_node rprev(Rb_node n); +static void recolor(Rb_node n); +static void single_rotate(Rb_node y, int l); +static void rb_print_tree(Rb_node t, int level); +static void rb_iprint_tree(Rb_node t, int level); + +/* Gcc complains if non-char* pointer is passed to printf %p */ +#define DONT_COMPLAIN (char*) + +#define isred(n) (n->s.red) +#define isblack(n) (!isred(n)) +#define isleft(n) (n->s.left) +#define isright(n) (!isleft(n)) +#define isint(n) (n->s.internal) +#define isext(n) (!isint(n)) +#define ishead(n) (n->s.head) +#define isroot(n) (n->s.root) +#define setred(n) n->s.red = 1 +#define setblack(n) n->s.red = 0 +#define setleft(n) n->s.left = 1 +#define setright(n) n->s.left = 0 +#define sethead(n) n->s.head = 1 +#define setroot(n) n->s.root = 1 +#define setint(n) n->s.internal = 1 +#define setext(n) n->s.internal = 0 +#define setnormal(n) { n->s.root = 0; n ->s.head = 0; } +#define sibling(n) ((isleft(n)) ? n->p.parent->c.child.right \ + : n->p.parent->c.child.left) + +static void insert(Rb_node item, Rb_node list) /* Inserts to the end of a list */ +{ + Rb_node last_node; + + last_node = list->c.list.blink; + + list->c.list.blink = item; + last_node->c.list.flink = item; + item->c.list.blink = last_node; + item->c.list.flink = list; +} + +static void delete_item(Rb_node item) /* Deletes an arbitrary iterm */ +{ + item->c.list.flink->c.list.blink = item->c.list.blink; + item->c.list.blink->c.list.flink = item->c.list.flink; +} + +#define mk_new_ext(new, kkkey, vvval) {\ + new = (Rb_node) malloc(sizeof(struct rb_node));\ + if(new==NULL) return NULL;\ + new->v.val = vvval;\ + new->k.key = kkkey;\ + setext(new);\ + setblack(new);\ + setnormal(new);\ +} + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il) +{ + Rb_node newnode; + + newnode = (Rb_node) malloc(sizeof(struct rb_node)); + setint(newnode); + setred(newnode); + setnormal(newnode); + newnode->c.child.left = l; + newnode->c.child.right = r; + newnode->p.parent = p; + newnode->k.lext = l; + newnode->v.rext = r; + l->p.parent = newnode; + r->p.parent = newnode; + setleft(l); + setright(r); + if (ishead(p)) { + p->p.root = newnode; + setroot(newnode); + } else if (il) { + setleft(newnode); + p->c.child.left = newnode; + } else { + setright(newnode); + p->c.child.right = newnode; + } + recolor(newnode); +} + + +Rb_node lprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isright(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node rprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isleft(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node make_rb() +{ + Rb_node head; + + head = (Rb_node) malloc (sizeof(struct rb_node)); + if(head!=NULL){ + head->c.list.flink = head; + head->c.list.blink = head; + head->p.root = head; + head->k.key = ""; + sethead(head); + } + return head; +} + +Rb_node rb_find_ikey_n(Rb_node n, int ikey, int *fnd) +{ + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_ikey_n called on non-head %p\n", + DONT_COMPLAIN n); + exit(1); + } + if (n->p.root == n) return n; + if (ikey == n->c.list.blink->k.ikey) { + *fnd = 1; + return n->c.list.blink; + } + if (ikey > n->c.list.blink->k.ikey) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + if (ikey == n->k.lext->k.ikey) { + *fnd = 1; + return n->k.lext; + } + n = (ikey < n->k.lext->k.ikey) ? n->c.child.left : n->c.child.right; + } +} + +Rb_node rb_find_ikey(Rb_node n, int ikey) +{ + int fnd; + return rb_find_ikey_n(n, ikey, &fnd); +} + +Rb_node rb_find_gkey_n(Rb_node n, const void *key, Rb_compfn *fxn, int *fnd) +{ + int cmp; + + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_gkey_n called on non-head %p\n", + DONT_COMPLAIN n); + exit(1); + } + if (n->p.root == n) return n; + cmp = (*fxn)(key, n->c.list.blink->k.key); + if (cmp == 0) { + *fnd = 1; + return n->c.list.blink; + } + if (cmp > 0) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + cmp = (*fxn)(key, n->k.lext->k.key); + if (cmp == 0) { + *fnd = 1; + return n->k.lext; + } + if (cmp < 0) n = n->c.child.left ; else n = n->c.child.right; + } +} + +Rb_node rb_find_gkey(Rb_node n, const void *key, Rb_compfn *fxn) +{ + int fnd; + return rb_find_gkey_n(n, key, fxn, &fnd); +} + +Rb_node rb_find_key_n(Rb_node n, const char *key, int *fnd) +{ + return rb_find_gkey_n(n, key, (Rb_compfn*)strcmp, fnd); +} + +Rb_node rb_find_key(Rb_node n, const char *key) +{ + int fnd; + return rb_find_gkey_n(n, key, (Rb_compfn*)strcmp, &fnd); +} + +static int ptrcmp(const void *a, const void *b) +{ + return (ap.root == n) { /* Tree is empty */ + mk_new_ext(newnode, key, val); + insert(newnode, n); + n->p.root = newnode; + newnode->p.parent = n; + setroot(newnode); + return newnode; + } else { + mk_new_ext(newright, key, val); + insert(newright, n); + newleft = newright->c.list.blink; + setnormal(newleft); + mk_new_int(newleft, newright, newleft->p.parent, isleft(newleft)); + p = rprev(newright); + if (!ishead(p)) p->k.lext = newright; + return newright; + } + } else { + mk_new_ext(newleft, key, val); + insert(newleft, n); + setnormal(n); + mk_new_int(newleft, n, n->p.parent, isleft(n)); + p = lprev(newleft); + if (!ishead(p)) p->v.rext = newleft; + return newleft; + } +} + +static void recolor(Rb_node n) +{ + Rb_node p, gp, s; + int done = 0; + + while(!done) { + if (isroot(n)) { + setblack(n); + return; + } + + p = n->p.parent; + + if (isblack(p)) return; + + if (isroot(p)) { + setblack(p); + return; + } + + gp = p->p.parent; + s = sibling(p); + if (isred(s)) { + setblack(p); + setred(gp); + setblack(s); + n = gp; + } else { + done = 1; + } + } + /* p's sibling is black, p is red, gp is black */ + + if ((isleft(n) == 0) == (isleft(p) == 0)) { + single_rotate(gp, isleft(n)); + setblack(p); + setred(gp); + } else { + single_rotate(p, isleft(n)); + single_rotate(gp, isleft(n)); + setblack(n); + setred(gp); + } +} + +static void single_rotate(Rb_node y, int l) +{ + int rl=0, ir=0; + Rb_node x=NULL, yp=NULL; + void *tmp=NULL; + + ir = isroot(y); + yp = y->p.parent; + if (!ir) { + rl = isleft(y); + } + + if (l) { + x = y->c.child.left; + y->c.child.left = x->c.child.right; + setleft(y->c.child.left); + y->c.child.left->p.parent = y; + x->c.child.right = y; + setright(y); + } else { + x = y->c.child.right; + y->c.child.right = x->c.child.left; + setright(y->c.child.right); + y->c.child.right->p.parent = y; + x->c.child.left = y; + setleft(y); + } + + x->p.parent = yp; + y->p.parent = x; + if (ir) { + yp->p.root = x; + setnormal(y); + setroot(x); + } else { + if (rl) { + yp->c.child.left = x; + setleft(x); + } else { + yp->c.child.right = x; + setright(x); + } + } +} + +void rb_delete_node(Rb_node n) +{ + Rb_node s, p, gp; + char ir; + + if (isint(n)) { + fprintf(stderr, "Cannot delete an internal node: %p\n", DONT_COMPLAIN n); + exit(1); + } + if (ishead(n)) { + fprintf(stderr, "Cannot delete the head of an rb_tree: %p\n", DONT_COMPLAIN n); + exit(1); + } + delete_item(n); /* Delete it from the list */ + p = n->p.parent; /* The only node */ + if (isroot(n)) { + p->p.root = p; + free(n); + return; + } + s = sibling(n); /* The only node after deletion */ + if (isroot(p)) { + s->p.parent = p->p.parent; + s->p.parent->p.root = s; + setroot(s); + free(p); + free(n); + return; + } + gp = p->p.parent; /* Set parent to sibling */ + s->p.parent = gp; + if (isleft(p)) { + gp->c.child.left = s; + setleft(s); + } else { + gp->c.child.right = s; + setright(s); + } + ir = isred(p); + free(p); + free(n); + + if (isext(s)) { /* Update proper rext and lext values */ + p = lprev(s); + if (!ishead(p)) p->v.rext = s; + p = rprev(s); + if (!ishead(p)) p->k.lext = s; + } else if (isblack(s)) { + fprintf(stderr, "DELETION PROB -- sib is black, internal\n"); + exit(1); + } else { + p = lprev(s); + if (!ishead(p)) p->v.rext = s->c.child.left; + p = rprev(s); + if (!ishead(p)) p->k.lext = s->c.child.right; + setblack(s); + return; + } + + if (ir) return; + + /* Recolor */ + + n = s; + p = n->p.parent; + s = sibling(n); + while(isblack(p) && isblack(s) && isint(s) && + isblack(s->c.child.left) && isblack(s->c.child.right)) { + setred(s); + n = p; + if (isroot(n)) return; + p = n->p.parent; + s = sibling(n); + } + + if (isblack(p) && isred(s)) { /* Rotation 2.3b */ + single_rotate(p, isright(n)); + setred(p); + setblack(s); + s = sibling(n); + } + + { Rb_node x, z; char il; + + if (isext(s)) { + fprintf(stderr, "DELETION ERROR: sibling not internal\n"); + exit(1); + } + + il = isleft(n); + x = il ? s->c.child.left : s->c.child.right ; + z = sibling(x); + + if (isred(z)) { /* Rotation 2.3f */ + single_rotate(p, !il); + setblack(z); + if (isred(p)) setred(s); else setblack(s); + setblack(p); + } else if (isblack(x)) { /* Recoloring only (2.3c) */ + if (isred(s) || isblack(p)) { + fprintf(stderr, "DELETION ERROR: 2.3c not quite right\n"); + exit(1); + } + setblack(p); + setred(s); + return; + } else if (isred(p)) { /* 2.3d */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + setred(s); + return; + } else { /* 2.3e */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + return; + } + } +} + + +void rb_print_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree %p is empty\n", DONT_COMPLAIN t); + } else if (ishead(t)) { + printf("Head: %p. Root = %p\n", DONT_COMPLAIN t, DONT_COMPLAIN t->p.root); + rb_print_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node %p: %c,%c: p=%p, k=%s\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->k.key); + } else { + rb_print_tree(t->c.child.left, level+2); + rb_print_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node %p: %c,%c: l=%p, r=%p, p=%p, lr=(%s,%s)\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->c.child.left, DONT_COMPLAIN t->c.child.right, + DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->k.lext->k.key, + DONT_COMPLAIN t->v.rext->k.key); + } + } +} + +void rb_iprint_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree %p is empty\n", DONT_COMPLAIN t); + } else if (ishead(t)) { + printf("Head: %p. Root = %p, < = %p, > = %p\n", + DONT_COMPLAIN t, DONT_COMPLAIN t->p.root, + DONT_COMPLAIN t->c.list.blink, + DONT_COMPLAIN t->c.list.flink); + rb_iprint_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node %p: %c,%c: p=%p, <=%p, >=%p k=%d\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->p.parent, DONT_COMPLAIN t->c.list.blink, + DONT_COMPLAIN t->c.list.flink, t->k.ikey); + } else { + rb_iprint_tree(t->c.child.left, level+2); + rb_iprint_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node %p: %c,%c: l=%p, r=%p, p=%p, lr=(%d,%d)\n", + DONT_COMPLAIN t, isred(t)?'R':'B', isleft(t)?'l':'r', + DONT_COMPLAIN t->c.child.left, DONT_COMPLAIN t->c.child.right, + DONT_COMPLAIN t->p.parent, t->k.lext->k.ikey, t->v.rext->k.ikey); + } + } +} + +int rb_nblack(Rb_node n) +{ + int nb; + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_nblack called on a non-external node %p\n", + DONT_COMPLAIN n); + exit(1); + } + nb = 0; + while(!ishead(n)) { + if (isblack(n)) nb++; + n = n->p.parent; + } + return nb; +} + +int rb_plength(Rb_node n) +{ + int pl; + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_plength called on a non-external node %p\n", + DONT_COMPLAIN n); + exit(1); + } + pl = 0; + while(!ishead(n)) { + pl++; + n = n->p.parent; + } + return pl; +} + +void rb_free_tree(Rb_node n) +{ + if (!ishead(n)) { + fprintf(stderr, "ERROR: Rb_free_tree called on a non-head node\n"); + exit(1); + } + + while(rb_first(n) != rb_nil(n)) { + rb_delete_node(rb_first(n)); + } + free(n); +} + +void *rb_val(Rb_node n) +{ + return n->v.val; +} + +Rb_node rb_insert_a(Rb_node nd, const void *key, void *val) +{ + return rb_insert_b(nd->c.list.flink, key, val); +} + +Rb_node rb_insert(Rb_node tree, const char *key, void *val) +{ + return rb_insert_b(rb_find_key(tree, key), key, val); +} + +Rb_node rb_inserti(Rb_node tree, int ikey, void *val) +{ + return rb_insert_b(rb_find_ikey(tree, ikey), (void *) ikey, val); +} + +Rb_node rb_insertg(Rb_node tree, const void *key, void *val, Rb_compfn *func) +{ + return rb_insert_b(rb_find_gkey(tree, key, func), key, val); +} + +Rb_node rb_insertp(Rb_node tree, const void *key, void *val) +{ + return rb_insertg(tree, key, val, ptrcmp); +} + + diff --git a/libtu/rb.h b/libtu/rb.h new file mode 100644 index 00000000..fec92c05 --- /dev/null +++ b/libtu/rb.h @@ -0,0 +1,143 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank, + 2004 Tuomo Valkonen. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +#ifndef LIBTU_RB_H +#define LIBTU_RB_H + +typedef struct rb_status { + unsigned red : 1 ; + unsigned internal : 1 ; + unsigned left : 1 ; + unsigned root : 1 ; + unsigned head : 1 ; +} Rb_status; + +/* Main rb_node. You only ever use the fields + + c.list.flink + c.list.blink + + k.key or k.ikey + v.val +*/ + + +typedef struct rb_node { + union { + struct { + struct rb_node *flink; + struct rb_node *blink; + } list; + struct { + struct rb_node *left; + struct rb_node *right; + } child; + } c; + union { + struct rb_node *parent; + struct rb_node *root; + } p; + Rb_status s; + union { + int ikey; + const void *key; + struct rb_node *lext; + } k; + union { + int ival; + void *val; + struct rb_node *rext; + } v; +} *Rb_node; + + +typedef int Rb_compfn(const void *, const void *); + + +extern Rb_node make_rb(); /* Creates a new rb-tree */ + +/* Creates a node with key key and val val and inserts it into the tree. + rb_insert uses strcmp() as comparison funcion. rb_inserti uses <>=, + rb_insertg uses func() */ + +extern Rb_node rb_insert(Rb_node tree, const char *key, void *val); +extern Rb_node rb_inserti(Rb_node tree, int ikey, void *val); +extern Rb_node rb_insertp(Rb_node tree, const void *key, void *val); +extern Rb_node rb_insertg(Rb_node tree, const void *key, void *val, + Rb_compfn *func); + + +/* returns an external node in t whose value is equal + k or whose value is the smallest value greater than k. */ + +extern Rb_node rb_find_key(Rb_node root, const char *key); +extern Rb_node rb_find_ikey(Rb_node root, int ikey); +extern Rb_node rb_find_pkey(Rb_node root, const void *key); +extern Rb_node rb_find_gkey(Rb_node root, const void *key, Rb_compfn *func); + + +/* Works just like the find_key versions only it returns whether or not + it found the key in the integer variable found */ + +extern Rb_node rb_find_key_n(Rb_node root, const char *key, int *found); +extern Rb_node rb_find_ikey_n(Rb_node root, int ikey, int *found); +extern Rb_node rb_find_pkey_n(Rb_node root, const void *key, int *found); +extern Rb_node rb_find_gkey_n(Rb_node root, const void *key, + Rb_compfn *func, int *found); + + +/* Creates a node with key key and val val and inserts it into the + tree before/after node nd. Does not check to ensure that you are + keeping the correct order */ + +extern Rb_node rb_insert_b(Rb_node nd, const void *key, void *val); +extern Rb_node rb_insert_a(Rb_node nd, const void *key, void *val); + + +extern void rb_delete_node(Rb_node node); /* Deletes and frees a node (but + not the key or val) */ +extern void rb_free_tree(Rb_node root); /* Deletes and frees an entire tree */ + +extern void *rb_val(Rb_node node); /* Returns node->v.val -- this is to shut + lint up */ + +extern int rb_nblack(Rb_node n); /* returns # of black nodes in path from + n to the root */ +int rb_plength(Rb_node n); /* returns the # of nodes in path from + n to the root */ + +#define rb_first(n) (n->c.list.flink) +#define rb_last(n) (n->c.list.blink) +#define rb_next(n) (n->c.list.flink) +#define rb_prev(n) (n->c.list.blink) +#define rb_empty(t) (t->c.list.flink == t) +#ifndef rb_nil +#define rb_nil(t) (t) +#endif + +#define rb_traverse(ptr, lst) \ + for(ptr = rb_first(lst); ptr != rb_nil(lst); ptr = rb_next(ptr)) + +#endif /* LIBTU_RB_H */ diff --git a/libtu/setparam.c b/libtu/setparam.c new file mode 100644 index 00000000..3b5e5eb1 --- /dev/null +++ b/libtu/setparam.c @@ -0,0 +1,60 @@ +/* + * libtu/setparam.c + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "setparam.h" + +int libtu_string_to_setparam(const char *str) +{ + if(str!=NULL){ + if(strcmp(str, "set")==0 || strcmp(str, "true")==0) + return SETPARAM_SET; + else if(strcmp(str, "unset")==0 || strcmp(str, "false")==0) + return SETPARAM_UNSET; + else if(strcmp(str, "toggle")==0) + return SETPARAM_TOGGLE; + } + + return SETPARAM_UNKNOWN; +} + + +bool libtu_do_setparam(int sp, bool val) +{ + switch(sp){ + case SETPARAM_SET: + return TRUE; + case SETPARAM_UNSET: + return FALSE; + case SETPARAM_TOGGLE: + return (val==FALSE); + default: + return val; + } +} + +bool libtu_do_setparam_str(const char *str, bool val) +{ + return libtu_do_setparam(libtu_string_to_setparam(str), val); +} + + +int libtu_setparam_invert(int sp) +{ + switch(sp){ + case SETPARAM_SET: + return SETPARAM_UNSET; + case SETPARAM_UNSET: + return SETPARAM_SET; + default: + return sp; + } +} + diff --git a/libtu/setparam.h b/libtu/setparam.h new file mode 100644 index 00000000..8e6fad6e --- /dev/null +++ b/libtu/setparam.h @@ -0,0 +1,27 @@ +/* + * libtu/setparam.h + * + * Copyright (c) Tuomo Valkonen 2005. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_SETPARAM_H +#define LIBTU_SETPARAM_H + +#include "types.h" + +enum{ + SETPARAM_UNKNOWN, + SETPARAM_SET, + SETPARAM_UNSET, + SETPARAM_TOGGLE +}; + +extern int libtu_string_to_setparam(const char *str); +extern bool libtu_do_setparam_str(const char *str, bool val); +extern bool libtu_do_setparam(int sp, bool val); +extern int libtu_setparam_invert(int sp); + +#endif /* LIBTU_SETPARAM_H */ diff --git a/libtu/snprintf_2.2/INSTALL b/libtu/snprintf_2.2/INSTALL new file mode 100644 index 00000000..9cdf4691 --- /dev/null +++ b/libtu/snprintf_2.2/INSTALL @@ -0,0 +1,24 @@ +HOW TO INSTALL - manually: + + 1. Read the description of macros that control the bahaviour + of the program at the beginning of the file snprintf.c, + change the definitions in snprintf.c or in Makefile if necessary. + + 2. make + + 3. move the file snprintf.o where your programs will find it + + + +HOW TO INSTALL - with autoconf: + +Contributed by Caolan McNamara : + + Though it might be overkill for snprintf I also have + an autoconf and automaked version which works out the need + for long long support and makes snprintf optionally into + a dynamic library on libtool supported platforms. + + 1. cd with_autoconf + + 2. follow instructions in the file INSTALL there. diff --git a/libtu/snprintf_2.2/LICENSE.txt b/libtu/snprintf_2.2/LICENSE.txt new file mode 100644 index 00000000..aeb06a7f --- /dev/null +++ b/libtu/snprintf_2.2/LICENSE.txt @@ -0,0 +1,121 @@ +The Frontier Artistic License Version 1.0 +Derived from the Artistic License at OpenSource.org. +Submitted to OpenSource.org for Open Source Initiative certification. + +Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to +make reasonable modifications. + +Definitions + + "Package" refers to the script, suite, file, or collection of + scripts, suites, and/or files distributed by the Copyright Holder, + and to derivatives of that Package created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes of + the Copyright Holder. + + "Copyright Holder" is whoever is named in the copyright statement + or statements for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the basis + of media cost, duplication charges, time of people involved, and + so on. (You will not be required to justify it to the Copyright + Holder, but only to the computing community at large as a market + that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it under + the same conditions they received it. + + +Terms + +1. You may make and give away verbatim copies of the source form of +the Standard Version of this Package without restriction, provided +that you duplicate all of the original copyright notices and +associated disclaimers. + +2. You may apply bug fixes, portability fixes, and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, +provided that you insert a prominent notice in each changed script, +suite, or file stating how and when you changed that script, suite, +or file, and provided that you do at least ONE of the following: + + a) Use the modified Package only within your corporation or + organization, or retain the modified Package solely for personal use. + + b) Place your modifications in the Public Domain or otherwise make + them Freely Available, such as by posting said modifications to Usenet + or an equivalent medium, or placing the modifications on a major archive + site such as ftp.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + c) Rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page (or equivalent) for each non-standard executable + that clearly documents how it differs from the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) Distribute a Standard Version of the executables and library + files, together with instructions (in the manual page or + equivalent) on where to get the Standard Version. + + b) Accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) Accompany any non-standard executables with their corresponding + Standard Version executables, give the non-standard executables + non-standard names, and clearly document the differences in manual + pages (or equivalent), together with instructions on where to get + the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of +this Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) +software distribution provided that you do not advertise this Package +as a product of your own. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whomever generated +them, and may be sold commercially, and may be aggregated with this +Package. + +7. Scripts, suites, or programs supplied by you that depend on or +otherwise make use of this Package shall not be considered part of +this Package. + +8. The name of the Copyright Holder may not be used to endorse or +promote products derived from this software without specific prior +written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End + + +http://www.spinwardstars.com/frontier/fal.html diff --git a/libtu/snprintf_2.2/Makefile.unused b/libtu/snprintf_2.2/Makefile.unused new file mode 100644 index 00000000..2e022f7a --- /dev/null +++ b/libtu/snprintf_2.2/Makefile.unused @@ -0,0 +1,43 @@ +# Make sure you include -DHAVE_SNPRINTF in CFLAGS if your system +# does have snprintf! + +# If you need (long long int) support and you sprintf supports it, +# define -DSNPRINTF_LONGLONG_SUPPORT + +CC = gcc + +CFLAGS = -DPREFER_PORTABLE_SNPRINTF -O3 \ + -Wall -Wpointer-arith -Wwrite-strings \ + -Wcast-qual -Wcast-align -Waggregate-return \ + -Wmissing-prototypes -Wmissing-declarations \ + -Wshadow -Wstrict-prototypes + +# -DNEED_ASPRINTF -DNEED_ASNPRINTF -DNEED_VASPRINTF -DNEED_VASNPRINTF +# -DNEED_SNPRINTF_ONLY + +# Digital Unix: native compiler usually produces better code than gcc +#CC = cc +#CFLAGS = -DPREFER_PORTABLE_SNPRINTF -O4 -std1 -arch host + +# Recommend to leave COMPATIBILITY empty for normal use. +# Should be set for bug compatibility when running tests +# too keep them less chatty. +COMPATIBILITY = + +#COMPATIBILITY = -DSOLARIS_BUG_COMPATIBLE +#COMPATIBILITY = -DHPUX_BUG_COMPATIBLE +#COMPATIBILITY = -DDIGITAL_UNIX_BUG_COMPATIBLE +#COMPATIBILITY = -DPERL_BUG_COMPATIBLE +#COMPATIBILITY = -DLINUX_COMPATIBLE + +.c.o: + rm -f $@ + $(CC) $(CFLAGS) $(COMPATIBILITY) -c $*.c + +all:snprintf.o Makefile + +test::snprintf.o test.c Makefile + $(CC) $(CFLAGS) $(COMPATIBILITY) snprintf.o -o $@ test.c + +clean: + /usr/bin/rm -f *.o test core diff --git a/libtu/snprintf_2.2/README b/libtu/snprintf_2.2/README new file mode 100644 index 00000000..af36f8e0 --- /dev/null +++ b/libtu/snprintf_2.2/README @@ -0,0 +1,283 @@ + + snprintf.c + - a portable implementation of snprintf, + including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf + + snprintf is a routine to convert numeric and string arguments to + formatted strings. It is similar to sprintf(3) provided in a system's + C library, yet it requires an additional argument - the buffer size - + and it guarantees never to store anything beyond the given buffer, + regardless of the format or arguments to be formatted. Some newer + operating systems do provide snprintf in their C library, but many do + not or do provide an inadequate (slow or idiosyncratic) version, which + calls for a portable implementation of this routine. + +Author + + Mark Martinec , April 1999, June 2000 + Copyright © 1999, Mark Martinec + +Terms and conditions ... + + This program is free software; you can redistribute it and/or modify + it under the terms of the Frontier Artistic License which comes with + this Kit. + +Features + + * careful adherence to specs regarding flags, field width and + precision; + * good performance for large string handling (large format, large + argument or large paddings). Performance is similar to system's + sprintf and in several cases significantly better (make sure you + compile with optimizations turned on, tell the compiler the code + is strict ANSI if necessary to give it more freedom for + optimizations); + * return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * written in standard ISO/ANSI C - requires an ANSI C compiler. + +Supported conversion specifiers and data types + + This snprintf only supports the following conversion specifiers: s, c, + d, o, u, x, X, p (and synonyms: i, D, U, O - see below) with flags: + '-', '+', ' ', '0' and '#'. An asterisk is supported for field width + as well as precision. + + Length modifiers 'h' (short int), 'l' (long int), and 'll' (long long + int) are supported. + + NOTE: + + If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + length modifier 'll' is recognized but treated the same as 'l', + which may cause argument value truncation! Defining + SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + handles length modifier 'll'. long long int is a language extension + which may not be portable. + + Conversion of numeric data (conversion specifiers d, o, u, x, X, p) + with length modifiers (none or h, l, ll) is left to the system routine + sprintf, but all handling of flags, field width and precision as well + as c and s conversions is done very carefully by this portable + routine. If a string precision (truncation) is specified (e.g. %.8s) + it is guaranteed the string beyond the specified precision will not be + referenced. + + Length modifiers h, l and ll are ignored for c and s conversions (data + types wint_t and wchar_t are not supported). + + The following common synonyms for conversion characters are supported: + * i is a synonym for d + * D is a synonym for ld, explicit length modifiers are ignored + * U is a synonym for lu, explicit length modifiers are ignored + * O is a synonym for lo, explicit length modifiers are ignored + + The D, O and U conversion characters are nonstandard, they are + supported for backward compatibility only, and should not be used for + new code. + + The following is specifically not supported: + * flag ' (thousands' grouping character) is recognized but ignored + * numeric conversion specifiers: f, e, E, g, G and synonym F, as + well as the new a and A conversion specifiers + * length modifier 'L' (long double) and 'q' (quad - use 'll' + instead) + * wide character/string conversions: lc, ls, and nonstandard + synonyms C and S + * writeback of converted string length: conversion character n + * the n$ specification for direct reference to n-th argument + * locales + + It is permitted for str_m to be zero, and it is permitted to specify + NULL pointer for resulting string argument if str_m is zero (as per + ISO C99). + + The return value is the number of characters which would be generated + for the given input, excluding the trailing null. If this value is + greater or equal to str_m, not all characters from the result have + been stored in str, output bytes beyond the (str_m-1) -th character + are discarded. If str_m is greater than zero it is guaranteed the + resulting string will be null-terminated. + + NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + but is different from some older and vendor implementations, and is + also different from XPG, XSH5, SUSv2 specifications. For historical + discussion on changes in the semantics and standards of snprintf see + printf(3) man page in the Linux programmers manual. + + Routines asprintf and vasprintf return a pointer (in the ptr argument) + to a buffer sufficiently large to hold the resulting string. This + pointer should be passed to free(3) to release the allocated storage + when it is no longer needed. If sufficient space cannot be allocated, + these functions will return -1 and set ptr to be a NULL pointer. These + two routines are a GNU C library extensions (glibc). + + Routines asnprintf and vasnprintf are similar to asprintf and + vasprintf, yet, like snprintf and vsnprintf counterparts, will write + at most str_m-1 characters into the allocated output string, the last + character in the allocated buffer then gets the terminating null. If + the formatted string length (the return value) is greater than or + equal to the str_m argument, the resulting string was truncated and + some of the formatted characters were discarded. These routines + present a handy way to limit the amount of allocated memory to some + sane value. + +Availability + + http://www.ijs.si/software/snprintf/ + * snprintf_1.3.tar.gz (1999-06-30), md5 sum: snprintf_1.3.tar.gz.md5 + * snprintf_2.1.tar.gz (2000-07-14), md5 sum: snprintf_2.1.tar.gz.md5 + * snprintf_2.2.tar.gz (2000-10-18), md5 sum: snprintf_2.2.tar.gz.md5 + +Mailing list + + There is a very low-traffic mailing list snprintf-announce@ijs.si + where announcements about new versions will be posted as well as + warnings about threatening bugs if discovered. The posting is + restricted to snprintf developer(s). + + To subscribe to (or unsubscribe from) the mailing list please visit + the list server's web page + http://mailman.ijs.si/listinfo/snprintf-announce + + You can also subscribe to the list by mailing the command SUBSCRIBE + either in the subject or in the message body to the address + snprintf-announce-request@ijs.si . You will be asked for confirmation + before subscription will be effective. + + The list of members is only accessible to the list administrator, so + there is no need for concern about automatic e-mail address gatherers. + + Questions about the mailing list and concerns for the attention of a + person should be sent to snprintf-announce-admin@ijs.si + + There is no general discussion list about portable snprintf at the + moment. Please send comments and suggestion to the author. + +Revision history + + Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade. + + 1999-06-30 V1.3 Mark Martinec + + + fixed runaway loop (eventually crashing when str_l wraps + beyond 2^31) while copying format string without conversion + specifiers to a buffer that is too short (thanks to Edwin + Young for spotting the problem); + + added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to + snprintf.h + + 2000-02-14 V2.0 (never released) Mark Martinec + + + relaxed license terms: The Artistic License now applies. You + may still apply the GNU GENERAL PUBLIC LICENSE as was + distributed with previous versions, if you prefer; + + changed REVISION HISTORY dates to use ISO 8601 date format; + + added vsnprintf (patch also independently proposed by Caolán + McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + + 2000-06-27 V2.1 Mark Martinec + + + removed POSIX check for str_m < 1; value 0 for str_m is + allowed by ISO C99 (and GNU C library 2.1) (pointed out on + 2000-05-04 by Caolán McNamara, caolan@ csn dot ul dot ie). + Besides relaxed license this change in standards adherence is + the main reason to bump up the major version number; + + added nonstandard routines asnprintf, vasnprintf, asprintf, + vasprintf that dynamically allocate storage for the resulting + string; these routines are not compiled by default, see + comments where NEED_V?ASN?PRINTF macros are defined; + + autoconf contributed by Caolán McNamara + + 2000-10-06 V2.2 Mark Martinec + + + BUG FIX: the %c conversion used a temporary variable that was + no longer in scope when referenced, possibly causing + incorrect resulting character; + + BUG FIX: make precision and minimal field width unsigned to + handle huge values (2^31 <= n < 2^32) correctly; also be more + careful in the use of signed/unsigned/size_t internal + variables -- probably more careful than many vendor + implementations, but there may still be a case where huge + values of str_m, precision or minimal field could cause + incorrect behaviour; + + use separate variables for signed/unsigned arguments, and for + short/int, long, and long long argument lengths to avoid + possible incompatibilities on certain computer architectures. + Also use separate variable arg_sign to hold sign of a numeric + argument, to make code more transparent; + + some fiddling with zero padding and "0x" to make it Linux + compatible; + + systematically use macros fast_memcpy and fast_memset instead + of case-by-case hand optimization; determine some breakeven + string lengths for different architectures; + + terminology change: format -> conversion specifier, C9x -> + ISO/IEC 9899:1999 ("ISO C99"), alternative form -> alternate + form, data type modifier -> length modifier; + + several comments rephrased and new ones added; + + make compiler not complain about 'credits' defined but not + used; + +Other implementations of snprintf + + I am aware of some other (more or less) portable implementations of + snprintf. I do not claim they are free software - please refer to + their respective copyright and licensing terms. If you know of other + versions please let me know. + * a very thorough implementation (src/util_snprintf.c) by the Apache + Group distributed with the Apache web server - + http://www.apache.org/ . Does its own floating point conversions + using routines ecvt(3), fcvt(3) and gcvt(3) from the standard C + library or from the GNU libc. + This is from the code: + + This software [...] was originally based on public domain software + written at the National Center for Supercomputing Applications, + University of Illinois, Urbana-Champaign. + [...] This code is based on, and used with the permission of, the + SIO stdio-replacement strx_* functions by Panos Tsirigotis + for xinetd. + * QCI Utilities use a modified version of snprintf from the Apache + group. + * implementations as distributed with OpenBSD, FreeBSD, and NetBSD + are all wrappers to vfprintf.c, which is derived from software + contributed to Berkeley by Chris Torek. + * implementation from Prof. Patrick Powell , + Dept. Electrical and Computer Engineering, San Diego State + University, San Diego, CA 92182-1309, published in Bugtraq + archives for 3rd quarter (Jul-Aug) 1995. No floating point + conversions. + * Brandon Long's modified version of Prof. + Patrick Powell's snprintf with contributions from others. With + minimal floating point support. + * implementation (src/snprintf.c) as distributed with sendmail - + http://www.sendmail.org/ is a cleaned up Prof. Patrick Powell's + version to compile properly and to support .precision and %lx. + * implementation from Caolán McNamara available at + http://www.csn.ul.ie/~caolan/publink/snprintf-1.1.tar.gz, handles + floating point. + * implementation used by newlog (a replacement for syslog(3)) made + available by the SOS Corporation. Enabling floating point support + is a compile-time option. + * implementation by Michael Richardson is + available at http://sandelman.ottawa.on.ca/SSW/snp/snp.html. It is + based on BSD44-lite's vfprintf() call, modified to function on + SunOS. Needs internal routines from the 4.4 strtod (included), + requires GCC to compile the long long (aka quad_t) portions. + * implementation from Tomi Salo distributed with SSH + 2.0 Unix Server. Not in public domain. Floating point conversions + done by system's sprintf. + * and for completeness: my portable version described in this very + document available at http://www.ijs.si/software/snprintf/ . + + In retrospect, it appears that a lot of effort was wasted by many + people for not being aware of what others are doing. Sigh. + + Also of interest: The Approved Base Working Group Resolution for XSH5, + Ref: bwg98-006, Topic: snprintf. + _________________________________________________________________ + + mm + Last updated: 2000-10-18 + + Valid HTML 4.0! diff --git a/libtu/snprintf_2.2/README.html b/libtu/snprintf_2.2/README.html new file mode 100644 index 00000000..4ecaab31 --- /dev/null +++ b/libtu/snprintf_2.2/README.html @@ -0,0 +1,382 @@ + + + + + +snprintf.c - a portable implementation of snprintf +(including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf) + + + + + + + + + +

snprintf.c +
- a portable implementation of snprintf, +
including +vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf +

+ +

snprintf is a routine to convert numeric and string arguments +to formatted strings. It is similar to sprintf(3) provided in a +system's C library, yet it requires an additional argument - the buffer +size - and it guarantees never to store anything beyond the given buffer, +regardless of the format or arguments to be formatted. Some newer +operating systems do provide snprintf in their C library, +but many do not or do provide an inadequate (slow or idiosyncratic) +version, which calls for a portable implementation of this routine. + +

Author

+ +

Mark Martinec +<mark.martinec@ijs.si>, +April 1999, June 2000 +
Copyright © 1999, Mark Martinec + +

Terms and conditions ...

+ +

This program is free software; you can redistribute it +and/or modify it under the terms of the +Frontier Artistic License +which comes with this Kit. + +

Features

+ +
    +
  • careful adherence to specs regarding flags, field width and precision; +
  • good performance for large string handling (large format, large argument +or large paddings). Performance is similar to system's sprintf +and in several cases significantly better (make sure you compile with +optimizations turned on, tell the compiler the code is strict ANSI +if necessary to give it more freedom for optimizations); +
  • return value semantics per ISO/IEC 9899:1999 ("ISO C99"); +
  • written in standard ISO/ANSI C - requires an ANSI C compiler. +
+ +

Supported conversion specifiers and data types

+ +

This snprintf only supports the following conversion specifiers: +s, c, d, o, u, x, X, p (and synonyms: i, D, U, O - see below) +with flags: '-', '+', ' ', '0' and '#'. +An asterisk is supported for field width as well as precision. + +

Length modifiers 'h' (short int), 'l' (long int), +and 'll' (long long int) are supported. + +

NOTE: +

+If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) +the length modifier 'll' is recognized but treated the same as 'l', +which may cause argument value truncation! +Defining SNPRINTF_LONGLONG_SUPPORT requires that your system's +sprintf also handles length modifier 'll'. +long long int is a language extension which may not be portable. +
+ +

Conversion of numeric data (conversion specifiers d, o, u, x, X, p) +with length modifiers (none or h, l, ll) is left to the system +routine sprintf, but all handling of flags, field width and precision +as well as c and s conversions is done very carefully by this portable routine. +If a string precision (truncation) is specified (e.g. %.8s) it is +guaranteed the string beyond the specified precision will not be referenced. + +

Length modifiers h, l and ll are ignored for c and s conversions +(data types wint_t and wchar_t are not supported). + +

The following common synonyms for conversion characters are supported: +

    +
  • i is a synonym for d +
  • D is a synonym for ld, explicit length modifiers are ignored +
  • U is a synonym for lu, explicit length modifiers are ignored +
  • O is a synonym for lo, explicit length modifiers are ignored +
+The D, O and U conversion characters are nonstandard, they are supported +for backward compatibility only, and should not be used for new code. + +

The following is specifically not supported: +

    +
  • flag ' (thousands' grouping character) is recognized but ignored +
  • numeric conversion specifiers: f, e, E, g, G and synonym F, +as well as the new a and A conversion specifiers +
  • length modifier 'L' (long double) +and 'q' (quad - use 'll' instead) +
  • wide character/string conversions: lc, ls, and nonstandard +synonyms C and S +
  • writeback of converted string length: conversion character n +
  • the n$ specification for direct reference to n-th argument +
  • locales +
+ +

It is permitted for str_m to be zero, and it is permitted to specify NULL +pointer for resulting string argument if str_m is zero (as per ISO C99). + +

The return value is the number of characters which would be generated +for the given input, excluding the trailing null. If this value +is greater or equal to str_m, not all characters from the result +have been stored in str, output bytes beyond the (str_m-1) -th character +are discarded. If str_m is greater than zero it is guaranteed +the resulting string will be null-terminated. + +

NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, +but is different from some older and vendor implementations, +and is also different from XPG, XSH5, SUSv2 specifications. +For historical discussion on changes in the semantics and standards +of snprintf see printf(3) man page in the Linux programmers manual. + +

Routines asprintf and vasprintf return a pointer (in the ptr argument) +to a buffer sufficiently large to hold the resulting string. This pointer +should be passed to free(3) to release the allocated storage when it is +no longer needed. If sufficient space cannot be allocated, these functions +will return -1 and set ptr to be a NULL pointer. These two routines are a +GNU C library extensions (glibc). + +

Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, +yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 +characters into the allocated output string, the last character in the +allocated buffer then gets the terminating null. If the formatted string +length (the return value) is greater than or equal to the str_m argument, +the resulting string was truncated and some of the formatted characters +were discarded. These routines present a handy way to limit the amount +of allocated memory to some sane value. + +

Availability

+ +

http://www.ijs.si/software/snprintf/ + +

+ + +

Mailing list

+ +

There is a very low-traffic mailing list snprintf-announce@ijs.si +where announcements about new versions will be posted +as well as warnings about threatening bugs if discovered. +The posting is restricted to snprintf developer(s). + +

To subscribe to (or unsubscribe from) the mailing list +please visit the list server's web page +http://mailman.ijs.si/listinfo/snprintf-announce + +

You can also subscribe to the list by mailing +the command SUBSCRIBE either in the subject or in the message body +to the address snprintf-announce-request@ijs.si . You will be asked for +confirmation before subscription will be effective. + +

The list of members is only accessible to the list administrator, +so there is no need for concern about automatic e-mail address gatherers. + +

Questions about the mailing list and concerns for the attention +of a person should be sent to snprintf-announce-admin@ijs.si + +

There is no general discussion list about portable snprintf +at the moment. Please send comments and suggestion to the author. + + +

Revision history

+ +

Version 1.3 fixes a runaway loop problem from 1.2. Please upgrade. + +

+
1999-06-30 V1.3 Mark Martinec <mark.martinec@ijs.si> +
    +
  • fixed runaway loop (eventually crashing when str_l wraps + beyond 2^31) while copying format string without + conversion specifiers to a buffer that is too short + (thanks to Edwin Young <edwiny@autonomy.com> for spotting the problem); +
  • added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) to snprintf.h +
+ +
2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec@ijs.si> +
    +
  • relaxed license terms: + The Artistic License now applies. + You may still apply the GNU GENERAL PUBLIC LICENSE + as was distributed with previous versions, if you prefer; +
  • changed REVISION HISTORY dates to use + ISO 8601 + date format; +
  • added vsnprintf (patch also independently proposed by + Caolán McNamara 2000-05-04, and Keith M Willenson 2000-06-01) +
+ +
2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si> +
    +
  • removed POSIX check for str_m < 1; value 0 for str_m is + allowed by ISO C99 (and GNU C library 2.1) (pointed out + on 2000-05-04 by Caolán McNamara, caolan@ csn dot ul dot ie). + Besides relaxed license this change in standards adherence + is the main reason to bump up the major version number; +
  • added nonstandard routines asnprintf, vasnprintf, asprintf, + vasprintf that dynamically allocate storage for the + resulting string; these routines are not compiled by default, + see comments where NEED_V?ASN?PRINTF macros are defined; +
  • autoconf contributed by Caolán McNamara +
+ +
2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si> +
    +
  • BUG FIX: the %c conversion used a temporary variable + that was no longer in scope when referenced, + possibly causing incorrect resulting character; +
  • BUG FIX: make precision and minimal field width unsigned + to handle huge values (2^31 <= n < 2^32) correctly; + also be more careful in the use of signed/unsigned/size_t + internal variables -- probably more careful than many + vendor implementations, but there may still be a case + where huge values of str_m, precision or minimal field + could cause incorrect behaviour; +
  • use separate variables for signed/unsigned arguments, + and for short/int, long, and long long argument lengths + to avoid possible incompatibilities on certain + computer architectures. Also use separate variable + arg_sign to hold sign of a numeric argument, + to make code more transparent; +
  • some fiddling with zero padding and "0x" to make it + Linux compatible; +
  • systematically use macros fast_memcpy and fast_memset + instead of case-by-case hand optimization; determine some + breakeven string lengths for different architectures; +
  • terminology change: format -> conversion specifier, + C9x -> ISO/IEC 9899:1999 ("ISO C99"), + alternative form -> alternate form, + data type modifier -> length modifier; +
  • several comments rephrased and new ones added; +
  • make compiler not complain about 'credits' defined but + not used; +
+
+ +

Other implementations of snprintf

+ +

I am aware of some other (more or less) portable implementations +of snprintf. I do not claim they are free software - please refer +to their respective copyright and licensing terms. +If you know of other versions please let +me know. + +

+ +In retrospect, it appears that a lot of effort was wasted by many +people for not being aware of what others are doing. Sigh. + +

Also of interest: +The Approved Base Working Group Resolution for XSH5, +Ref: bwg98-006, Topic: snprintf. + +


+mm +
Last updated: 2000-10-18 + +

Valid HTML 4.0! + + diff --git a/libtu/snprintf_2.2/snprintf-orig.c b/libtu/snprintf_2.2/snprintf-orig.c new file mode 100644 index 00000000..86f42e90 --- /dev/null +++ b/libtu/snprintf_2.2/snprintf-orig.c @@ -0,0 +1,1025 @@ +/* + * snprintf.c - a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This 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 Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf only supports the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is supported for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are supported. + * NOTE: + * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + * length modifier 'll' is recognized but treated the same as 'l', + * which may cause argument value truncation! Defining + * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + * handles length modifier 'll'. long long int is a language extension + * which may not be portable. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (data + * types wint_t and wchar_t are not supported). + * + * The following common synonyms for conversion characters are supported: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are supported + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT supported: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + * REVISION HISTORY + * 1999-04 V0.9 Mark Martinec + * - initial version, some modifications after comparing printf + * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, + * and checking how Perl handles sprintf (differently!); + * 1999-04-09 V1.0 Mark Martinec + * - added main test program, fixed remaining inconsistencies, + * added optional (long long int) support; + * 1999-04-12 V1.1 Mark Martinec + * - support the 'p' conversion (pointer to void); + * - if a string precision is specified + * make sure the string beyond the specified precision + * will not be referenced (e.g. by strlen); + * 1999-04-13 V1.2 Mark Martinec + * - support synonyms %D=%ld, %U=%lu, %O=%lo; + * - speed up the case of long format string with few conversions; + * 1999-06-30 V1.3 Mark Martinec + * - fixed runaway loop (eventually crashing when str_l wraps + * beyond 2^31) while copying format string without + * conversion specifiers to a buffer that is too short + * (thanks to Edwin Young for + * spotting the problem); + * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) + * to snprintf.h + * 2000-02-14 V2.0 (never released) Mark Martinec + * - relaxed license terms: The Artistic License now applies. + * You may still apply the GNU GENERAL PUBLIC LICENSE + * as was distributed with previous versions, if you prefer; + * - changed REVISION HISTORY dates to use ISO 8601 date format; + * - added vsnprintf (patch also independently proposed by + * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + * 2000-06-27 V2.1 Mark Martinec + * - removed POSIX check for str_m<1; value 0 for str_m is + * allowed by ISO C99 (and GNU C library 2.1) - (pointed out + * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). + * Besides relaxed license this change in standards adherence + * is the main reason to bump up the major version number; + * - added nonstandard routines asnprintf, vasnprintf, asprintf, + * vasprintf that dynamically allocate storage for the + * resulting string; these routines are not compiled by default, + * see comments where NEED_V?ASN?PRINTF macros are defined; + * - autoconf contributed by Caolan McNamara + * 2000-10-06 V2.2 Mark Martinec + * - BUG FIX: the %c conversion used a temporary variable + * that was no longer in scope when referenced, + * possibly causing incorrect resulting character; + * - BUG FIX: make precision and minimal field width unsigned + * to handle huge values (2^31 <= n < 2^32) correctly; + * also be more careful in the use of signed/unsigned/size_t + * internal variables - probably more careful than many + * vendor implementations, but there may still be a case + * where huge values of str_m, precision or minimal field + * could cause incorrect behaviour; + * - use separate variables for signed/unsigned arguments, + * and for short/int, long, and long long argument lengths + * to avoid possible incompatibilities on certain + * computer architectures. Also use separate variable + * arg_sign to hold sign of a numeric argument, + * to make code more transparent; + * - some fiddling with zero padding and "0x" to make it + * Linux compatible; + * - systematically use macros fast_memcpy and fast_memset + * instead of case-by-case hand optimization; determine some + * breakeven string lengths for different architectures; + * - terminology change: 'format' -> 'conversion specifier', + * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', + * 'alternative form' -> 'alternate form', + * 'data type modifier' -> 'length modifier'; + * - several comments rephrased and new ones added; + * - make compiler not complain about 'credits' defined but + * not used; + */ + + +/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. + * + * If HAVE_SNPRINTF is defined this module will not produce code for + * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, + * causing this portable version of snprintf to be called portable_snprintf + * (and portable_vsnprintf). + */ +/* #define HAVE_SNPRINTF */ + +/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and + * vsnprintf but you would prefer to use the portable routine(s) instead. + * In this case the portable routine is declared as portable_snprintf + * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') + * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . + * Defining this macro is only useful if HAVE_SNPRINTF is also defined, + * but does does no harm if defined nevertheless. + */ +/* #define PREFER_PORTABLE_SNPRINTF */ + +/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support + * data type (long long int) and length modifier 'll' (e.g. %lld). + * If undefined, 'll' is recognized but treated as a single 'l'. + * + * If the system's sprintf does not handle 'll' + * the SNPRINTF_LONGLONG_SUPPORT must not be defined! + * + * This is off by default as (long long int) is a language extension. + */ +/* #define SNPRINTF_LONGLONG_SUPPORT */ + +/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. + * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, + * otherwise both snprintf and vsnprintf routines will be defined + * and snprintf will be a simple wrapper around vsnprintf, at the expense + * of an extra procedure call. + */ +/* #define NEED_SNPRINTF_ONLY */ + +/* Define NEED_V?ASN?PRINTF macros if you need library extension + * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, + * and your system library does not provide them. They are all small + * wrapper routines around portable_vsnprintf. Defining any of the four + * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY + * and turns on PREFER_PORTABLE_SNPRINTF. + * + * Watch for name conflicts with the system library if these routines + * are already present there. + * + * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as + * specified by C99, to be able to traverse the same list of arguments twice. + * I don't know of any other standard and portable way of achieving the same. + * With some versions of gcc you may use __va_copy(). You might even get away + * with "ap2 = ap", in this case you must not call va_end(ap2) ! + * #define va_copy(ap2,ap) ap2 = ap + */ +/* #define NEED_ASPRINTF */ +/* #define NEED_ASNPRINTF */ +/* #define NEED_VASPRINTF */ +/* #define NEED_VASNPRINTF */ + + +/* Define the following macros if desired: + * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, + * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, + * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, + * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, + * + * - For portable applications it is best not to rely on peculiarities + * of a given implementation so it may be best not to define any + * of the macros that select compatibility and to avoid features + * that vary among the systems. + * + * - Selecting compatibility with more than one operating system + * is not strictly forbidden but is not recommended. + * + * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . + * + * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is + * documented in a sprintf man page on a given operating system + * and actually adhered to by the system's sprintf (but not on + * most other operating systems). It may also refer to and enable + * a behaviour that is declared 'undefined' or 'implementation specific' + * in the man page but a given implementation behaves predictably + * in a certain way. + * + * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf + * that contradicts the sprintf man page on the same operating system. + * + * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE + * conditionals take into account all idiosyncrasies of a particular + * implementation, there may be other incompatibilities. + */ + + + +/* ============================================= */ +/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ +/* ============================================= */ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names snprintf and vsnprintf */ +#define portable_snprintf snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif diff --git a/libtu/snprintf_2.2/snprintf.c b/libtu/snprintf_2.2/snprintf.c new file mode 100644 index 00000000..8720be7e --- /dev/null +++ b/libtu/snprintf_2.2/snprintf.c @@ -0,0 +1,1032 @@ +#include +#define NEED_ASPRINTF +#define NEED_VASPRINTF +/* + * snprintf.c - a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This 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 Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf only supports the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is supported for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are supported. + * NOTE: + * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + * length modifier 'll' is recognized but treated the same as 'l', + * which may cause argument value truncation! Defining + * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + * handles length modifier 'll'. long long int is a language extension + * which may not be portable. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (data + * types wint_t and wchar_t are not supported). + * + * The following common synonyms for conversion characters are supported: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are supported + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT supported: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + * REVISION HISTORY + * 1999-04 V0.9 Mark Martinec + * - initial version, some modifications after comparing printf + * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, + * and checking how Perl handles sprintf (differently!); + * 1999-04-09 V1.0 Mark Martinec + * - added main test program, fixed remaining inconsistencies, + * added optional (long long int) support; + * 1999-04-12 V1.1 Mark Martinec + * - support the 'p' conversion (pointer to void); + * - if a string precision is specified + * make sure the string beyond the specified precision + * will not be referenced (e.g. by strlen); + * 1999-04-13 V1.2 Mark Martinec + * - support synonyms %D=%ld, %U=%lu, %O=%lo; + * - speed up the case of long format string with few conversions; + * 1999-06-30 V1.3 Mark Martinec + * - fixed runaway loop (eventually crashing when str_l wraps + * beyond 2^31) while copying format string without + * conversion specifiers to a buffer that is too short + * (thanks to Edwin Young for + * spotting the problem); + * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) + * to snprintf.h + * 2000-02-14 V2.0 (never released) Mark Martinec + * - relaxed license terms: The Artistic License now applies. + * You may still apply the GNU GENERAL PUBLIC LICENSE + * as was distributed with previous versions, if you prefer; + * - changed REVISION HISTORY dates to use ISO 8601 date format; + * - added vsnprintf (patch also independently proposed by + * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + * 2000-06-27 V2.1 Mark Martinec + * - removed POSIX check for str_m<1; value 0 for str_m is + * allowed by ISO C99 (and GNU C library 2.1) - (pointed out + * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). + * Besides relaxed license this change in standards adherence + * is the main reason to bump up the major version number; + * - added nonstandard routines asnprintf, vasnprintf, asprintf, + * vasprintf that dynamically allocate storage for the + * resulting string; these routines are not compiled by default, + * see comments where NEED_V?ASN?PRINTF macros are defined; + * - autoconf contributed by Caolan McNamara + * 2000-10-06 V2.2 Mark Martinec + * - BUG FIX: the %c conversion used a temporary variable + * that was no longer in scope when referenced, + * possibly causing incorrect resulting character; + * - BUG FIX: make precision and minimal field width unsigned + * to handle huge values (2^31 <= n < 2^32) correctly; + * also be more careful in the use of signed/unsigned/size_t + * internal variables - probably more careful than many + * vendor implementations, but there may still be a case + * where huge values of str_m, precision or minimal field + * could cause incorrect behaviour; + * - use separate variables for signed/unsigned arguments, + * and for short/int, long, and long long argument lengths + * to avoid possible incompatibilities on certain + * computer architectures. Also use separate variable + * arg_sign to hold sign of a numeric argument, + * to make code more transparent; + * - some fiddling with zero padding and "0x" to make it + * Linux compatible; + * - systematically use macros fast_memcpy and fast_memset + * instead of case-by-case hand optimization; determine some + * breakeven string lengths for different architectures; + * - terminology change: 'format' -> 'conversion specifier', + * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', + * 'alternative form' -> 'alternate form', + * 'data type modifier' -> 'length modifier'; + * - several comments rephrased and new ones added; + * - make compiler not complain about 'credits' defined but + * not used; + */ + + +/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. + * + * If HAVE_SNPRINTF is defined this module will not produce code for + * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, + * causing this portable version of snprintf to be called portable_snprintf + * (and portable_vsnprintf). + */ +/* #define HAVE_SNPRINTF */ + +/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and + * vsnprintf but you would prefer to use the portable routine(s) instead. + * In this case the portable routine is declared as portable_snprintf + * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') + * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . + * Defining this macro is only useful if HAVE_SNPRINTF is also defined, + * but does does no harm if defined nevertheless. + */ +/* #define PREFER_PORTABLE_SNPRINTF */ + +/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support + * data type (long long int) and length modifier 'll' (e.g. %lld). + * If undefined, 'll' is recognized but treated as a single 'l'. + * + * If the system's sprintf does not handle 'll' + * the SNPRINTF_LONGLONG_SUPPORT must not be defined! + * + * This is off by default as (long long int) is a language extension. + */ +/* #define SNPRINTF_LONGLONG_SUPPORT */ + +/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. + * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, + * otherwise both snprintf and vsnprintf routines will be defined + * and snprintf will be a simple wrapper around vsnprintf, at the expense + * of an extra procedure call. + */ +/* #define NEED_SNPRINTF_ONLY */ + +/* Define NEED_V?ASN?PRINTF macros if you need library extension + * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, + * and your system library does not provide them. They are all small + * wrapper routines around portable_vsnprintf. Defining any of the four + * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY + * and turns on PREFER_PORTABLE_SNPRINTF. + * + * Watch for name conflicts with the system library if these routines + * are already present there. + * + * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as + * specified by C99, to be able to traverse the same list of arguments twice. + * I don't know of any other standard and portable way of achieving the same. + * With some versions of gcc you may use __va_copy(). You might even get away + * with "ap2 = ap", in this case you must not call va_end(ap2) ! + * #define va_copy(ap2,ap) ap2 = ap + */ +#ifndef va_copy +#define va_copy(ap2,ap) ap2 = ap +#endif + +/* #define NEED_ASPRINTF */ +/* #define NEED_ASNPRINTF */ +/* #define NEED_VASPRINTF */ +/* #define NEED_VASNPRINTF */ + + +/* Define the following macros if desired: + * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, + * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, + * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, + * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, + * + * - For portable applications it is best not to rely on peculiarities + * of a given implementation so it may be best not to define any + * of the macros that select compatibility and to avoid features + * that vary among the systems. + * + * - Selecting compatibility with more than one operating system + * is not strictly forbidden but is not recommended. + * + * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . + * + * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is + * documented in a sprintf man page on a given operating system + * and actually adhered to by the system's sprintf (but not on + * most other operating systems). It may also refer to and enable + * a behaviour that is declared 'undefined' or 'implementation specific' + * in the man page but a given implementation behaves predictably + * in a certain way. + * + * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf + * that contradicts the sprintf man page on the same operating system. + * + * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE + * conditionals take into account all idiosyncrasies of a particular + * implementation, there may be other incompatibilities. + */ + + + +/* ============================================= */ +/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ +/* ============================================= */ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names snprintf and vsnprintf */ +#define portable_snprintf snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (size_t)(q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff); + str_arg_l = !q ? precision : (size_t)(q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + ssize_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif diff --git a/libtu/snprintf_2.2/snprintf.h b/libtu/snprintf_2.2/snprintf.h new file mode 100644 index 00000000..70ec8415 --- /dev/null +++ b/libtu/snprintf_2.2/snprintf.h @@ -0,0 +1,26 @@ +#ifndef _PORTABLE_SNPRINTF_H_ +#define _PORTABLE_SNPRINTF_H_ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#ifdef HAVE_SNPRINTF +#include +#else +extern int snprintf(char *, size_t, const char *, /*args*/ ...); +extern int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF) +extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#define snprintf portable_snprintf +#define vsnprintf portable_vsnprintf +#endif + +extern int asprintf (char **ptr, const char *fmt, /*args*/ ...); +extern int vasprintf (char **ptr, const char *fmt, va_list ap); +extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap); + +#endif diff --git a/libtu/snprintf_2.2/test.c b/libtu/snprintf_2.2/test.c new file mode 100644 index 00000000..5fddd825 --- /dev/null +++ b/libtu/snprintf_2.2/test.c @@ -0,0 +1,689 @@ +/* + * test.c - test a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This 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 Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * NOTE: This test program is a QUICK and DIRTY tool + * ===== used while testing and benchmarking my portable snprintf. + * Certain data types are not fully supported, certain test + * cases were fabricated during testing by modifying the code + * or running it by specifying test parameters in the command line. + * + * You are on your own if you want to use this test program! + */ + +/* If no command arguments are specified do the exhaustive test. + * This takes a long time. You may want to reduce the fw and fp + * upper limits in the for loops. + * You may also reduce the number of test elements in the array iargs. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#ifdef HAVE_SNPRINTF +extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#else +extern int snprintf(char *, size_t, const char *, /*args*/ ...); +extern int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#ifndef HAVE_SNPRINTF +#define portable_snprintf snprintf +#define portable_vsnprintf vsnprintf +#endif + +#ifndef CLOCKS_PER_SEC +#define CLOCKS_PER_SEC 100 +#endif + +#define min(a,b) ((a)<(b) ? (a) : (b)) +#define max(a,b) ((a)>(b) ? (a) : (b)) + +extern int asprintf (char **ptr, const char *fmt, /*args*/ ...); +extern int vasprintf (char **ptr, const char *fmt, va_list ap); +extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap); + +#ifndef NEED_SNPRINTF_ONLY +int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +int wrap_vsnprintf(char *str, size_t str_m, const char *fmt, ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#ifdef NEED_VASPRINTF +int wrap_vasprintf(char **ptr, const char *fmt, /*args*/ ...); +int wrap_vasprintf(char **ptr, const char *fmt, ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vasprintf(ptr, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#ifdef NEED_VASNPRINTF +int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +int wrap_vasnprintf(char **ptr, size_t str_m, const char *fmt, ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = vasnprintf(ptr, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +int main(int argc, char *argv[]) { + char str1[256], str2[256]; +#ifdef HAVE_SNPRINTF + char str3[256]; +#endif + int len1, len2, len3; + int bad = 0; + size_t str_m = 20; /* declared str size */ + + if (0) { + /* benchmarking */ + const int cnt = 100000; + size_t size; + char str[40000]; + time_t t0,t; + int j,len,l1,l2; + char *p; + int breakpoint; + + size = 18000; + + printf("\n\nsize = %d\n", (int)size); + p = malloc(size); assert(p); + memset(p,'h',size); p[size-1] = '\0'; + t0 = clock(); + + printf("\ndetermine breakeven point to see when it is worth\n"); + printf("calling memcpy and when to do inline string copy\n"); + printf("str_l, memcpy, inline\n"); + for (breakpoint=0; breakpoint<=35; breakpoint++) { + register size_t nnn = (size_t)breakpoint; + printf("%5ld", nnn); + for (j=10*cnt; j>0; j--) memcpy(str, p, nnn); + t = clock(); printf(" %1.3f", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=10*cnt; j>0; j--) { + register size_t nn = (size_t)breakpoint; + if (nn > 0) { + register char *dd; register const char *ss; + for (ss=p, dd=str; nn>0; nn--) *dd++ = *ss++; + } + } + t = clock(); printf(" %1.3f\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + } + + printf("\nmeasuring time to SKIP a long format with no conversions\n"); + p[0] = '%'; p[1] = 's'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p,"1234567890"); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,(size_t)8,p,"1234567890"); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + p[0] = p[1] = 'h'; + + printf("\nmeasuring time to copy a long format with no conversions\n"); + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring time to copy a long format with one conversion\n"); + p[size-10] = '%'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring string argument copy speed\n"); + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%.18000s",p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%.18000s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%.18000s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring left padding speed\n"); + p[0] = '\0'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%-18000s",p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%-18000s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%-18000s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring right padding speed\n"); + p[0] = '\0'; + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%18000s",p); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%18000s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%18000s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring zero padding speed\n"); + for (j=cnt; j>0; j--) l1=portable_snprintf(NULL,(size_t)0,"%018000d",1); + t = clock(); printf("t_port_nul = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + for (j=cnt; j>0; j--) l2=portable_snprintf(str,sizeof(str),"%018000d",1); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + assert(l1==l2); + for (j=cnt; j>0; j--) sprintf(str,"%018000d",1); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + printf("\nmeasuring system's sprintf to efficiently handle truncated strings\n"); + memset(p,'h',size); p[size-1] = '\0'; + t0 = clock(); + for (j=cnt; j>0; j--) len = strlen(p); + printf("len = %d\n", len); + t = clock(); printf("t_strlen = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; +/* test if the system sprintf scans the whole string (e.g. by strlen) + * before recognizing this was a bad idea since the format specified + * a truncated string precision, e.g. "%.8s" . + */ + for (j=cnt; j>0; j--) sprintf(str,"%.2s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; +#ifdef HAVE_SNPRINTF + for (j=cnt; j>0; j--) snprintf(str,sizeof(str),"%.2s",p); + t = clock(); printf("t_sys = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; +#endif + for (j=cnt; j>0; j--) portable_snprintf(str,sizeof(str),"%.2s",p); + t = clock(); printf("t_port = %1.3f s\n", (float)(t-t0)/CLOCKS_PER_SEC); t0 = t; + + free(p); + return 0; + } + + +/* preliminary halfhearted test */ +{ + const char fmt[] = "Bla%.4s%05iHE%%%-50sTail"; + char *ptr4=0, *ptr5=0, *ptr6=0, *ptr7=0; + int len1f; + char str_full[256]; + + printf("\npreliminary test: snprintf\n"); + len1 = snprintf(str1, str_m, fmt, "abcdef",-12,"str"); + len1f = snprintf(str_full, sizeof(str_full), fmt, "abcdef",-12,"str"); + assert(len1f==len1); + assert(memcmp(str1,str_full,min(len1,str_m-1)) == 0); + assert(str1[str_m-1] == '\0'); + assert(str_full[sizeof(str_full)-1] == '\0'); + +#ifndef NEED_SNPRINTF_ONLY + printf("preliminary test: vsnprintf\n"); + len2 = wrap_vsnprintf(str2, str_m, fmt, "abcdef",-12,"str"); + assert(len2==len1); + assert(memcmp(str1,str2,min(len1+1,str_m)) == 0); + assert(str2[str_m-1] == '\0'); +#endif + +#ifdef NEED_ASPRINTF + printf("preliminary test: asprintf\n"); + len4 = asprintf(&ptr4, fmt, "abcdef",-12,"str"); + assert(ptr4); + assert(len4==len1); + assert(memcmp(str_full,ptr4,min(len4+1,sizeof(str_full))) == 0); + assert(ptr4[len4] == '\0'); +#endif + +#ifdef NEED_ASNPRINTF + printf("preliminary test: asnprintf\n"); + len5 = asnprintf(&ptr5, str_m, fmt, "abcdef",-12,"str"); + assert(ptr5); + assert(len5==len1); + assert(memcmp(str1,ptr5,min(len5+1,str_m)) == 0); + assert(ptr5[len5] == '\0'); +#endif + +#ifdef NEED_VASPRINTF + printf("preliminary test: vasprintf\n"); + len6 = wrap_vasprintf(&ptr6, fmt, "abcdef",-12,"str"); + assert(ptr6); + assert(len6==len1); + assert(memcmp(str_full,ptr6,min(len6+1,sizeof(str_full))) == 0); + assert(ptr6[len6] == '\0'); +#endif + +#ifdef NEED_VASNPRINTF + printf("preliminary test: vasnprintf\n"); + len7 = wrap_vasnprintf(&ptr7, str_m, fmt, "abcdef",-12,"str"); + assert(ptr7); + assert(len7==len1); + assert(memcmp(str1,ptr7,min(len7+1,str_m)) == 0); + assert(ptr7[len7] == '\0'); +#endif + + if (ptr4) free(ptr4); + if (ptr5) free(ptr5); + if (ptr6) free(ptr6); + if (ptr7) free(ptr7); +} + +/* second preliminary halfhearted test */ +{ + printf("\nsecond preliminary test:\n"); + + printf("test 0a\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), ""); + len2 = sprintf (str2, ""); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 0b\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "YK"); + len2 = sprintf (str2, "YK"); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 1\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%+d",0); + len2 = sprintf (str2, "%+d",0); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 2\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.2147483647s", "13"); + len2 = sprintf (str2, "%.2147483647s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 3a\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.2147483648s", "13"); + len2 = sprintf (str2, "%.2147483648s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 3b\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.2147483649s", "13"); + len2 = sprintf (str2, "%.2147483649s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 4\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.2147483647s", "13"); + len2 = sprintf (str2, "%-.2147483647s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 5\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.2147483648s", "13"); + len2 = sprintf (str2, "%-.2147483648s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 6\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.4294967295s", "13"); + len2 = sprintf (str2, "%.4294967295s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 7\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.4294967296s", "13"); + len2 = sprintf (str2, "%.4294967296s", "13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + + printf("test 12\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483647,"13"); + len2 = sprintf (str2, "%.*s", 2147483647,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 13\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 2147483648U,"13"); + len2 = sprintf (str2, "%.*s", 2147483648U,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 14\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483647,"13"); + len2 = sprintf (str2, "%-.*s", 2147483647,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 15\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.*s", 2147483648U,"13"); + len2 = sprintf (str2, "%-.*s", 2147483648U,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 16\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967295U,"13"); + len2 = sprintf (str2, "%.*s", 4294967295U,"13"); + printf("len1=%d, len2=%d\n", len1,len2); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 17\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.*s", 4294967296U,"13"); +/* len2 = sprintf (str2, "%.*s", 4294967296U,"13"); */ /* core dumps on HPUX */ +/* assert(len1==len2); + * assert(memcmp(str1,str2,(size_t)len1) == 0); + */ + + + printf("test 95\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%c",'A'); + len2 = sprintf (str2, "%c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 96\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%10c",'A'); + len2 = sprintf (str2, "%10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 97\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-10c",'A'); + len2 = sprintf (str2, "%-10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 98\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%.10c",'A'); + len2 = sprintf (str2, "%.10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + printf("test 99\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, sizeof(str1), "%-.10c",'A'); + len2 = sprintf (str2, "%-.10c",'A'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)len1) == 0); + + + printf("test 100\n"); + memset(str1,'x',sizeof(str1)); memset(str2,'x',sizeof(str2)); + len1 = snprintf(str1, (size_t)8, "blaBhb%shehe%cX","ABCD",'1'); + len2 = sprintf (str2, "blaBhb%shehe%cX","ABCD",'1'); + assert(len1==len2); + assert(memcmp(str1,str2,(size_t)7) == 0); + assert(str1[7] == '\0'); + assert(memcmp(str1+14,str2+16,(size_t)(len1-16)) == 0); + +} + + /* testing for correctness and compatibility */ + if (argc >= 3) { + char *c; int alldigits = 1; + for (c=argv[2]; *c; c++) + if (! (*c == '-' || (*c >= '0' && *c <= '9'))) alldigits = 0; + if (alldigits) { + int j = atoi(argv[2]); + len1 = portable_snprintf(str1, str_m, argv[1], j, 3); + len2 = sprintf(str2, argv[1], j, 3); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, argv[1], j, 3); +#endif + } else { + len1 = portable_snprintf(str1, str_m, argv[1], argv[2], 3); + len2 = sprintf(str2, argv[1], argv[2], 3); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, argv[1], argv[2], 3); +#endif + } + printf("portable: |%s| len = %d\n", str1, len1); + printf("sys sprintf: |%s| len = %d\n", str2, len2); +#ifdef HAVE_SNPRINTF + printf("sys snprintf: |%s| len = %d\n", str3, len3); +#endif + } else { /* exhaustive testing */ + + const char flags[] = "+- 0#"; /* set of test flags (including '\0')*/ + int flags_l = strlen(flags); + + const char fspec[] = "scdpoxXuiy"; /* set of test formats (including '\0') */ + int fspec_l = strlen(fspec); + +#ifdef SNPRINTF_LONGLONG_SUPPORT + const char datatype[] = " hl2"; /* set of datatypes */ +#else + const char datatype[] = " hl"; /* set of datatypes */ +#endif + int datatype_l = strlen(datatype); + + const long int iargs[] = /* set of numeric test arguments */ + { 0,1,9,10,28,99,100,127,128,129,998,1000,32767,32768,32769, + -1,-9,-10,-28,-99,-100,-127,-128,-129, + -998,-1000,-32767,-32768,-32769 }; + int iargs_l = sizeof(iargs)/sizeof(iargs[0]); + const char *sargs[] = /* set of string test arguments */ + { "", "a", "0", "-ab", "abcde", "abcdefghijk mnopqrstuv" }; + int sargs_l = sizeof(sargs)/sizeof(sargs[0]); + + char fmt[256]; + int fmt_l; + int a, fs, fl1, fl2, fl3, fl4, fl5, fw, fp, dt; + + for (fs=0; fs<=fspec_l; fs++) { /* format specifier */ + int strtype = (fspec[fs] == 's' || fspec[fs] == '%'); + int args_l = (strtype ? sargs_l : iargs_l); + + for (fw= -1; fw<=3; fw++) { /* minimal field width */ + + printf("Trying format %%"); + if (fw >= 0) printf("%d", fw); + if (fspec[fs]) putchar(fspec[fs]); + putchar('\n'); + + for (fp= -2; fp<=3; fp++) { /* format field precision */ + + /* data type modifiers */ + for (dt=0; dt < ((strtype||fspec[fs]=='c') ? 1 : datatype_l); dt++) { + + int dataty = datatype[dt]; + + if (fspec[fs] == 'D' || fspec[fs] == 'U' || fspec[fs] == 'O') + dataty = 'l'; + + if (fspec[fs] == 'p' && dataty == '2') continue; + + for (fl1=0; fl1<=flags_l; fl1++) { /* flags */ + for (fl2=0; fl2<=flags_l; fl2++) { + for (fl3=0; fl3<=flags_l; fl3++) { + for (fl4=0; fl4<=flags_l; fl4++) { + for (fl5=0; fl5<=flags_l; fl5++) { + + for (a=0; a= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fw); + if (fp >= -1) { + fmt[fmt_l++] = '.'; + if (fp >= 0) fmt_l += sprintf(fmt+fmt_l, "%d", fp); + } + if (dataty == '2') + { fmt[fmt_l++] = 'l'; fmt[fmt_l++] = 'l'; } + else if (dataty != ' ') + { fmt[fmt_l++] = dataty; } + + if (fspec[fs]) fmt[fmt_l++] = fspec[fs]; + fmt[fmt_l++] = '\0'; + + if (a==0 && fl1==flags_l && fl2==flags_l && fl3==flags_l + && fl4==flags_l && fl5==flags_l) printf("%s\n", fmt); + +#ifdef HAVE_SNPRINTF + memset(str1,'G',sizeof(str1)); + memset(str2,'G',sizeof(str2)); + memset(str3,'G',sizeof(str3)); +#endif + len1 = len2 = len3 = 0; + if (strtype) { + len1 = portable_snprintf(str1, str_m, fmt, sargs[a]); + len2 = sprintf(str2, fmt, sargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, sargs[a]); +#endif + } else if (fspec[fs] == 'p') { + len1 = portable_snprintf(str1, str_m, fmt, (void *)iargs[a]); + len2 = sprintf(str2, fmt, (void *)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (void *)iargs[a]); +#endif + } else { + switch (dataty) { + case '\0': + len1 = portable_snprintf(str1, str_m, fmt, (int) iargs[a]); + len2 = sprintf(str2, fmt, (int) iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (int) iargs[a]); +#endif + break; + case 'h': + len1 = portable_snprintf(str1, str_m, fmt, (short int)iargs[a]); + len2 = sprintf(str2, fmt, (short int)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (short int)iargs[a]); +#endif + break; + case 'l': + len1 = portable_snprintf(str1, str_m, fmt, (long int)iargs[a]); + len2 = sprintf(str2, fmt, (long int)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (long int)iargs[a]); +#endif + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + len1 = portable_snprintf(str1, str_m, fmt, (long long int)iargs[a]); + len2 = sprintf(str2, fmt, (long long int)iargs[a]); +#ifdef HAVE_SNPRINTF + len3 = snprintf(str3, str_m, fmt, (long long int)iargs[a]); +#endif + break; +#endif + } + } + + if (0) { +#ifdef HAVE_SNPRINTF + } else if (len1 != len3 || + memcmp(str1,str3,min(len1+20,sizeof(str1))) != 0) { + bad = 1; + if (strtype) printf("\n2: %s, <%s>\n", fmt, sargs[a]); + else printf("\n2: %s, %ld\n", fmt, iargs[a]); + printf("portable: |%s| len = %d\n", str1, len1); + printf("sys sprintf: |%s| len = %d\n", str2, len2); + printf("sys snprintf: |%s| len = %d\n", str3, len3); +#else + } else if (len1 != len2 || + (len1>0 && memcmp(str1,str2,min(len1,str_m)-1) != 0)) { + bad = 1; + if (strtype) printf("\n1: %s, <%s>\n", fmt, sargs[a]); + else printf("\n1: %s, %ld\n", fmt, iargs[a]); + printf("portable: |%s| len = %d\n", str1, len1); + printf("sys sprintf: |%s| len = %d\n", str2, len2); +#endif + } + if (bad) return(1); + } + + } + } + } + } + + } + } + } + } + } + } + return (bad?1:0); +} diff --git a/libtu/stringstore.c b/libtu/stringstore.c new file mode 100644 index 00000000..3c5b7a1e --- /dev/null +++ b/libtu/stringstore.c @@ -0,0 +1,144 @@ +/* + * libtu/stringstore.c + * + * Copyright (c) Tuomo Valkonen 2004-2007. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include + +#include "misc.h" +#include "output.h" +#include "rb.h" +#include "stringstore.h" + + +static Rb_node stringstore=NULL; + + +const char *stringstore_get(StringId id) +{ + return (id==STRINGID_NONE + ? NULL + : (const char*)(((Rb_node)id)->k.key)); +} + + +typedef struct{ + const char *key; + uint len; +} D; + +static int cmp(const void *d_, const char *nodekey) +{ + D *d=(D*)d_; + + int res=strncmp(d->key, nodekey, d->len); + + return (res!=0 + ? res + : (nodekey[d->len]=='\0' ? 0 : -1)); +} + + +StringId stringstore_find_n(const char *str, uint l) +{ + Rb_node node; + int found=0; + D d; + + if(stringstore==NULL) + return STRINGID_NONE; + + d.key=str; + d.len=l; + + node=rb_find_gkey_n(stringstore, &d, (Rb_compfn*)cmp, &found); + + if(!found) + return STRINGID_NONE; + + return (StringId)node; +} + + +StringId stringstore_find(const char *str) +{ + return stringstore_find_n(str, strlen(str)); +} + + +StringId stringstore_alloc_n(const char *str, uint l) +{ + Rb_node node=(Rb_node)stringstore_find_n(str, l); + char *s; + + if(node!=NULL){ + node->v.ival++; + return node; + } + + if(stringstore==NULL){ + stringstore=make_rb(); + if(stringstore==NULL) + return STRINGID_NONE; + } + + s=scopyn(str, l); + + if(s==NULL) + return STRINGID_NONE; + + node=rb_insert(stringstore, s, NULL); + + if(node==NULL) + return STRINGID_NONE; + + node->v.ival=1; + + return (StringId)node; +} + + +StringId stringstore_alloc(const char *str) +{ + if(str==NULL) + return STRINGID_NONE; + + return stringstore_alloc_n(str, strlen(str)); +} + + +void stringstore_free(StringId id) +{ + Rb_node node=(Rb_node)id; + + if(node==NULL) + return; + + if(node->v.ival<=0){ + warn("Stringstore reference count corrupted."); + return; + } + + node->v.ival--; + + if(node->v.ival==0){ + char *s=(char*)(node->k.key); + rb_delete_node(node); + free(s); + } +} + + +void stringstore_ref(StringId id) +{ + Rb_node node=(Rb_node)id; + + if(node!=NULL) + node->v.ival++; +} + diff --git a/libtu/stringstore.h b/libtu/stringstore.h new file mode 100644 index 00000000..5510ef41 --- /dev/null +++ b/libtu/stringstore.h @@ -0,0 +1,25 @@ +/* + * libtu/stringstore.h + * + * Copyright (c) Tuomo Valkonen 2004-2007. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_STRINGSTORE_H +#define LIBTU_STRINGSTORE_H + +typedef void* StringId; + +#define STRINGID_NONE ((StringId)0) + +extern const char *stringstore_get(StringId id); +extern StringId stringstore_find(const char *str); +extern StringId stringstore_alloc(const char *str); +extern StringId stringstore_find_n(const char *str, uint l); +extern StringId stringstore_alloc_n(const char *str, uint l); +extern void stringstore_free(StringId id); +extern void stringstore_ref(StringId id); + +#endif /* LIBTU_STRINGSTORE_H */ diff --git a/libtu/system-autodetect.mk b/libtu/system-autodetect.mk new file mode 100644 index 00000000..60be973d --- /dev/null +++ b/libtu/system-autodetect.mk @@ -0,0 +1,88 @@ +## +## System settings +## + +# Beware: in releases, the Notion system.mk is used to build both libtu, +# libextl and notion - so structural changes to this file should also be +# carried out on the Notion system.mk + +## +## Installation paths +## + +PREFIX=/usr/local/ + +# No need to modify these usually +BINDIR=$(PREFIX)/bin +ETCDIR=$(PREFIX)/etc +MANDIR=$(PREFIX)/man +DOCDIR=$(PREFIX)/doc +LIBDIR=$(PREFIX)/lib +INCDIR=$(PREFIX)/include + + +## +## libc +## + +# You may uncomment this if you know your system has +# asprintf and vasprintf in the c library. (gnu libc has.) +# If HAS_SYSTEM_ASPRINTF is not defined, an implementation +# in sprintf_2.2/ is used. +HAS_SYSTEM_ASPRINTF=1 + + +## +## C compiler +## + +CC=gcc + +# The POSIX_SOURCE, XOPEN_SOURCE and WARN options should not be necessary, +# they're mainly for development use. So, if they cause trouble (not +# the ones that should be used on your system or the system is broken), +# just comment them out. + +C89_SOURCE=-ansi +POSIX_SOURCE=-D_POSIX_SOURCE + +# Same as '-Wall -pedantic' without '-Wunused' as callbacks often +# have unused variables. +WARN= -W -Wimplicit -Wreturn-type -Wswitch -Wcomment \ + -Wtrigraphs -Wformat -Wchar-subscripts \ + -Wparentheses -pedantic -Wuninitialized + + +CFLAGS=-g -Os $(WARN) $(DEFINES) $(INCLUDES) $(EXTRA_INCLUDES) -DHAS_SYSTEM_ASPRINTF=$(HAS_SYSTEM_ASPRINTF) +LDFLAGS=-g $(LIBS) $(EXTRA_LIBS) + + +## +## make depend +## + +DEPEND_FILE=.depend +MAKE_DEPEND=$(CC) -M $(DEFINES) $(INCLUDES) $(EXTRA_INCLUDES) > $(DEPEND_FILE) + + +## +## AR +## + +AR=ar +ARFLAGS=cr +RANLIB=ranlib + + +## +## Install & strip +## + +# Should work almost everywhere +INSTALL=sh $(TOPDIR)/install-sh -c +INSTALLDIR=mkdir -p + +BIN_MODE=755 +DATA_MODE=664 + +STRIP=strip diff --git a/libtu/tester.c b/libtu/tester.c new file mode 100644 index 00000000..4f27fd6d --- /dev/null +++ b/libtu/tester.c @@ -0,0 +1,54 @@ +/* + * libtu/tester.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "misc.h" +#include "tokenizer.h" +#include "util.h" + +int main(int argc, char *argv[]) +{ + Tokenizer*tokz; + Token tok=TOK_INIT; + + libtu_init(argv[0]); + + if(!(tokz=tokz_open_file(stdin, "stdin"))) + return EXIT_FAILURE; + + while(tokz_get_token(tokz, &tok)){ + switch(tok.type){ + case TOK_LONG: + printf("long - %ld\n", TOK_LONG_VAL(&tok)); + break; + case TOK_DOUBLE: + printf("double - %g\n", TOK_DOUBLE_VAL(&tok)); + break; + case TOK_CHAR: + printf("char - '%c'\n", TOK_CHAR_VAL(&tok)); + break; + case TOK_STRING: + printf("string - \"%s\"\n", TOK_STRING_VAL(&tok)); + break; + case TOK_IDENT: + printf("ident - %s\n", TOK_IDENT_VAL(&tok)); + break; + case TOK_COMMENT: + printf("comment - %s\n", TOK_COMMENT_VAL(&tok)); + break; + case TOK_OP: + printf("operator - %03x\n", TOK_OP_VAL(&tok)); + break; + } + } + + return EXIT_SUCCESS; +} + diff --git a/libtu/tester2.c b/libtu/tester2.c new file mode 100644 index 00000000..832c4744 --- /dev/null +++ b/libtu/tester2.c @@ -0,0 +1,69 @@ +/* + * libtu/tester2.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "misc.h" +#include "tokenizer.h" +#include "parser.h" +#include "util.h" + + +static bool test_fn(Tokenizer *tokz, int n, Token *toks) +{ + printf("test_fn() %d %s\n", n, TOK_IDENT_VAL(toks)); + + return TRUE; +} + + + +static bool sect_fn(Tokenizer *tokz, int n, Token *toks) +{ + printf("sect_fn() %d %s\n", n, TOK_IDENT_VAL(toks+1)); + + return TRUE; +} + + +static bool test2_fn(Tokenizer *tokz, int n, Token *toks) +{ + printf("test2_fn() %d %s %f\n", n, TOK_BOOL_VAL(toks+1) ? "TRUE" : "FALSE", TOK_DOUBLE_VAL(toks+2)); + + return TRUE; +} + +static bool test3_fn(Tokenizer *tokz, int n, Token *toks) +{ + if(n<=2) + printf("test3_fn() %d \"%s\"\n", n, TOK_STRING_VAL(toks+1)); + else + printf("test3_fn() %d \"%s\" %ld\n", n, TOK_STRING_VAL(toks+1), TOK_LONG_VAL(toks+2)); + + return TRUE; +} + + +static ConfOpt opts[]={ + {"test", NULL, test_fn, NULL}, + {"t2", "bd", test2_fn, NULL}, + {"foo", "s?l", test3_fn, NULL}, + {"sect", "s", sect_fn, opts}, + {NULL, NULL, NULL, NULL} +}; + + +int main(int argc, char *argv[]) +{ + libtu_init(argv[0]); + parse_config_file(stdin, opts, TOKZ_ERROR_TOLERANT); + + return EXIT_SUCCESS; +} + diff --git a/libtu/tester3.c b/libtu/tester3.c new file mode 100644 index 00000000..12bda855 --- /dev/null +++ b/libtu/tester3.c @@ -0,0 +1,72 @@ +/* + * libtu/tester3.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include + +#include "util.h" +#include "misc.h" +#include "optparser.h" + + +static const char usage[]= + "Usage: $p [options]\n" + "\n" + "Where options are:\n" + "$o\n"; + + +static OptParserOpt opts[]={ + {'o', "opt", OPT_ARG, "OPTION", "foo bar baz quk asdf jklö äölk dfgh quik aaaa bbbb cccc dddd eeee ffff"}, + {'f', "file", OPT_ARG, "FILE", "asdfsadlfölökjasdflökjasdflkjöasdflkjöas dlöfjkasdfölkjasdfölkjasdfasdflöjasdfkasödjlfkasdlföjasdölfjkölkasjdfasdfölkjasd asdöljfasöldf asdölfköasdlf asfdlök asdföljkadsfölasdfölasdölkfjasdölfasödlflöskflasdföaölsdf"}, + {'v', "view", 0, NULL, "asfasdf"}, + {'z', "zip", 0, NULL, "asdfasdf"}, + {'x', "extract", 0, NULL, "asdfasdf"}, + {0, NULL, 0, NULL, NULL} +}; + +static OptParserCommonInfo tester3_cinfo={ + NULL, + usage, + NULL +}; + + +int main(int argc, char *argv[]) +{ + int opt; + + libtu_init(argv[0]); + + optparser_init(argc, argv, OPTP_NO_DASH, opts, &tester3_cinfo); + + while((opt=optparser_get_opt())){ + switch(opt){ + case 'o': + printf("opt: %s\n", optparser_get_arg()); + break; + case 'f': + printf("file: %s\n", optparser_get_arg()); + break; + case 'v': + printf("view\n"); + break; + case 'z': + printf("zip\n"); + break; + case 'x': + printf("extract\n"); + break; + default: + optparser_print_error(); + return 1; + } + } + return 0; +} + diff --git a/libtu/tokenizer.c b/libtu/tokenizer.c new file mode 100644 index 00000000..cf929424 --- /dev/null +++ b/libtu/tokenizer.c @@ -0,0 +1,958 @@ +/* + * libtu/tokenizer.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tokenizer.h" +#include "misc.h" +#include "output.h" +#include "private.h" + + +static const char *errors[]={ + DUMMY_TR("(no error)"), + DUMMY_TR("Unexpected end of file"), /* E_TOKZ_UNEXPECTED_EOF */ + DUMMY_TR("Unexpected end of line"), /* E_TOKZ_UNEXPECTED_EOL */ + DUMMY_TR("End of line expected"), /* E_TOKZ_EOL_EXPECTED */ + DUMMY_TR("Invalid character"), /* E_TOKZ_INVALID_CHAR*/ + DUMMY_TR("Numeric constant too big"), /* E_TOKZ_TOOBIG */ + DUMMY_TR("Invalid numberic format"), /* E_TOKZ_NUMFMT */ + DUMMY_TR("Junk after numeric constant"), /* E_TOKZ_NUM_JUNK */ + DUMMY_TR("Not an integer"), /* E_TOKZ_NOTINT */ + DUMMY_TR("Numeric constant out of range"), /* E_TOKZ_RANGE */ + DUMMY_TR("Multi-character character constant"), /* E_TOKZ_MULTICHAR */ + DUMMY_TR("Token/statement limit reached"), /* E_TOKZ_TOKEN_LIMIT */ + DUMMY_TR("Unknown option"), /* E_TOKZ_UNKONWN_OPTION */ + DUMMY_TR("Syntax error"), /* E_TOKZ_SYNTAX */ + DUMMY_TR("Invalid argument"), /* E_TOKZ_INVALID_ARGUMENT */ + DUMMY_TR("End of statement expected"), /* E_TOKZ_EOS_EXPECTED */ + DUMMY_TR("Too few arguments"), /* E_TOKZ_TOO_FEW_ARGS */ + DUMMY_TR("Too many arguments"), /* E_TOKZ_TOO_MANY_ARGS */ + DUMMY_TR("Maximum section nestin level exceeded"), /* E_TOK_Z_MAX_NEST */ + DUMMY_TR("Identifier expected"), /* E_TOKZ_IDENTIFIER_EXPECTED */ + DUMMY_TR("Starting brace ('{') expected"), /* E_TOKZ_LBRACE_EXPECTED */ +}; + + +/* */ + +#define STRBLEN 32 + +#define STRING_DECL(X) int err=0; char* X=NULL; char X##_tmp[STRBLEN]; int X##_tmpl=0 +#define STRING_DECL_P(X, P) int err=0; char* X=NULL; char X##_tmp[STRBLEN]=P; int X##_tmpl=sizeof(P)-1 +#define STRING_APPEND(X, C) {if(!_string_append(&X, X##_tmp, &X##_tmpl, c)) err=-ENOMEM;} +#define STRING_FREE(X) if(X!=NULL) free(X) +#define STRING_FINISH(X) {if(err!=0) return err; if(!_string_finish(&X, X##_tmp, X##_tmpl)) err=-ENOMEM;} + + +static bool _string_append(char **p, char *tmp, int *tmplen, char c) +{ + char *tmp2; + + if(*tmplen==STRBLEN-1){ + tmp[STRBLEN-1]='\0'; + if(*p!=NULL){ + tmp2=scat(*p, tmp); + free(*p); + *p=tmp2; + }else{ + *p=scopy(tmp); + } + *tmplen=1; + tmp[0]=c; + return *p!=NULL; + }else{ + tmp[(*tmplen)++]=c; + return TRUE; + } +} + + +static bool _string_finish(char **p, char *tmp, int tmplen) +{ + char *tmp2; + + if(tmplen==0){ + if(*p==NULL) + *p=scopy(""); + }else{ + tmp[tmplen]='\0'; + if(*p!=NULL){ + tmp2=scat(*p, tmp); + free(*p); + *p=tmp2; + }else{ + *p=scopy(tmp); + } + } + return *p!=NULL; +} + + +/* */ + + +#define INC_LINE() tokz->line++ +#define GETCH() _getch(tokz) +#define UNGETCH(C) _ungetch(tokz, C) + +static int _getch(Tokenizer *tokz) +{ + int c; + + if(tokz->ungetc!=-1){ + c=tokz->ungetc; + tokz->ungetc=-1; + }else if (tokz->flags&TOKZ_READ_FROM_BUFFER) { + assert(tokz->buffer.data!=NULL); + if (tokz->buffer.pos==tokz->buffer.len) + c=EOF; + else + c=tokz->buffer.data[tokz->buffer.pos++]; + }else{ + c=getc(tokz->file); + } + + return c; +} + + +static void _ungetch(Tokenizer *tokz, int c) +{ + tokz->ungetc=c; +} + + +/* */ + + +static int scan_line_comment(Token *tok, Tokenizer *tokz) +{ + STRING_DECL_P(s, "#"); + int c; + + c=GETCH(); + + while(c!='\n' && c!=EOF){ + STRING_APPEND(s, c); + c=GETCH(); + } + + UNGETCH(c); + + STRING_FINISH(s); + + TOK_SET_COMMENT(tok, s); + + return 0; +} + + +static int skip_line_comment(Tokenizer *tokz) +{ + int c; + + do{ + c=GETCH(); + }while(c!='\n' && c!=EOF); + + UNGETCH(c); + + return 0; +} + + +/* */ + + +static int scan_c_comment(Token *tok, Tokenizer *tokz) +{ + STRING_DECL_P(s, "/*"); + int c; + int st=0; + + while(1){ + c=GETCH(); + + if(c==EOF){ + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOF; + } + + STRING_APPEND(s, c); + + if(c=='\n'){ + INC_LINE(); + }else if(st==0 && c=='*'){ + st=1; + }else if(st==1){ + if(c=='/') + break; + st=0; + } + } + + STRING_FINISH(s); + + TOK_SET_COMMENT(tok, s); + + return 0; +} + + +static int skip_c_comment(Tokenizer *tokz) +{ + int c; + int st=0; + + while(1){ + c=GETCH(); + + if(c==EOF) + return E_TOKZ_UNEXPECTED_EOF; + + if(c=='\n') + INC_LINE(); + else if(st==0 && c=='*') + st=1; + else if(st==1){ + if(c=='/') + break; + st=0; + } + } + + return 0; +} + + +/* */ + + +static int scan_char_escape(Tokenizer *tokz) +{ + static char* special_chars="nrtbae"; + static char* specials="\n\r\t\b\a\033"; + int base, max; + int i ,c; + + c=GETCH(); + + for(i=0;special_chars[i];i++){ + if(special_chars[i]==c) + return specials[c]; + } + + if(c=='x' || c=='X'){ + base=16;max=2;i=0; + }else if(c=='d' || c=='D'){ + base=10;max=3;i=0; + }else if(c=='8' || c=='9'){ + base=10;max=2;i=c-'0'; + }else if('0'<=c && c<='7'){ + base=8;max=2;i=c-'0'; + }else if(c=='\n'){ + UNGETCH(c); + return -2; + }else{ + return c; + } + + + while(--max>=0){ + c=GETCH(); + + if(c==EOF) + return EOF; + + if(c=='\n'){ + UNGETCH(c); + return -2; + } + + if(base==16){ + if(!isxdigit(c)) + break; + + i<<=4; + + if(isdigit(c)) + i+=c-'0'; + else if(i>='a') + i+=0xa+c-'a'; + else + i+=0xa+c-'a'; + + }else if(base==10){ + if(!isdigit(c)) + break; + i*=10; + i+=c-'0'; + }else{ + if(c<'0' || c>'7') + break; + i<<=3; + i+=c-'0'; + } + } + + if(max>=0) + UNGETCH(c); + + return i; +} + + +/* */ + + +static int scan_string(Token *tok, Tokenizer *tokz, bool escapes) +{ + STRING_DECL(s); + int c; + + while(1){ + c=GETCH(); + + if(c=='"') + break; + + if(c=='\n'){ + UNGETCH(c); + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOL; + } + + if(c=='\\' && escapes){ + c=scan_char_escape(tokz); + if(c==-2){ + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOL; + } + } + + if(c==EOF){ + STRING_FREE(s); + return E_TOKZ_UNEXPECTED_EOF; + } + + STRING_APPEND(s, c); + } + + STRING_FINISH(s); + + TOK_SET_STRING(tok, s); + + return 0; +} + + +/* */ + + +static int scan_char(Token *tok, Tokenizer *tokz) +{ + int c, c2; + + c=GETCH(); + + if(c==EOF) + return E_TOKZ_UNEXPECTED_EOF; + + if(c=='\n') + return E_TOKZ_UNEXPECTED_EOL; + + if(c=='\\'){ + c=scan_char_escape(tokz); + + if(c==EOF) + return E_TOKZ_UNEXPECTED_EOF; + + if(c==-2) + return E_TOKZ_UNEXPECTED_EOL; + } + + c2=GETCH(); + + if(c2!='\'') + return E_TOKZ_MULTICHAR; + + TOK_SET_CHAR(tok, c); + + return 0; +} + + +/* */ + + +#define START_IDENT(X) (isalpha(X) || X=='_' || X=='$') + + +static int scan_identifier(Token *tok, Tokenizer *tokz, int c) +{ + STRING_DECL(s); + + do{ + STRING_APPEND(s, c); + c=GETCH(); + }while(isalnum(c) || c=='_' || c=='$'); + + UNGETCH(c); + + STRING_FINISH(s); + + TOK_SET_IDENT(tok, s); + + return 0; +} + +#define NP_SIMPLE_IMPL +#include "np/numparser2.h" +#include "np/np-conv.h" + + +static int scan_number(Token *tok, Tokenizer *tokz, int c) +{ + NPNum num=NUM_INIT; + int e; + + if((e=parse_number(&num, tokz, c))) + return e; + + if(num.type==NPNUM_INT){ + long l; + if((e=num_to_long(&l, &num, TRUE))) + return e; + + TOK_SET_LONG(tok, l); + }else if(num.type==NPNUM_FLOAT){ + double d; + if((e=num_to_double(&d, &num))) + return e; + + TOK_SET_DOUBLE(tok, d); + }else{ + return E_TOKZ_NUMFMT; + } + + return 0; +} + + +/* */ + + +static uchar op_map[]={ + 0x00, /* ________ 0-7 */ + 0x00, /* ________ 8-15 */ + 0x00, /* ________ 16-23 */ + 0x00, /* ________ 24-31 */ + 0x62, /* _!___%&_ 32-39 */ + 0xff, /* ()*+,-./ 40-47 */ + 0x00, /* ________ 48-55 */ + 0xfc, /* __:;<=>? 56-63 */ + 0x01, /* @_______ 64-71 */ + 0x00, /* ________ 72-79 */ + 0x00, /* ________ 80-87 */ + 0x78, /* ___[_]^_ 88-95 */ + 0x00, /* ________ 96-103 */ + 0x00, /* ________ 104-111 */ + 0x00, /* ________ 112-119 */ + 0x38 /* ___{|}__ 120-127 */ +}; + + +static bool map_isset(uchar *map, uint ch) +{ + if(ch>127) + return FALSE; + + return map[ch>>3]&(1<<(ch&7)); +} + + +static bool is_opch(uint ch) +{ + return map_isset(op_map, ch); +} + + +static int scan_op(Token *tok, Tokenizer *tokz, int c) +{ + int c2; + int op=-1; + + /* Quickly check it is an operator character */ + if(!is_opch(c)) + return E_TOKZ_INVALID_CHAR; + + switch(c){ + case '+': + case '-': + case '*': +/* case '/': Checked elsewhere */ + case '%': + case '^': + case '!': + case '=': + case '<': + case '>': + c2=GETCH(); + if(c2=='='){ + op=c|(c2<<8); + }else if(c2==c && (c2!='%' && c2!='!' && c2!='*')){ + if(c=='<' || c=='>'){ + int c3=GETCH(); + if(c3=='='){ + op=c|(c2<<8)|(c3<<16); + }else{ + UNGETCH(c3); + op=c|(c2<<8); + } + }else{ + op=c|(c2<<8); + } + }else{ + UNGETCH(c2); + op=c; + } + break; + + /* It is already known that it is a operator so these are not needed + case ':': + case '~': + case '?': + case '.': + case ';'; + case '{': + case '}': + case '@': + case '|': + case '&': + */ + default: + op=c; + } + + TOK_SET_OP(tok, op); + + return 0; +} + + +/* */ + + +void tokz_warn(const Tokenizer *tokz, int line, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if(tokz!=NULL) + warn_obj_line_v(tokz->name, line, fmt, args); + else + warn(fmt, args); + + va_end(args); +} + + +void tokz_warn_error(const Tokenizer *tokz, int line, int e) +{ + if(e==E_TOKZ_UNEXPECTED_EOF) + line=0; + + if(e<0) + tokz_warn(tokz, line, "%s", strerror(-e)); + else + tokz_warn(tokz, line, "%s", TR(errors[e])); +} + + +bool tokz_get_token(Tokenizer *tokz, Token *tok) +{ + int c, c2, e; + + if (!(tokz->flags&TOKZ_READ_FROM_BUFFER)) + assert(tokz->file!=NULL); + + tok_free(tok); + + if(!TOK_IS_INVALID(&(tokz->ungettok))){ + *tok=tokz->ungettok; + tokz->ungettok.type=TOK_INVALID; + return TRUE; + } + + while(1){ + + e=0; + + do{ + c=GETCH(); + }while(c!='\n' && c!=EOF && isspace(c)); + + tok->line=tokz->line; + + switch(c){ + case EOF: + TOK_SET_OP(tok, OP_EOF); + return TRUE; + + case '\n': + INC_LINE(); + + if(tokz->flags&TOKZ_IGNORE_NEXTLINE) + continue; + + TOK_SET_OP(tok, OP_NEXTLINE); + + return TRUE; + + case '\\': + do{ + c=GETCH(); + if(c==EOF){ + TOK_SET_OP(tok, OP_EOF); + return FALSE; + } + if(!isspace(c) && e==0){ + e=E_TOKZ_EOL_EXPECTED; + tokz_warn_error(tokz, tokz->line, e); + if(!(tokz->flags&TOKZ_ERROR_TOLERANT)) + return FALSE; + } + }while(c!='\n'); + + INC_LINE(); + continue; + + case '#': + if(tokz->flags&TOKZ_READ_COMMENTS){ + e=scan_line_comment(tok, tokz); + break; + }else if((e=skip_line_comment(tokz))){ + break; + } + + continue; + + case '/': + c2=GETCH(); + + if(c2=='='){ + TOK_SET_OP(tok, OP_AS_DIV); + return TRUE; + } + + if(c2!='*'){ + UNGETCH(c2); + TOK_SET_OP(tok, OP_DIV); + return TRUE; + } + + if(tokz->flags&TOKZ_READ_COMMENTS){ + e=scan_c_comment(tok, tokz); + break; + }else if((e=skip_c_comment(tokz))){ + break; + } + + continue; + + case '\"': + e=scan_string(tok, tokz, TRUE); + break; + + case '\'': + e=scan_char(tok, tokz); + break; + + default: + if(('0'<=c && c<='9') || c=='-' || c=='+'){ + e=scan_number(tok, tokz, c); + break; + } + + if(START_IDENT(c)) + e=scan_identifier(tok, tokz, c); + else + e=scan_op(tok, tokz, c); + } + + if(!e) + return TRUE; + + tokz_warn_error(tokz, tokz->line, e); + return FALSE; + } +} + + +void tokz_unget_token(Tokenizer *tokz, Token *tok) +{ + tok_free(&(tokz->ungettok)); + tokz->ungettok=*tok; + tok->type=TOK_INVALID; +} + + +/* + * File open + */ + +static bool do_tokz_pushf(Tokenizer *tokz) +{ + Tokenizer_FInfo *finfo; + + finfo=REALLOC_N(tokz->filestack, Tokenizer_FInfo, + tokz->filestack_n, tokz->filestack_n+1); + + if(finfo==NULL) + return FALSE; + + tokz->filestack=finfo; + finfo=&(finfo[tokz->filestack_n++]); + + finfo->file=tokz->file; + finfo->name=tokz->name; + finfo->line=tokz->line; + finfo->ungetc=tokz->ungetc; + finfo->ungettok=tokz->ungettok; + + return TRUE; +} + + +bool tokz_pushf_file(Tokenizer *tokz, FILE *file, const char *fname) +{ + char *fname_copy=NULL; + + if(file==NULL) + return FALSE; + + if(fname!=NULL){ + fname_copy=scopy(fname); + if(fname_copy==NULL){ + warn_err(); + return FALSE; + } + } + + if(tokz->file!=NULL){ + if(!do_tokz_pushf(tokz)){ + warn_err(); + if(fname_copy!=NULL) + free(fname_copy); + return FALSE; + } + } + + tokz->file=file; + tokz->name=fname_copy; + tokz->line=1; + tokz->ungetc=-1; + tokz->ungettok.type=TOK_INVALID; + + return TRUE; +} + + +bool tokz_pushf(Tokenizer *tokz, const char *fname) +{ + FILE *file; + + file=fopen(fname, "r"); + + if(file==NULL){ + warn_err_obj(fname); + return FALSE; + } + + if(!tokz_pushf_file(tokz, file, fname)){ + fclose(file); + return FALSE; + } + + return TRUE; +} + + + +static Tokenizer *tokz_create() +{ + Tokenizer*tokz; + + tokz=ALLOC(Tokenizer); + + if(tokz==NULL){ + warn_err(); + return NULL; + } + + tokz->file=NULL; + tokz->name=NULL; + tokz->line=1; + tokz->ungetc=-1; + tokz->ungettok.type=TOK_INVALID; + tokz->flags=0; + tokz->optstack=NULL; + tokz->nest_lvl=0; + tokz->filestack_n=0; + tokz->filestack=NULL; + tokz->buffer.data=0; + tokz->buffer.len=0; + tokz->buffer.pos=0; + + return tokz; +} + + +Tokenizer *tokz_open(const char *fname) +{ + Tokenizer *tokz; + + tokz=tokz_create(); + + if(!tokz_pushf(tokz, fname)){ + free(tokz); + return NULL; + } + + return tokz; +} + + +Tokenizer *tokz_open_file(FILE *file, const char *fname) +{ + Tokenizer *tokz; + + tokz=tokz_create(); + + if(!tokz_pushf_file(tokz, file, fname)){ + free(tokz); + return NULL; + } + + return tokz; +} + +Tokenizer *tokz_prepare_buffer(char *buffer, int len) +{ + Tokenizer *tokz; + char old=0; + + tokz=tokz_create(); + if(len>0){ + old=buffer[len-1]; + buffer[len-1]='\0'; + } + + tokz->flags|=TOKZ_READ_FROM_BUFFER; + tokz->buffer.data=scopy(buffer); + tokz->buffer.len=(len>0 ? (uint)len : strlen(tokz->buffer.data)); + tokz->buffer.pos=0; + + if(old>0) + buffer[len-1]=old; + + return tokz; +} + +/* + * File close + */ + +static bool do_tokz_popf(Tokenizer *tokz, bool shrink) +{ + Tokenizer_FInfo *finfo; + + if(tokz->filestack_n<=0) + return FALSE; + + if(tokz->file!=NULL) + fclose(tokz->file); + if(tokz->name!=NULL) + free(tokz->name); + + finfo=&(tokz->filestack[--tokz->filestack_n]); + + tokz->file=finfo->file; + tokz->name=finfo->name; + tokz->line=finfo->line; + tokz->ungetc=finfo->ungetc; + tokz->ungettok=finfo->ungettok; + + if(tokz->filestack_n==0){ + free(tokz->filestack); + tokz->filestack=NULL; + }else if(shrink){ + finfo=REALLOC_N(tokz->filestack, Tokenizer_FInfo, + tokz->filestack_n+1, tokz->filestack_n); + if(finfo==NULL) + warn_err(); + else + tokz->filestack=finfo; + } + + return TRUE; +} + + +bool tokz_popf(Tokenizer *tokz) +{ + return do_tokz_popf(tokz, TRUE); +} + + +void tokz_close(Tokenizer *tokz) +{ + while(tokz->filestack_n>0) + do_tokz_popf(tokz, FALSE); + + if(tokz->file!=NULL) + fclose(tokz->file); + if(tokz->name!=NULL) + free(tokz->name); + tok_free(&(tokz->ungettok)); + + free(tokz); +} + + + +/* */ + + +void tok_free(Token *tok) +{ + if(TOK_IS_STRING(tok) || TOK_IS_IDENT(tok) || TOK_IS_COMMENT(tok)){ + if(TOK_STRING_VAL(tok)!=NULL) + free(TOK_STRING_VAL(tok)); + } + + tok->type=TOK_INVALID; +} + + +void tok_init(Token *tok) +{ + static Token dummy=TOK_INIT; + + memcpy(tok, &dummy, sizeof(*tok)); +} + diff --git a/libtu/tokenizer.h b/libtu/tokenizer.h new file mode 100644 index 00000000..034a4a7e --- /dev/null +++ b/libtu/tokenizer.h @@ -0,0 +1,212 @@ +/* + * libtu/tokenizer.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_TOKENIZER_H +#define LIBTU_TOKENIZER_H + +#include +#include "types.h" + + +#define TOK_SET_BOOL(TOK, VAL) {(TOK)->type=TOK_BOOL; (TOK)->u.bval=VAL;} +#define TOK_SET_LONG(TOK, VAL) {(TOK)->type=TOK_LONG; (TOK)->u.lval=VAL;} +#define TOK_SET_DOUBLE(TOK, VAL) {(TOK)->type=TOK_DOUBLE; (TOK)->u.dval=VAL;} +#define TOK_SET_CHAR(TOK, VAL) {(TOK)->type=TOK_CHAR; (TOK)->u.cval=VAL;} +#define TOK_SET_STRING(TOK, VAL) {(TOK)->type=TOK_STRING; (TOK)->u.sval=VAL;} +#define TOK_SET_IDENT(TOK, VAL) {(TOK)->type=TOK_IDENT; (TOK)->u.sval=VAL;} +#define TOK_SET_COMMENT(TOK, VAL) {(TOK)->type=TOK_COMMENT; (TOK)->u.sval=VAL;} +#define TOK_SET_OP(TOK, VAL) {(TOK)->type=TOK_OP; (TOK)->u.opval=VAL;} + +#define TOK_TYPE(TOK) ((TOK)->type) +#define TOK_BOOL_VAL(TOK) ((TOK)->u.bval) +#define TOK_LONG_VAL(TOK) ((TOK)->u.lval) +#define TOK_DOUBLE_VAL(TOK) ((TOK)->u.dval) +#define TOK_CHAR_VAL(TOK) ((TOK)->u.cval) +#define TOK_STRING_VAL(TOK) ((TOK)->u.sval) +#define TOK_IDENT_VAL(TOK) ((TOK)->u.sval) +#define TOK_COMMENT_VAL(TOK) ((TOK)->u.sval) +#define TOK_OP_VAL(TOK) ((TOK)->u.opval) + +#define TOK_IS_INVALID(TOK) ((TOK)->type==TOK_INVALID) +#define TOK_IS_BOOL(TOK) ((TOK)->type==TOK_BOOL) +#define TOK_IS_LONG(TOK) ((TOK)->type==TOK_LONG) +#define TOK_IS_DOUBLE(TOK) ((TOK)->type==TOK_DOUBLE) +#define TOK_IS_CHAR(TOK) ((TOK)->type==TOK_CHAR) +#define TOK_IS_STRING(TOK) ((TOK)->type==TOK_STRING) +#define TOK_IS_IDENT(TOK) ((TOK)->type==TOK_IDENT) +#define TOK_IS_COMMENT(TOK) ((TOK)->type==TOK_COMMENT) +#define TOK_IS_OP(TOK) ((TOK)->type==TOK_OP) + +#define TOK_OP_IS(TOK, OP) ((TOK)->type==TOK_OP && (TOK)->u.opval==(OP)) + +#define TOK_TAKE_STRING_VAL(TOK) ((TOK)->type=TOK_INVALID, (TOK)->u.sval) +#define TOK_TAKE_IDENT_VAL(TOK) ((TOK)->type=TOK_INVALID, (TOK)->u.sval) +#define TOK_TAKE_COMMENT_VAL(TOK) ((TOK)->type=TOK_INVALID, (TOK)->u.sval) + + +enum{ + TOK_INVALID=0, + TOK_LONG='l', + TOK_DOUBLE='d', + TOK_CHAR='c', + TOK_STRING='s', + TOK_IDENT='i', + TOK_BOOL='b', + TOK_COMMENT='#', + TOK_OP='+' +}; + + +enum{ +#define OP2(X,Y) ((X)|((Y)<<8)) +#define OP3(X,Y,Z) ((X)|((Y)<<8)|((Z)<<16)) + + OP_L_PAR= '(', OP_R_PAR= ')', OP_L_BRK= '[', OP_R_BRK= ']', + OP_L_BRC= '{', OP_R_BRC= '}', OP_COMMA= ',', OP_SCOLON= ';', + + OP_PLUS= '+', OP_MINUS= '-', OP_MUL= '*', OP_DIV= '/', + OP_MOD= '%', OP_POW= '^', OP_OR= '|', OP_AND= '&', + /*OP_NOT= '~',*/ OP_NOT= '!', OP_ASGN= '=', OP_LT= '<', + OP_GT= '>', OP_DOT= '.', OP_COLON= ':', OP_QMARK= '?', + OP_AT= '@', + OP_NEXTLINE='\n',OP_EOF= -1, + + OP_INC= OP2('+','+'), OP_DEC= OP2('-','-'), + OP_LSHIFT= OP2('<','<'), OP_RSHIFT= OP2('>','>'), + OP_AS_INC= OP2('+','='), OP_AS_DEC= OP2('-','='), + OP_AS_MUL= OP2('*','='), OP_AS_DIV= OP2('/','='), + OP_AS_MOD= OP2('%','='), OP_AS_POW= OP2('^','='), + +/* AS_OR= OP2('|','='), AS_AND= OP2('&','='), */ + OP_EQ= OP2('=','='), OP_NE= OP2('!','='), + OP_LE= OP2('<','='), OP_GE= OP2('>','=') + +/* L_AND= OP2('&','&'), L_OR= OP2('|','|'), + L_XOR= OP2('^','^'), */ + +/* AsLShift= OP3('<','<','='), + AsRShift= OP3('>','>','='), */ + +#undef OP2 +#undef OP3 +}; + + +typedef struct{ + int type; + int line; + union{ + bool bval; + long lval; + double dval; + char cval; + char *sval; + int opval; + } u; +} Token; + +#define TOK_INIT {0, 0, {0}} + + +extern void tok_free(Token*tok); +extern void tok_init(Token*tok); + + +/* */ + + +enum{ + TOKZ_IGNORE_NEXTLINE=0x1, + TOKZ_READ_COMMENTS=0x2, + TOKZ_PARSER_INDENT_MODE=0x04, + TOKZ_ERROR_TOLERANT=0x8, + TOKZ_READ_FROM_BUFFER=0x10, + TOKZ_DEFAULT_OPTION=0x20 +}; + + +enum{ + E_TOKZ_UNEXPECTED_EOF=1, + E_TOKZ_UNEXPECTED_EOL, + E_TOKZ_EOL_EXPECTED, + E_TOKZ_INVALID_CHAR, + E_TOKZ_TOOBIG, + E_TOKZ_NUMFMT, + E_TOKZ_NUM_JUNK, + E_TOKZ_NOTINT, + E_TOKZ_RANGE, + E_TOKZ_MULTICHAR, + + E_TOKZ_TOKEN_LIMIT, + E_TOKZ_UNKNOWN_OPTION, + E_TOKZ_SYNTAX, + E_TOKZ_INVALID_ARGUMENT, + E_TOKZ_EOS_EXPECTED, + E_TOKZ_TOO_FEW_ARGS, + E_TOKZ_TOO_MANY_ARGS, + E_TOKZ_MAX_NEST, + E_TOKZ_IDENTIFIER_EXPECTED, + + E_TOKZ_LBRACE_EXPECTED +}; + + +struct _ConfOpt; + +typedef struct _Tokenizer_FInfo{ + FILE *file; + char *name; + int line; + int ungetc; + Token ungettok; +} Tokenizer_FInfo; + +typedef struct _Tokenizer_Buffer{ + char *data; + int len; + int pos; +} Tokenizer_Buffer; + +typedef struct _Tokenizer{ + FILE *file; + char *name; + int line; + int ungetc; + Token ungettok; + + Tokenizer_Buffer buffer; + + int flags; + const struct _ConfOpt **optstack; + int nest_lvl; + void *user_data; + + int filestack_n; + Tokenizer_FInfo *filestack; + + char **includepaths; +} Tokenizer; + + +extern Tokenizer *tokz_open(const char *fname); +extern Tokenizer *tokz_open_file(FILE *file, const char *fname); +extern Tokenizer *tokz_prepare_buffer(char *buffer, int len); +extern void tokz_close(Tokenizer *tokz); +extern bool tokz_get_token(Tokenizer *tokz, Token *tok); +extern void tokz_unget_token(Tokenizer *tokz, Token *tok); +extern void tokz_warn_error(const Tokenizer *tokz, int line, int e); +extern void tokz_warn(const Tokenizer *tokz, int line, const char *fmt, ...); + +extern bool tokz_pushf(Tokenizer *tokz, const char *fname); +extern bool tokz_pushf_file(Tokenizer *tokz, FILE *file, const char *fname); +extern bool tokz_popf(Tokenizer *tokz); + +extern void tokz_set_includepaths(Tokenizer *tokz, char **paths); + +#endif /* LIBTU_TOKENIZER_H */ diff --git a/libtu/types.h b/libtu/types.h new file mode 100644 index 00000000..b2bd6a79 --- /dev/null +++ b/libtu/types.h @@ -0,0 +1,86 @@ +/* + * libtu/types.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_TYPES_H +#define LIBTU_TYPES_H + +#include + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifndef LIBTU_TYPEDEF_UXXX + + /* All systems seem to define these whichever way they want to + * despite -D_*_SOURCE etc. so there is no easy way to know whether + * they can be typedef'd or not. Unless you want to go through using + * autoconf or similar methods. ==> Just stick to #define. :-( + */ + +#ifndef uchar +#define uchar unsigned char +#endif + +#ifndef ushort +#define ushort unsigned short +#endif + +#ifndef uint +#define uint unsigned int +#endif + +#ifndef ulong +#define ulong unsigned long +#endif + +#else /* LIBTU_TYPEDEF_UXXX */ + +#ifndef uchar +typedef unsigned char uchar; +#endif + +#ifndef ushort +typedef unsigned short ushort; +#endif + +#ifndef uint +typedef unsigned int uint; +#endif + +#ifndef ulong +typedef unsigned long ulong; +#endif + +#endif /* LIBTU_TYPEDEF_UXXX */ + + +#ifndef LIBTU_TYPEDEF_BOOL + +#ifndef bool +#define bool int +#endif + +#else /* LIBTU_TYPEDEF_BOOL */ + +#ifndef bool +typedef int bool; +#endif + +#endif /* LIBTU_TYPEDEF_BOOL */ + +#endif /* LIBTU_TYPES_H */ diff --git a/libtu/util.c b/libtu/util.c new file mode 100644 index 00000000..e4168710 --- /dev/null +++ b/libtu/util.c @@ -0,0 +1,45 @@ +/* + * libtu/util.c + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#include +#include +#include +#include + +#include "locale.h" +#include "util.h" +#include "misc.h" + + +static const char *progname=NULL; + + +void libtu_init(const char *argv0) +{ + progname=argv0; + +#ifndef CF_NO_GETTEXT + textdomain(simple_basename(argv0)); +#endif +} + + +const char *libtu_progname() +{ + return progname; +} + + +const char *libtu_progbasename() +{ + const char *s=strrchr(progname, '/'); + + return (s==NULL ? progname : s+1); +} + diff --git a/libtu/util.h b/libtu/util.h new file mode 100644 index 00000000..00e63fa3 --- /dev/null +++ b/libtu/util.h @@ -0,0 +1,39 @@ +/* + * libtu/util.h + * + * Copyright (c) Tuomo Valkonen 1999-2002. + * + * You may distribute and modify this library under the terms of either + * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. + */ + +#ifndef LIBTU_UTIL_H +#define LIBTU_UTIL_H + +#include +#include +#include + +#include "types.h" +#include "optparser.h" + +/** + * @parame argv0 The program name used to invoke the current program, with + * path (if specified). Unfortunately it is generally not easy to determine + * the encoding of this string, so we don't require a specific one here. + * + * @see http://stackoverflow.com/questions/5408730/what-is-the-encoding-of-argv + */ +extern void libtu_init(const char *argv0); +/** + * The program name used to invoke the current program, with path (if + * supplied). Unfortunately the encoding is undefined. + */ +extern const char *libtu_progname(); +/** + * The program name used to invoke the current program, without path. + * Unfortunately the encoding is undefined. + */ +extern const char *libtu_progbasename(); + +#endif /* LIBTU_UTIL_H */ -- 2.11.4.GIT