diff options
Diffstat (limited to 'lib')
100 files changed, 8161 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..88e5b35 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,44 @@ +SUBDIRS = cli++ fdbuf mystring +noinst_LIBRARIES = libmisc.a libnullmailer.a +noinst_HEADERS = list.h +EXTRA_DIST = make_defines.sh listtest.cc mergelib.sh +CLEANFILES = defines.cc + +libmisc_a_SOURCES = \ + ac/dirent.h ac/time.h ac/wait.h \ + address.h address.cc \ + argparse.h argparse.cc \ + autoclose.h \ + base64.h base64.cc \ + canonicalize.h canonicalize.cc \ + configio.h config_path.cc \ + config_read.cc config_readlist.cc config_readint.cc config_syserr.cc \ + connect.h tcpconnect.cc \ + defines.h \ + errcodes.h errcodes.cc \ + hostname.h hostname.cc \ + itoa.h itoa.cc \ + makefield.cc makefield.h \ + netstring.h netstring.cc \ + forkexec.cc forkexec.h \ + selfpipe.cc selfpipe.h \ + setenv.cc setenv.h +nodist_libmisc_a_SOURCES = defines.cc + +libnullmailer_a_SOURCES = +libnullmailer.a: mergelib.sh libmisc.a fdbuf/libfdbuf.a \ + mystring/libmystring.a Makefile + $(RM) -f libnullmailer.a + sh $(srcdir)/mergelib.sh libnullmailer.a \ + libmisc.a \ + fdbuf/libfdbuf.a \ + mystring/libmystring.a + +defines.cc: Makefile make_defines.sh + @echo Creating defines.cc + @sh $(srcdir)/make_defines.sh \ + @localstatedir@/spool/nullmailer \ + @sysconfdir@/nullmailer \ + @libexecdir@/nullmailer \ + @bindir@ \ + @sbindir@ diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..50c8159 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,738 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = lib +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libmisc_a_AR = $(AR) $(ARFLAGS) +libmisc_a_LIBADD = +am_libmisc_a_OBJECTS = address.$(OBJEXT) argparse.$(OBJEXT) \ + base64.$(OBJEXT) canonicalize.$(OBJEXT) config_path.$(OBJEXT) \ + config_read.$(OBJEXT) config_readlist.$(OBJEXT) \ + config_readint.$(OBJEXT) config_syserr.$(OBJEXT) \ + tcpconnect.$(OBJEXT) errcodes.$(OBJEXT) hostname.$(OBJEXT) \ + itoa.$(OBJEXT) makefield.$(OBJEXT) netstring.$(OBJEXT) \ + forkexec.$(OBJEXT) selfpipe.$(OBJEXT) setenv.$(OBJEXT) +nodist_libmisc_a_OBJECTS = defines.$(OBJEXT) +libmisc_a_OBJECTS = $(am_libmisc_a_OBJECTS) \ + $(nodist_libmisc_a_OBJECTS) +libnullmailer_a_AR = $(AR) $(ARFLAGS) +libnullmailer_a_LIBADD = +am_libnullmailer_a_OBJECTS = +libnullmailer_a_OBJECTS = $(am_libnullmailer_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libmisc_a_SOURCES) $(nodist_libmisc_a_SOURCES) \ + $(libnullmailer_a_SOURCES) +DIST_SOURCES = $(libmisc_a_SOURCES) $(libnullmailer_a_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +HAVE_GETADDRINFO = @HAVE_GETADDRINFO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RM = @RM@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = cli++ fdbuf mystring +noinst_LIBRARIES = libmisc.a libnullmailer.a +noinst_HEADERS = list.h +EXTRA_DIST = make_defines.sh listtest.cc mergelib.sh +CLEANFILES = defines.cc +libmisc_a_SOURCES = \ + ac/dirent.h ac/time.h ac/wait.h \ + address.h address.cc \ + argparse.h argparse.cc \ + autoclose.h \ + base64.h base64.cc \ + canonicalize.h canonicalize.cc \ + configio.h config_path.cc \ + config_read.cc config_readlist.cc config_readint.cc config_syserr.cc \ + connect.h tcpconnect.cc \ + defines.h \ + errcodes.h errcodes.cc \ + hostname.h hostname.cc \ + itoa.h itoa.cc \ + makefield.cc makefield.h \ + netstring.h netstring.cc \ + forkexec.cc forkexec.h \ + selfpipe.cc selfpipe.h \ + setenv.cc setenv.h + +nodist_libmisc_a_SOURCES = defines.cc +libnullmailer_a_SOURCES = +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libmisc.a: $(libmisc_a_OBJECTS) $(libmisc_a_DEPENDENCIES) $(EXTRA_libmisc_a_DEPENDENCIES) + $(AM_V_at)-rm -f libmisc.a + $(AM_V_AR)$(libmisc_a_AR) libmisc.a $(libmisc_a_OBJECTS) $(libmisc_a_LIBADD) + $(AM_V_at)$(RANLIB) libmisc.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/address.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/argparse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/canonicalize.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_path.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_read.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_readint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_readlist.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_syserr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defines.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errcodes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/forkexec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/itoa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makefield.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netstring.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selfpipe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setenv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpconnect.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-noinstLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + +libnullmailer.a: mergelib.sh libmisc.a fdbuf/libfdbuf.a \ + mystring/libmystring.a Makefile + $(RM) -f libnullmailer.a + sh $(srcdir)/mergelib.sh libnullmailer.a \ + libmisc.a \ + fdbuf/libfdbuf.a \ + mystring/libmystring.a + +defines.cc: Makefile make_defines.sh + @echo Creating defines.cc + @sh $(srcdir)/make_defines.sh \ + @localstatedir@/spool/nullmailer \ + @sysconfdir@/nullmailer \ + @libexecdir@/nullmailer \ + @bindir@ \ + @sbindir@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/ac/dirent.h b/lib/ac/dirent.h new file mode 100644 index 0000000..5b0f191 --- /dev/null +++ b/lib/ac/dirent.h @@ -0,0 +1,17 @@ +#include <sys/types.h> +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif diff --git a/lib/ac/time.h b/lib/ac/time.h new file mode 100644 index 0000000..10d1136 --- /dev/null +++ b/lib/ac/time.h @@ -0,0 +1,10 @@ +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif diff --git a/lib/ac/wait.h b/lib/ac/wait.h new file mode 100644 index 0000000..665ced5 --- /dev/null +++ b/lib/ac/wait.h @@ -0,0 +1,10 @@ +#include <sys/types.h> +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif diff --git a/lib/address.cc b/lib/address.cc new file mode 100644 index 0000000..6aea5b7 --- /dev/null +++ b/lib/address.cc @@ -0,0 +1,672 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include <ctype.h> +#include "canonicalize.h" +#include "mystring/mystring.h" +#include "list.h" + +#define LSQBRACKET '[' +#define RSQBRACKET ']' +#define QUOTE '"' +#define CR '\n' +#define LPAREN '(' +#define RPAREN ')' + +enum node_type { + EMPTY = 0, + // Full tokens, with string content: + ATOM = 'A', + QUOTED_STRING = 'Q', + DOMAIN_LITERAL = 'D', + COMMENT = 'C', + // Special characters, no content: + LABRACKET = '<', + RABRACKET = '>', + AT = '@', + COMMA = ',', + SEMICOLON = ';', + COLON = ':', + ESCAPE = '\\', + PERIOD = '.', + // End of tokens + EOT = '$', +}; + +struct token +{ + const node_type type; + const bool has_ws; + const mystring str; + + token(node_type); + token(node_type, bool, mystring); +}; + +token::token(node_type t) + : type(t), has_ws(false) +{ +} + +token::token(node_type t, bool w, mystring s) + : type(t), has_ws(w), str(s) +{ +} + +struct anode : public token +{ + anode* next; + anode(node_type, const char*, const char*, const char*); + anode(node_type, bool, mystring); +}; + +anode::anode(node_type t, + const char* wstart, + const char* start, + const char* end) + : token(t, start > wstart, mystring(start, end-start)), next(0) +{ +} + +anode::anode(node_type t, bool w, mystring s) + : token(t, w, s), next(0) +{ +} + +struct result +{ + anode* next; + bool good; + mystring str; + mystring comment; + mystring addr; + + result(); + result(const result&); + result(anode*); + result(anode*, const mystring&, const mystring&, const mystring&); + bool operator!() const + { + return !good; + } + operator bool() const + { + return good; + } +}; + +result::result() + : next(0), good(0) +{ +} + +result::result(anode* n) + : next(n), good(1) +{ +} + +result::result(anode* n, const mystring& s, + const mystring& c, const mystring& l) + : next(n), good(1), str(s), comment(c), addr(l) +{ +} + +result::result(const result& r) + : next(r.next), good(r.good), str(r.str), comment(r.comment), addr(r.addr) +{ +} + +#ifndef TRACE +#define ENTER(R) +#define FAIL(MSG) return result() +#define RETURNR(R) return R +#define RETURN(N,S,C,L) return result(N,S,C,L) +#else +#include "fdbuf/fdbuf.h" +static const char indentstr[] = " "; +static const char* indent = indentstr + sizeof indentstr - 1; +#define ENTER(R) do{ fout << indent-- << __FUNCTION__ << ": \"" << node->str << "\": " << R << endl; }while(0) +#define FAIL(MSG) do{ fout << ++indent << __FUNCTION__ << ": failed: " << MSG << endl; return result(); }while(0) +#define RETURNR(R) do{ fout << ++indent << __FUNCTION__ << ": succeeded str=" << R.str << " comment=" << R.comment << " addr=" << R.addr << endl; return (R); }while(0) +#define RETURN(N,S,C,L) do{ result _result(N,S,C,L); RETURNR(_result); }while(0) +#endif + +#define RULE(X) static result match_##X(anode* node) +#define MATCHTOKEN(X) do{ if(node->type != X) FAIL("node is not type " #X); else node = node->next; }while(0) +#define MATCHRULE(V,R) result V = match_##R(node); if(!V) FAIL("did not match " #R); +#define OR_RULE(ALT1,ALT2) { result r=match_##ALT1(node); if(r) RETURNR(r); }{ result r=match_##ALT2(node); if(r) RETURNR(r); } FAIL("did not match " #ALT1 " OR " #ALT2); + +static bool issymbol(char c) +{ + switch(c) { + case LPAREN: case RPAREN: + case LABRACKET: case RABRACKET: + case LSQBRACKET: case RSQBRACKET: + case AT: case COMMA: + case SEMICOLON: case COLON: + case ESCAPE: case QUOTE: case PERIOD: + return true; + default: + return false; + } +} + +static bool isctl(char c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +static bool isqtext(char c) +{ + return c && c != QUOTE && c != ESCAPE; +} + +static bool isdtext(char c) +{ + return c && c != LSQBRACKET && c != RSQBRACKET && + c != ESCAPE && c != CR; +} + +// quoted-pair = ESCAPE CHAR +static bool isqpair(const char* ptr) +{ + return *ptr && *ptr == ESCAPE && + *(ptr+1); +} + +static bool isatom(char ch) +{ + return !(isspace(ch) || issymbol(ch) || isctl(ch)); +} + +static anode* tokenize_atom(const char* wstart, const char* &ptr) +{ + if(!isatom(*ptr)) return 0; + const char* start = ptr; + do { + ++ptr; + } while(isatom(*ptr)); + return new anode(ATOM, wstart, start, ptr); +} + +static anode* tokenize_comment(const char* wstart, const char* &ptr) +{ + if(*ptr != LPAREN) return 0; + unsigned count = 0; + const char* start = ptr; + char ch = *ptr; + while(ch) { + if(isqpair(ptr)) + ++ptr; + else if(ch == LPAREN) + ++count; + else if(ch == RPAREN) { + --count; + if(!count) + return new anode(COMMENT, wstart, start, ++ptr); + } + else if(ch == CR) + return 0; // ERROR + ++ptr; + ch = *ptr; + } + return 0; // ERROR +} + +static anode* tokenize_domain_literal(const char* wstart, const char* &ptr) +{ + if(*ptr != LSQBRACKET) return 0; + const char* start = ptr; + ++ptr; + while(isspace(*ptr)) ++ptr; + for(; *ptr; ++ptr) { + if(isdtext(*ptr)) + continue; + else if(isqpair(ptr)) + ++ptr; + else + break; + } + while(isspace(*ptr)) ++ptr; + if(*ptr != RSQBRACKET) + return 0; // ERROR + return new anode(DOMAIN_LITERAL, wstart, start, ptr); +} + +static anode* tokenize_quoted_string(const char* wstart, const char* &ptr) +{ + if(*ptr != QUOTE) return 0; + const char* start = ptr; + for(++ptr; *ptr; ++ptr) { + if(isqtext(*ptr)) + continue; + else if(isqpair(ptr)) + ++ptr; + else + break; + } + if(*ptr != QUOTE) return 0; + ++ptr; + return new anode(QUOTED_STRING, wstart, start, ptr); +} + +static anode* tokenize(const char* &ptr) +{ + const char* wstart = ptr; + while(isspace(*ptr)) ++ptr; + char ch = *ptr; + switch(ch) { + case 0: + return new anode(EOT, wstart, ptr, ptr); + case LABRACKET: + case RABRACKET: + case AT: + case COMMA: + case SEMICOLON: + case COLON: + case ESCAPE: + case PERIOD: + ++ptr; + return new anode((node_type)ch, wstart, ptr-1, ptr); + case LPAREN: + return tokenize_comment(wstart, ptr); + case LSQBRACKET: + return tokenize_domain_literal(wstart, ptr); + case QUOTE: + return tokenize_quoted_string(wstart, ptr); + default: + return tokenize_atom(wstart, ptr); + } +} + +anode* tokenize(const mystring str) +{ + const char* ptr = str.c_str(); + anode* head = new anode(EMPTY, ptr, ptr, ptr); + anode* tail = head; + anode* tmp; + while((tmp = tokenize(ptr)) != 0) { + tail = tail->next = tmp; + if(tmp->type == EOT) { + tail = head->next; + delete head; + return tail; + } + } + return 0; +} + +static mystring quote(const mystring& in) +{ + unsigned length = in.length(); + // The result will never be more than double the length of the input plus 2 + char out[length*2 + 2 + 1]; + char* ptrout = out; + const char* ptrin = in.c_str(); + bool quoted = false; + for(; length; ++ptrin, ++ptrout, --length) { + if(*ptrin == QUOTE || *ptrin == ESCAPE) + *ptrout++ = ESCAPE; + if(issymbol(*ptrin)) + quoted = true; + *ptrout = *ptrin; + } + *ptrout = 0; + if(quoted) + return mystringjoin("\"") + out + "\""; + else + return in; +} + +static mystring unquote(const mystring& in) +{ + unsigned length = in.length(); + // The result will never be more than the length of the input + char out[length+1]; + bool modified = false; + const char* ptrin = in.c_str(); + char* ptrout = out; + if(in[0] == QUOTE && in[length-1] == QUOTE) { + length -= 2; + ptrin++; + modified = true; + } + // Skip leading whitespace before copying to out + for(; length > 0 && isspace(*ptrin); ++ptrin, --length, modified = true) + ; + for(; length; ++ptrin, ++ptrout, --length) { + if(isqpair(ptrin)) { + ++ptrin; + --length; + modified = true; + } + *ptrout = *ptrin; + } + // Skip trailing whitespace copied into out + for(; ptrout > out && isspace(ptrout[-1]); --ptrout, modified = true) + ; + *ptrout = 0; + if(modified) + return out; + else + return in; +} + +anode* skipcomment(anode* node, mystring& comment) +{ + while(node->type == COMMENT) { + comment = comment + " " + node->str; + node = node->next; + } + return node; +} + +RULE(sub_domain) +{ + // Note atom <= domain-ref + ENTER("atom / domain-literal"); + mystring comment; + node = skipcomment(node, comment); + if(node->type == ATOM || node->type == DOMAIN_LITERAL) + RETURN(node->next, node->str, comment, node->str); + FAIL("did not match ATOM or DOMAIN-LITERAL"); +} + +RULE(domain) +{ + ENTER("sub-domain *(PERIOD sub-domain) [PERIOD]"); + MATCHRULE(r, sub_domain); + if(!r) FAIL("did not match sub-domain"); + mystring comment; + for(;;) { + node = r.next = skipcomment(r.next, comment); + if(node->type != PERIOD) + break; + r.str += PERIOD; + r.addr += PERIOD; + node = node->next; + result r1 = match_sub_domain(node); + if(r1) { + r.next = r1.next; + r.str += r1.str; + comment += r1.comment; + r.addr += r1.addr; + } + else { + r.next = node; + node = r.next = skipcomment(r.next, comment); + } + } + r.comment += comment; + RETURNR(r); +} + +RULE(route) +{ + ENTER("1#(AT domain) COLON"); + unsigned count=0; + mystring str; + mystring comment; + for(;;) { + if(node->type != AT) break; + node = node->next; + MATCHRULE(r, domain); + str += AT; + str += r.str; + comment += r.comment; + ++count; + node = r.next; + } + if(count == 0) + FAIL("matched no domains"); + node = skipcomment(node, comment); + MATCHTOKEN(COLON); + RETURN(node, str, comment, ""); +} + +RULE(word) +{ + ENTER("atom / quoted-string"); + mystring comment; + node = skipcomment(node, comment); + if(node->type == ATOM) + RETURN(node->next, node->str, comment, node->str); + else if(node->type == QUOTED_STRING) { + mystring addr = unquote(node->str); + RETURN(node->next, quote(addr), comment, addr); + } + FAIL("did not match ATOM or QUOTED-STRING"); +} + +RULE(local_part) +{ + ENTER("word *(PERIOD word)"); + MATCHRULE(r, word); + for(;;) { + node = r.next = skipcomment(r.next, r.comment); + if(node->type != PERIOD) + break; + node = node->next; + result r1 = match_word(node); + if(!r1) + break; + r.next = r1.next; + r.str += PERIOD; + r.str += r1.str; + r.comment += r1.comment; + r.addr += PERIOD; + r.addr += r1.addr; + } + RETURNR(r); +} + +RULE(addr_spec) +{ + ENTER("local-part *( AT domain )"); + MATCHRULE(r, local_part); + mystring domain; + for(;;) { + node = r.next = skipcomment(r.next, r.comment); + if(node->type != AT) + break; + node = node->next; + result r2 = match_domain(node); + if(!r2) break; + if(!!domain) { + r.str += AT; + r.str += domain; + r.addr += AT; + r.addr += domain; + } + domain = r2.addr; + r.comment += r2.comment; + r.next = r2.next; + } + canonicalize(domain); + RETURN(r.next, r.str + "@" + domain, r.comment, + r.addr + "@" + domain + "\n"); +} + +RULE(route_addr) +{ + ENTER("LABRACKET [route] addr-spec RABRACKET"); + mystring comment; + node = skipcomment(node, comment); + MATCHTOKEN(LABRACKET); + result r1 = match_route(node); + if(r1) node = r1.next; + comment += r1.comment; + MATCHRULE(r2, addr_spec); + node = r2.next; + comment += r2.comment; + node = skipcomment(node, comment); + MATCHTOKEN(RABRACKET); + RETURN(node, "<" + r2.str + ">" + comment, "", r2.addr); +} + +RULE(phrase) +{ + ENTER("word *(word / PERIOD / CFWS)"); + MATCHRULE(r1, word); + for(;;) { + if(r1.next->type == PERIOD) { + if (r1.next->has_ws) + r1.str += ' '; + r1.str += PERIOD; + r1.next = r1.next->next; + } + else { + result r2 = match_word(r1.next); + if(!r2) + break; + if (r1.next->has_ws) + r1.str += ' '; + r1.str += r2.str; + r1.comment += r2.comment; + r1.next = r2.next; + } + } + RETURNR(r1); +} + +RULE(route_spec) +{ + ENTER("[phrase] route-addr"); + result r1 = match_phrase(node); + if(r1) + node = r1.next; + MATCHRULE(r2, route_addr); + if(!r1) + RETURNR(r2); + r2.str = r1.str + r1.comment + " " + r2.str + r2.comment; + RETURNR(r2); +} + +RULE(mailbox) +{ + ENTER("route-spec / addr-spec"); + OR_RULE(route_spec, addr_spec); +} + +RULE(mailboxes) +{ + ENTER("mailbox *(*(COMMA) mailbox)"); + MATCHRULE(r1, mailbox); + r1.str += r1.comment; + r1.comment = ""; + for(;;) { + node = r1.next; + for(;;) { + node = skipcomment(node, r1.str); + if(node->type == COMMA) node = node->next; + else break; + } + if(node->type == EOT) + break; + result r2 = match_mailbox(node); + if(!r2) break; + r1.next = r2.next; + r1.str = r1.str + ", " + r2.str + r2.comment; + r1.addr += r2.addr; + } + node = skipcomment(node, r1.str); + r1.next = node; + RETURNR(r1); +} + +RULE(group) +{ + ENTER("phrase COLON [#mailboxes] SEMICOLON"); + MATCHRULE(r1, phrase); + node = r1.next; + MATCHTOKEN(COLON); + result r2 = match_mailboxes(node); + if(r2) node = r2.next; + mystring comment; + node = skipcomment(node, comment); + MATCHTOKEN(SEMICOLON); + RETURN(node, r1.str + ": " + r2.str + r2.comment + comment + ";", + "", r2.addr); +} + +RULE(address) +{ + ENTER("group / mailbox"); + OR_RULE(group, mailbox); +} + +RULE(addresses) +{ + ENTER("[address *(*(COMMA) address)] EOF"); + + // Special-case handling for empty address lists + if(node->type == EOT) RETURN(0, "", "", ""); + if(node->type == COMMENT && node->next->type == EOT) + RETURN(0, node->str, "", ""); + + MATCHRULE(r1, address); + r1.str += r1.comment; + r1.comment = ""; + for(;;) { + node = r1.next; + for(;;) { + node = skipcomment(node, r1.str); + if(node->type == COMMA) node = node->next; + else break; + } + if(node->type == EOT) + break; + result r2 = match_address(node); + if(!r2) break; + r1.next = r2.next; + r1.str = r1.str + ", " + r2.str + r2.comment; + r1.addr += r2.addr; + } + node = skipcomment(node, r1.str); + if(node->next) FAIL("Rule ended before EOF"); + RETURNR(r1); +} + +static void del_tokens(anode* node) +{ + while(node) { + anode* tmp = node->next; + delete node; + node = tmp; + } +} + +bool parse_addresses(mystring& line, mystring& list) +{ + anode* tokenlist = tokenize(line.c_str()); + if(!tokenlist) + return false; + result r = match_addresses(tokenlist); + del_tokens(tokenlist); + if(r) { + line = r.str; + list = r.addr; + return true; + } + else + return false; +} diff --git a/lib/address.h b/lib/address.h new file mode 100644 index 0000000..ca0f93a --- /dev/null +++ b/lib/address.h @@ -0,0 +1,8 @@ +#ifndef NULLMAILER__ADDRESS__H__ +#define NULLMAILER__ADDRESS__H__ + +#include "mystring/mystring.h" + +bool parse_addresses(mystring& line, mystring& list); + +#endif // NULLMAILER__ADDRESS__H__ diff --git a/lib/argparse.cc b/lib/argparse.cc new file mode 100644 index 0000000..0a231e1 --- /dev/null +++ b/lib/argparse.cc @@ -0,0 +1,61 @@ +#include <ctype.h> +#include "argparse.h" + +static const char* parse_arg(mystring& arg, const char* start, const char* end) +{ + const char* ptr; + for (ptr = start; ptr < end && ! isspace(*ptr); ++ptr) { + switch (*ptr) { + case '\'': + arg.append(start, ptr - start); + for (start = ++ptr; ptr < end && *ptr != '\''; ++ptr) ; + arg.append(start, ptr - start); + start = ptr + 1; + continue; + case '"': + arg.append(start, ptr - start); + for (start = ++ptr; ptr < end && *ptr != '\"'; ptr++) { + if (*ptr == '\\') { + arg.append(start, ptr - start); + if (++ptr < end) + arg.append(ptr, 1); + start = ++ptr; + } + } + arg.append(start, ptr - start); + start = ptr + 1; + continue; + case '\\': + arg.append(start, ptr - start); + if (++ptr < end) + arg.append(ptr, 1); + start = ++ptr; + continue; + } + } + if ((ptr - start) > 0) + arg.append(start, ptr - start); + return ptr; +} + +unsigned parse_args(arglist& lst, const mystring& str) +{ + lst.empty(); + const char* ptr = str.c_str(); + const char* end = ptr + str.length(); + unsigned count = 0; + while (ptr < end) { + // Skip any leading spaces + if (isspace(*ptr)) + ++ptr; + else { + mystring s; + ptr = parse_arg(s, ptr, end); + if (ptr == 0) + break; + lst.append(s); + ++count; + } + } + return count; +} diff --git a/lib/argparse.h b/lib/argparse.h new file mode 100644 index 0000000..589342d --- /dev/null +++ b/lib/argparse.h @@ -0,0 +1,10 @@ +#ifndef NULLMAILER__ARGPARSE__H +#define NULLMAILER__ARGPARSE__H + +#include "mystring/mystring.h" +#include "list.h" + +typedef list<mystring> arglist; +unsigned parse_args(arglist&, const mystring& str); + +#endif diff --git a/lib/autoclose.h b/lib/autoclose.h new file mode 100644 index 0000000..8bd38fd --- /dev/null +++ b/lib/autoclose.h @@ -0,0 +1,68 @@ +#ifndef NULLMAILER_AUTOCLOSE__H__ +#define NULLMAILER_AUTOCLOSE__H__ + +#include <unistd.h> + +// Simple inline wrapper to automatically close an open file descriptor +class autoclose +{ + private: + int fd; + + public: + inline autoclose(int f = -1) : fd(f) { } + inline ~autoclose() { close(); } + inline operator int() const { return fd; } + inline int operator =(int f) + { + close(); + return fd = f; + } + inline void close() + { + if (fd >= 0) { + ::close(fd); + fd = -1; + } + } +}; + +// Simple inline wrapper to handle opening and closing a pipe pair +class autoclose_pipe +{ + private: + int fds[2]; + + public: + inline autoclose_pipe() + { + fds[0] = fds[1] = -1; + } + inline ~autoclose_pipe() + { + close(); + } + inline int operator[](int i) const { return fds[i]; } + inline bool open() + { + return pipe(fds) == 0; + } + inline void close() + { + if (fds[0] >= 0) { + ::close(fds[0]); + ::close(fds[1]); + fds[0] = fds[1] = -1; + } + } + // Close one half of the pair, return the other, and mark both as if they were closed. + inline int extract(int which) + { + int result = fds[which]; + ::close(fds[1-which]); + fds[0] = fds[1] = -1; + return result; + } +}; + +#endif // NULLMAILER_AUTOCLOSE__H__ diff --git a/lib/base64.cc b/lib/base64.cc new file mode 100644 index 0000000..2492526 --- /dev/null +++ b/lib/base64.cc @@ -0,0 +1,63 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "base64.h" + +static char basis[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void base64_encode(const mystring& in, mystring& out) +{ + size_t length; + const unsigned char* ptr; + char buf[4]; + for (length = in.length(), ptr = (const unsigned char*)in.c_str(); + length >= 3; + length -= 3, ptr += 3) { + base64_encode_chunk(ptr, 3, buf); + out.append(buf, 4); + } + if (length > 0) { + base64_encode_chunk(ptr, length, buf); + out.append(buf, 4); + } +} + +void base64_encode_chunk(const unsigned char bin[3], unsigned len, + char encoded[4]) +{ + encoded[0] = basis[bin[0] >> 2]; + switch(len) { + case 1: + encoded[1] = basis[(bin[0] << 4) & 0x3f]; + encoded[2] = encoded[3] = '='; + break; + case 2: + encoded[1] = basis[(bin[0] << 4 | bin[1] >> 4) & 0x3f]; + encoded[2] = basis[(bin[1] << 2) & 0x3f]; + encoded[3] = '='; + break; + case 3: + encoded[1] = basis[(bin[0] << 4 | bin[1] >> 4) & 0x3f]; + encoded[2] = basis[(bin[1] << 2 | bin[2] >> 6) & 0x3f]; + encoded[3] = basis[bin[2] & 0x3f]; + } +} diff --git a/lib/base64.h b/lib/base64.h new file mode 100644 index 0000000..0f5e498 --- /dev/null +++ b/lib/base64.h @@ -0,0 +1,10 @@ +#ifndef NULLMAILER_BASE64__H__ +#define NULLMAILER_BASE64__H__ + +#include "mystring/mystring.h" + +extern void base64_encode(const mystring& in, mystring& out); +extern void base64_encode_chunk(const unsigned char bin[3], unsigned len, + char encoded[4]); + +#endif // NULLMAILER_BASE64__H__ diff --git a/lib/canonicalize.cc b/lib/canonicalize.cc new file mode 100644 index 0000000..c716f8d --- /dev/null +++ b/lib/canonicalize.cc @@ -0,0 +1,38 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include "mystring/mystring.h" +#include "canonicalize.h" +#include "hostname.h" + +void canonicalize(mystring& domain) +{ + if(!domain) + domain = defaulthost; + if(domain != "localhost" && domain.find_first('.') < 0) { + if(!!defaultdomain) { + if (!!domain) domain += "."; + domain += defaultdomain; + } + } +} + diff --git a/lib/canonicalize.h b/lib/canonicalize.h new file mode 100644 index 0000000..f046aec --- /dev/null +++ b/lib/canonicalize.h @@ -0,0 +1,7 @@ +#ifndef NULLMAILER__CANONICALIZE__H__ +#define NULLMAILER__CANONICALIZE__H__ + +#include "mystring/mystring.h" +void canonicalize(mystring& domain); + +#endif // NULLMAILER__CANONICALIZE__H__ diff --git a/lib/cli++/ChangeLog b/lib/cli++/ChangeLog new file mode 100644 index 0000000..7cd88de --- /dev/null +++ b/lib/cli++/ChangeLog @@ -0,0 +1,123 @@ +2001-03-07 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * Renamed cli.h to cli++.h + +2000-10-25 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * cli2pod.pl (parse_options): Ignore {0,} as well as {0}. + +2000-08-15 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * cli2pod.pl: Replaced the CLI documentation programs with this + script which outputs POD, which can be translated to man pages or + HTML (or LaTeX, or text, or FM). + +2000-08-14 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * cli_parse.pl (parse_header_line): Rewrote the parsing to deal + with multi-line strings. + + * cli2man.pl (synopsis): Add usage string. + + * cli2html.pl (synopsis): Add usage string. + +2000-08-12 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * cli_parse.pl, cli2html.pl, cli2man.pl: Created these programs. + +2000-08-01 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * main.cc (show_option): Fixed several width glitches. + +2000-07-18 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * cli.h (struct cli_option): Added new uinteger type. + + * main.cc (fill): Removed use of mystring. + +2000-07-13 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * Removed include of mystring. + +2000-01-09 Bruce Guenter <bruceg@daedalus.bfsmedia.com> + + * main.cc (parse_short): Modified the logic here to treat a string + value immediately following a string option as the value for that + option rather than as more flags. This makes it behave much more + like the standard getopt library. + +1999-09-30 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (show_option): Changed stringlist option string from + "=LIST" to "=ITEM". + +1999-09-29 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (cli_option::set): Fixed problem with setting a string + list option. + +1999-09-11 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (show_help): Split off two parts of this routine into + calc_max_width and show_option. + (show_option): Add "=INT" for integer options, and don't add extra + space for non-value long options. + (set): Use strtol instead of atoi to parse the integer string, to + allow for error checking. + (show_option): Fixed handling of string lists. + +1999-08-14 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (set_argv0): Sets argv0 to the complete value of + argv[0], argv0dir to the part of argv[0] up to and including the + last '/' (or blank if there is none), and argv0base to the + remainder of argv[0]. This is for use in programs that determine + what to do based on the value of the program name. + +1999-07-14 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (parse_long_eq): Fixed to account for "counter" flag + type. + (parse_long_noeq): Fixed to account for "counter" flag type. + (parse_long_eq): set() will return one, but this shouldn't return + one, so subtract one from its result. + (show_help): Added a mechanism to display default values on a + second line. + (show_help): Output a blank line before the "--help" option line. + +1999-07-04 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (show_help): Only show a "=VALUE" for string and integer + option types. + +1999-06-30 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * messages.cc (cli_error): Moved this routine into a separate + module, and added a "cli_warning" routine. + +1999-06-25 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc (set): Added handling for two new option types: string + list and counters. A stringlist maintains a linked list of all + the given option arguments. A counter adds the flag_value to the + dataptr each time it is encountered. + (parse_short): Fixed faulty logic regarding options with values. + Need to merge parts into cli_option::set(). + +1999-06-24 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * main.cc: Rewrote the "help" option handling to stop it being a + special case, by making an internal option list that includes a + "help" option at the end of it. + (show_help): Cleaned up the option formatting to produce more + correct output. + (build_options): Removed the need to count the options. Note + that this breaks compatibility with previous versions. + (cli_error): Added this convenience function for the CLI program + to report errors and optionally exit. + (set): Added functionality to call functions when an option is + parsed, and moved some of the option parsing into class methods + from the structure. + (main): Moved the test for showing the usage information before + the test for counting command-line arguments. + diff --git a/lib/cli++/Makefile.am b/lib/cli++/Makefile.am new file mode 100644 index 0000000..7cb7392 --- /dev/null +++ b/lib/cli++/Makefile.am @@ -0,0 +1,9 @@ +noinst_LIBRARIES = libcli++.a +EXTRA_DIST = clitest.cc cli++topod.pl + +AM_CPPFLAGS = -I$(top_srcdir)/lib +#LIBS = @LIBS@ -L. -lcli++ -L../lib -lvmailmgr + +libcli___a_SOURCES = cli++.h main.cc messages.cc only_long.cc +#clitest_SOURCES = clitest.cc + diff --git a/lib/cli++/Makefile.in b/lib/cli++/Makefile.in new file mode 100644 index 0000000..a133e95 --- /dev/null +++ b/lib/cli++/Makefile.in @@ -0,0 +1,554 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = lib/cli++ +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libcli___a_AR = $(AR) $(ARFLAGS) +libcli___a_LIBADD = +am_libcli___a_OBJECTS = main.$(OBJEXT) messages.$(OBJEXT) \ + only_long.$(OBJEXT) +libcli___a_OBJECTS = $(am_libcli___a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcli___a_SOURCES) +DIST_SOURCES = $(libcli___a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + ChangeLog +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +HAVE_GETADDRINFO = @HAVE_GETADDRINFO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RM = @RM@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libcli++.a +EXTRA_DIST = clitest.cc cli++topod.pl +AM_CPPFLAGS = -I$(top_srcdir)/lib +#LIBS = @LIBS@ -L. -lcli++ -L../lib -lvmailmgr +libcli___a_SOURCES = cli++.h main.cc messages.cc only_long.cc +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/cli++/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/cli++/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libcli++.a: $(libcli___a_OBJECTS) $(libcli___a_DEPENDENCIES) $(EXTRA_libcli___a_DEPENDENCIES) + $(AM_V_at)-rm -f libcli++.a + $(AM_V_AR)$(libcli___a_AR) libcli++.a $(libcli___a_OBJECTS) $(libcli___a_LIBADD) + $(AM_V_at)$(RANLIB) libcli++.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/messages.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/only_long.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + +#clitest_SOURCES = clitest.cc + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/cli++/cli++.h b/lib/cli++/cli++.h new file mode 100644 index 0000000..3dfeb14 --- /dev/null +++ b/lib/cli++/cli++.h @@ -0,0 +1,69 @@ +#ifndef VMAILMGR__CLIPP__CLIPP__H__ +#define VMAILMGR__CLIPP__CLIPP__H__ + +typedef bool (*cli_funcptr)(void*); + +struct cli_stringlist +{ + const char* string; + cli_stringlist* next; + + cli_stringlist(const char* s) + : string(s), next(0) + { + } +}; + +struct cli_option +{ + char ch; + const char* name; + enum { flag, counter, integer, string, stringlist, ulong } type; + int flag_value; + void* dataptr; + const char* helpstr; + const char* defaultstr; + + int set(const char* arg); + int parse_long_eq(const char* arg, int as_short); + int parse_long_noeq(const char* arg, int as_short); +}; + +#define CLI_OPTION_END {0, 0, cli_option::flag, 0, 0, 0, 0} + +/* The following are required from the CLI program */ +extern const char* cli_program; +extern const char* cli_help_prefix; +extern const char* cli_help_suffix; +extern const char* cli_args_usage; +extern const int cli_args_min; +extern const int cli_args_max; +extern cli_option cli_options[]; +extern const bool cli_only_long; +extern int cli_main(int argc, char* argv[]); + +/* The following are provided to the CLI program */ +extern const char* argv0; +extern const char* argv0base; +extern const char* argv0dir; +extern void usage(int exit_value, const char* errorstr = 0); +extern int cli_parse_args(int argc, char* argv[]); + +extern void cli_error(int exit_value, + const char*, + const char* = 0, + const char* = 0, + const char* = 0); + +extern void cli_syserror(int exit_value, + const char*, + const char* = 0, + const char* = 0, + const char* = 0); + +extern void cli_warning(const char*, + const char* = 0, + const char* = 0, + const char* = 0); + +#endif diff --git a/lib/cli++/cli++topod.pl b/lib/cli++/cli++topod.pl new file mode 100644 index 0000000..5d2ef13 --- /dev/null +++ b/lib/cli++/cli++topod.pl @@ -0,0 +1,213 @@ +#!/usr/bin/perl + +sub cstr2pod { + local($_) = shift; + s/\\"/"/go; + s/"([^\"]*)"/"C<$1>"/go; + $_; +} + +$section = 1; + +@section_order = ( + 'NAME', + 'SYNOPSIS', + 'DESCRIPTION', + 'OPTIONS', + 'RETURN VALUE', + 'ERRORS', + 'EXAMPLES', + 'ENVIRONMENT', + 'FILES', + 'SEE ALSO', + 'NOTES', + 'CAVEATS', + 'WARNINGS', + 'DIAGNOSTICS', + 'BUGS', + 'RESTRICTIONS', + 'AUTHOR', + 'AUTHORS', + 'HISTORY' + ); + +sub type2word { + my($type) = shift; + return 'INT' if $type eq 'integer'; + return 'UINT' if $type eq 'ulong'; + return 'STR' if $type eq 'string' || $type eq 'stringlist'; + return '' if $type eq 'flag' || $type eq 'counter'; + die "Invalid cli option type '$type'"; +} + +sub add_option { + my($short, $long, $type, $desc) = @_; + + my $s = '[B<'; + my $o = '=item B<'; + if($short) { + $s .= "-$short"; + $o .= "-$short"; + if($type) { + $s .= " $type"; + $o .= " $type"; + } + } + if($short && $long) { + $s .= ">]\n[B<"; + $o .= ">, B<"; + } + if($long) { + $s .= "--$long"; + $o .= "--$long"; + if($type) { + $s .= "=$type"; + $o .= "=$type"; + } + } + $s .= ">]\n"; + $o .= ">\n\n$desc\n\n"; + + $synopsis .= $s; + $options = "=over 8\n\n" unless $options; + $options .= $o; +} + +sub parse_option { + local($_) = shift; + s/^\s*\{\s*//o; + s/\s*\},?\s*/ /o; + + my $short = $1 if s/^'([^\'])',\s*//o; + die "Invalid cli option" unless $short || s/^0,\s*//o; + + my $long = $1 if s/^"([^\"]+)",\s*//o; + die "Invalid cli_option" unless $long || s/^0,\s*//o; + + my $type = $1 if s/^cli_option::(\S+),\s*//o; + die "Invalid cli_option" unless $type; + $type = &type2word($type); + + my $val = $1 if s/^([^,]+),\s*//o; + my $var = $1 if s/^&([^,]+),\s*//o; + + my $desc = cstr2pod($1) if s/^"([^,]+)",\s*//o; + die "Invalid cli_option" unless $desc; + $desc =~ s/\.?$/./o if $desc; + + my $default = $1 if s/^"([^\"]+)"\s+//o; + die "Invalid cli_option" unless $default || s/^0\s+//o; + $desc .= " Defaults to $default." if $default; + + s/\s*\/\/\s*/ /go; + s/^\s*//o; + + add_option($short, $long, $type, $_ || $desc); +} + +sub parse_options { + $synopsis = "B<$program>\n"; + + my $line; + while(<>) { + s/^\s+//o; + s/\s+$//o; + if($line && /^\{/o) { + &parse_option($line); + $line = ""; + } + next if /^\{\s*0\s*\},?/o; + next if /^\{\s*0\s*,\s*\},?/o; + last if /^\s*\};/o; + $line =~ s/$/ $_/; + } + &parse_option($line) if $line; + + $synopsis .= "I<$usage>" if $usage; + $options .= "=back" if $options; + $sections{'SYNOPSIS'} = $synopsis; + $sections{'OPTIONS'} = $options; +} + +sub parse_notes { + my $section; + my $title; + while(<>) { + chomp; + last unless /^$/o || s/^\/\/\s*//o; + if(/^[\sA-Z]+$/o) { + $sections{$title} = $section if $title && $section; + undef $section; + $title = $_; + } else { + $section .= "$_\n"; + } + } + $sections{$title} = $section if $title && $section; +} + +sub parse_header_line { + local($_, $comment) = @_; + if(s/^\s*const\s+char\s*\*\s*cli_(\S+)\s*=\s*//o) { + my $name = $1; + s/;\s*$//o; + s/^\"//; + s/\"$//o; + s/\\n$//o; + s/\\n""/\n/go; + $program = $_ if $name eq 'program'; + $prefix = $_ if $name eq 'help_prefix'; + $usage = $_ if $name eq 'args_usage'; + $suffix = $_ if $name eq 'help_suffix'; + } +} + +sub parse_header { + my $comment = ''; + my $line = ''; + while(<>) { + s/^\s+//o; + s/\s+$//o; + if(s/^.*Copyright\s*\(C\)\s*[\d,]+\s*//o) { + $author = $_; + } else { + last if ($program && $prefix && /^$/o); + next if /^#/o; + $comment .= "$1\n" if s|\s*//\s*(.*)$||o; + $line =~ s/$/\n$_/; + if(/;$/o) { + &parse_header_line($line, $comment); + undef $line; + undef $comment; + } + } + } +} + +sub parse_description { + while(<>) { + s/^\s+//o; + s/\s+$//o; + last if / cli_options\[\]\s*=\s*\{/o; + next unless s/^\/\/\s*//o; + $description .= "$_\n"; + } +} + +&parse_header; +&parse_description; +&parse_options; +&parse_notes; + +$description .= "\n\n$suffix\n" if $suffix; + +$sections{'NAME'} = "$program - $prefix"; +$sections{'DESCRIPTION'} = $description; +$sections{'AUTHORS'} = $author if $author; + +foreach $section (@section_order) { + print "=head1 $section\n\n$sections{$section}\n\n" + if $sections{$section}; +} + +1; diff --git a/lib/cli++/clitest.cc b/lib/cli++/clitest.cc new file mode 100644 index 0000000..97f703d --- /dev/null +++ b/lib/cli++/clitest.cc @@ -0,0 +1,47 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +#include <config.h> +#include "cli++.h" +#include "fdbuf/fdbuf.h" + +const char* cli_program = "clitest"; +const char* cli_help_prefix = "Does nothing but set flags\n"; +const char* cli_help_suffix = ""; +const char* cli_args_usage = ""; +const int cli_args_min = 0; +const int cli_args_max = -1; +int o_flag = 0; +int o_int = 0; +char* o_string = "nostring"; +cli_option cli_options[] = { + { 'f', "flag", cli_option::flag, 1, &o_flag, "Sets a flag", 0 }, + { 'i', "int", cli_option::integer, 0, &o_int, "Sets an integer", 0 }, + { 's', "str", cli_option::string, 0, &o_string, "Sets a string", 0}, + {0} }; + +int cli_main(int argc, char* argv[]) +{ + fout << "argv0=" << argv0 << endl + << " argv0dir=" << argv0dir << endl + << " argv0base=" << argv0base << endl; + fout << "The flag is set to " << o_flag << endl; + fout << "The integer is set to " << o_int << endl; + fout << "The string is set to " << o_string << endl; + for(int i = 0; i < argc; i++) + fout << "argv[" << i << "] = '" << argv[i] << "'\n"; + return 0; +} diff --git a/lib/cli++/main.cc b/lib/cli++/main.cc new file mode 100644 index 0000000..83c26e2 --- /dev/null +++ b/lib/cli++/main.cc @@ -0,0 +1,377 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +#include <config.h> +#include "ac/time.h" +#include "fdbuf/fdbuf.h" +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include "cli++.h" + +#ifndef HAVE_SRANDOM +void srandom(unsigned int seed); +#endif + +static bool do_show_usage = false; +const char* argv0; +const char* argv0base; +const char* argv0dir; + +static cli_option help_option = { 'h', "help", cli_option::flag, + true, &do_show_usage, + "Display this help and exit", 0 }; + +static cli_option** options; +static unsigned optionc; +static const char* short_options; + +static void build_options() +{ + for(optionc = 0; + cli_options[optionc].ch || cli_options[optionc].name; + optionc++) ; + optionc++; + options = new cli_option*[optionc]; + for(unsigned i = 0; i < optionc-1; i++) + options[i] = &cli_options[i]; + options[optionc-1] = &help_option; + + char* so; + short_options = so = new char[optionc+1]; + for (unsigned i = 0; i < optionc; i++) + if (options[i]->ch != 0) + *so++ = options[i]->ch; + *so = 0; +} + +static inline int max(int a, int b) +{ + return (a>b) ? a : b; +} + +static const char* fill(int i) +{ + static int lastlen = 0; + static char* buf = 0; + if(i > lastlen) { + delete[] buf; + buf = new char[i+1]; + lastlen = i; + } + memset(buf, ' ', i); + buf[i] = 0; + return buf; +} + +static void show_usage() +{ + fout << "usage: " << cli_program << " [flags] " << cli_args_usage << endl; +} + +static int calc_width(const cli_option* o) +{ + int width = (o->ch || !cli_only_long) ? 4 : 2; + if (o->name) { + width += strlen(o->name) + 2 + !cli_only_long; + switch (o->type) { + case cli_option::string: width += 6; break; + case cli_option::integer: width += 4; break; + case cli_option::ulong: width += 4; break; + case cli_option::stringlist: width += 5; break; + case cli_option::flag: break; + case cli_option::counter: break; + } + } + return width; +} + +static int calc_max_width() +{ + // maxwidth is the maximum width of the option text prefix + int maxwidth = 0; + for(unsigned i = 0; i < optionc; i++) + maxwidth = max(maxwidth, calc_width(options[i])); + return maxwidth; +} + +static void show_option(const cli_option* o, int maxwidth) +{ + if(o == &help_option) + fout << '\n'; + fout << " "; + if(o->ch) + fout << '-' << o->ch; + else if (!cli_only_long) + fout << " "; + if (o->ch || !cli_only_long) + fout << (o->ch && o->name ? ", " : " "); + if(o->name) { + const char* extra = ""; + switch(o->type) { + case cli_option::string: extra = "=VALUE"; break; + case cli_option::integer: extra = "=INT"; break; + case cli_option::ulong: extra = "=UNS"; break; + case cli_option::stringlist: extra = "=ITEM"; break; + case cli_option::flag: break; + case cli_option::counter: break; + } + fout << (cli_only_long ? "-" : "--") << o->name << extra + << fill(maxwidth - strlen(o->name) - strlen(extra) - !cli_only_long + - (o->ch || !cli_only_long ? 4 : 0)); + } + else + fout << fill(maxwidth-3); + fout << o->helpstr << '\n'; + if(o->defaultstr) + fout << fill(maxwidth+3) << "(Defaults to " << o->defaultstr << ")\n"; +} + +static void show_help() +{ + if(cli_help_prefix) + fout << cli_help_prefix; + int maxwidth = calc_max_width(); + for(unsigned i = 0; i < optionc; i++) + show_option(options[i], maxwidth); + if(cli_help_suffix) + fout << cli_help_suffix; +} + +void usage(int exit_value, const char* errorstr) +{ + if(errorstr) + ferr << cli_program << ": " << errorstr << endl; + show_usage(); + show_help(); + exit(exit_value); +} + +cli_stringlist* stringlist_append(cli_stringlist* node, const char* newstr) +{ + cli_stringlist* newnode = new cli_stringlist(newstr); + if(node) { + cli_stringlist* head = node; + while(node->next) + node = node->next; + node->next = newnode; + return head; + } + else + return newnode; +} + +int cli_option::set(const char* arg) +{ + char* endptr; + switch(type) { + case flag: + *(int*)dataptr = flag_value; + return 0; + case counter: + *(int*)dataptr += flag_value; + return 0; + case integer: + { + long longresult = strtol(arg, &endptr, 10); + if(*endptr || longresult < INT_MIN || longresult > INT_MAX) { + ferr << argv0 << ": invalid integer: " << arg << endl; + return -1; + } + *(int*)dataptr = (int) longresult; + return 1; + } + case ulong: + *(unsigned long*)dataptr = strtoul(arg, &endptr, 10); + if(*endptr) { + ferr << argv0 << ": invalid unsigned integer: " << arg << endl; + return -1; + } + return 1; + case stringlist: + *(cli_stringlist**)dataptr = + stringlist_append(*(cli_stringlist**)dataptr, arg); + return 1; + default: // string + *(const char**)dataptr = arg; + return 1; + } +} + +static int parse_short(int argc, char* argv[]) +{ + int end = strlen(argv[0]) - 1; + for(int i = 1; i <= end; i++) { + int ch = argv[0][i]; + unsigned j; + for(j = 0; j < optionc; j++) { + cli_option* o = options[j]; + if(o->ch == ch) { + if(o->type != cli_option::flag && + o->type != cli_option::counter) { + if(i < end) { + if(o->set(argv[0]+i+1) != -1) + return 0; + } + else if(argc <= 1) { + ferr << argv0 << ": option -" << o->ch + << " requires a value." << endl; + } + else + if(o->set(argv[1]) != -1) + return 1; + } + else if(o->set(0) != -1) + break; + return -1; + } + } + if(j >= optionc) { + ferr << argv0 << ": unknown option letter -" << argv[0][i] << endl; + return -1; + } + } + return 0; +} + +static void option_error(const cli_option* o, int as_short, const char* text) +{ + ferr << argv0 << ": option "; + if (as_short) + ferr << '-' << o->ch; + else + ferr << (cli_only_long ? "-" : "--") << o->name; + ferr << text << endl; +} + +int cli_option::parse_long_eq(const char* arg, int as_short) +{ + if(type == flag || type == counter) { + option_error(this, as_short, " does not take a value."); + return -1; + } + else + return set(arg)-1; +} + +int cli_option::parse_long_noeq(const char* arg, int as_short) +{ + if(type == flag || type == counter) + return set(0); + else if(arg) + return set(arg); + else { + option_error(this, as_short, " requires a value."); + return -1; + } +} + +static int parse_long(int, char* argv[]) +{ + const char* arg = argv[0]+1; + // Handle both short and long args + if (arg[0] == '-') + ++arg; + for(unsigned j = 0; j < optionc; j++) { + cli_option* o = options[j]; + if(o->name) { + size_t len = strlen(o->name); + if(!memcmp(arg, o->name, len)) { + if(arg[len] == '\0') + return o->parse_long_noeq(argv[1], false); + else if(arg[len] == '=') + return o->parse_long_eq(arg+len+1, false); + } + } + } + ferr << argv0 << ": unknown option string: '" << argv[0] << "'" << endl; + return -1; +} + +static int parse_either(int argc, char* argv[]) +{ + return (strchr(short_options, argv[0][1]) != 0) + ? parse_short(argc, argv) + : parse_long(argc, argv); +} + +int cli_parse_args(int argc, char* argv[]) +{ + int i; + for(i = 1; i < argc; i++) { + const char* arg = argv[i]; + // Stop at the first non-option argument + if(arg[0] != '-') + break; + // Stop after the first "-" or "--" + if(arg[1] == '\0' || + (arg[1] == '-' && arg[2] == '\0')) { + i++; + break; + } + int j = (arg[1] == '-') ? parse_long(argc-i, argv+i) + : cli_only_long ? parse_either(argc-i, argv+i) + : parse_short(argc-i, argv+i); + if(j < 0) + usage(1); + else + i += j; + } + return i; +} + +static void set_argv0(const char* p) +{ + argv0 = p; + static const char* empty = ""; + const char* s = strrchr(p, '/'); + if(s) { + ++s; + argv0base = s; + size_t length = s-p; + char* tmp = new char[length+1]; + memcpy(tmp, p, length); + tmp[length] = 0; + argv0dir = tmp; + } + else { + argv0base = p; + argv0dir = empty; + } +} + +int main(int argc, char* argv[]) +{ + struct timeval tv; + gettimeofday(&tv, 0); + srandom(tv.tv_usec ^ tv.tv_sec); + + set_argv0(argv[0]); + build_options(); + int lastarg = cli_parse_args(argc, argv); + + if(do_show_usage) + usage(0); + + argc -= lastarg; + argv += lastarg; + if(argc < cli_args_min) + usage(1, "Too few command-line arguments"); + if(cli_args_max >= cli_args_min && argc > cli_args_max) + usage(1, "Too many command-line arguments"); + + return cli_main(argc, argv); +} diff --git a/lib/cli++/messages.cc b/lib/cli++/messages.cc new file mode 100644 index 0000000..d9e053d --- /dev/null +++ b/lib/cli++/messages.cc @@ -0,0 +1,68 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +#include <errno.h> +#include <config.h> +#include "fdbuf/fdbuf.h" +#include <stdlib.h> +#include <string.h> +#include "cli++.h" + +extern const char* argv0; + +static void cli_msg(const char* prefix, + const char* a, + const char* b, + const char* c, + const char* d, + bool add_error = false) +{ + ferr << cli_program << ": " << prefix << a; + if(b) ferr << b; + if(c) ferr << c; + if(d) ferr << d; + if (add_error) + ferr << ": " << strerror(errno); + ferr << endl; +} + +void cli_error(int exit_value, + const char* a, + const char* b, + const char* c, + const char* d) +{ + cli_msg("Error: ", a, b, c, d, false); + exit(exit_value); +} + +void cli_syserror(int exit_value, + const char* a, + const char* b, + const char* c, + const char* d) +{ + cli_msg("Error: ", a, b, c, d, true); + exit(exit_value); +} + +void cli_warning(const char* a, + const char* b, + const char* c, + const char* d) +{ + cli_msg("Warning: ", a, b, c, d, false); +} diff --git a/lib/cli++/only_long.cc b/lib/cli++/only_long.cc new file mode 100644 index 0000000..3800a25 --- /dev/null +++ b/lib/cli++/only_long.cc @@ -0,0 +1,3 @@ +#include "cli++.h" + +const bool cli_only_long = false; diff --git a/lib/config_path.cc b/lib/config_path.cc new file mode 100644 index 0000000..f758a23 --- /dev/null +++ b/lib/config_path.cc @@ -0,0 +1,58 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include "defines.h" +#include <stdlib.h> +#include "configio.h" +#include "fdbuf/fdbuf.h" + +mystring config_BIN_DIR; +mystring config_CONFIG_DIR; +mystring config_HOME_DIR; +mystring config_PROTOCOLS_DIR; +mystring config_SBIN_DIR; + +static mystring test_prefix; +static bool initialized = false; + +mystring config_path(const char* dflt, const char* testdir, const char* subdir, const char* filename) +{ + if (!initialized) { + // Check if the program is running setuid, to avoid privilege escalation. + if (getuid() == geteuid()) + test_prefix = getenv("NULLMAILER_TEST_PREFIX"); + } + mystring result = test_prefix; + if (!result) + result = dflt; + else { + result += '/'; + result += testdir; + } + result += '/'; + if (subdir) { + result += subdir; + result += '/'; + } + result += filename; + return result; +} diff --git a/lib/config_read.cc b/lib/config_read.cc new file mode 100644 index 0000000..e6c4e77 --- /dev/null +++ b/lib/config_read.cc @@ -0,0 +1,37 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include "defines.h" +#include "configio.h" +#include "fdbuf/fdbuf.h" + +bool config_read(const char* filename, mystring& result) +{ + const mystring fullname = CONFIG_PATH(CONFIG, NULL, filename); + fdibuf in(fullname.c_str()); + if (!in) + return config_syserr(fullname.c_str()); + if(!in.getline(result)) + return false; + result = result.strip(); + return result.length() > 0; +} diff --git a/lib/config_readint.cc b/lib/config_readint.cc new file mode 100644 index 0000000..c2b0eb9 --- /dev/null +++ b/lib/config_readint.cc @@ -0,0 +1,38 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include <stdlib.h> +#include <limits.h> +#include "defines.h" +#include "configio.h" +#include "fdbuf/fdbuf.h" + +bool config_readint(const char* filename, int& result) +{ + mystring tmp; + if(!config_read(filename, tmp)) + return false; + char* endptr; + long longresult = strtol(tmp.c_str(), &endptr, 10); + result = (int) longresult; + return endptr > tmp.c_str() && longresult >= INT_MIN && longresult <= INT_MAX; +} diff --git a/lib/config_readlist.cc b/lib/config_readlist.cc new file mode 100644 index 0000000..2b49d5d --- /dev/null +++ b/lib/config_readlist.cc @@ -0,0 +1,43 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include "defines.h" +#include "configio.h" +#include "fdbuf/fdbuf.h" + +bool config_readlist(const char* filename, list<mystring>& result) +{ + const mystring fullname = CONFIG_PATH(CONFIG, NULL, filename); + fdibuf in(fullname.c_str()); + if(!in) + return config_syserr(fullname.c_str()); + mystring tmp; + bool nonempty = false; + while(in.getline(tmp)) { + tmp = tmp.strip(); + if(tmp[0] != '#' && tmp.length() > 0) { + result.append(tmp); + nonempty = true; + } + } + return nonempty; +} diff --git a/lib/config_syserr.cc b/lib/config_syserr.cc new file mode 100644 index 0000000..9dd70c0 --- /dev/null +++ b/lib/config_syserr.cc @@ -0,0 +1,33 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include <errno.h> +#include "config.h" +#include "errcodes.h" +#include "configio.h" +#include "cli++/cli++.h" + +bool config_syserr(const char* filename) +{ + if (errno != ENOENT) + cli_syserror(ERR_CONFIG, "Could not read config file \"", filename, "\""); + return false; +} diff --git a/lib/configio.h b/lib/configio.h new file mode 100644 index 0000000..c176ae8 --- /dev/null +++ b/lib/configio.h @@ -0,0 +1,15 @@ +#ifndef NULLMAILER__CONFIGIO__H__ +#define NULLMAILER__CONFIGIO__H__ + +#include "mystring/mystring.h" +#include "list.h" + +mystring config_path(const char* dflt, const char* testdir, const char* subdir, const char* filename); +#define CONFIG_PATH(NAME, SUBDIR, FILENAME) config_path(NAME##_DIR, NAME##_TEST_DIR, SUBDIR, FILENAME) + +bool config_read(const char* filename, mystring& result); +bool config_readlist(const char* filename, list<mystring>& result); +bool config_readint(const char* filename, int& result); +bool config_syserr(const char* filename); + +#endif // NULLMAILER__CONFIGIO__H__ diff --git a/lib/connect.h b/lib/connect.h new file mode 100644 index 0000000..d3354b0 --- /dev/null +++ b/lib/connect.h @@ -0,0 +1,6 @@ +#ifndef NULLMAILER_CONNECT__H__ +#define NULLMAILER_CONNECT__H__ + +extern int tcpconnect(const char* hostname, int port, const char* source); + +#endif // NULLMAILER_CONNECT__H__ diff --git a/lib/defines.h b/lib/defines.h new file mode 100644 index 0000000..e7c4427 --- /dev/null +++ b/lib/defines.h @@ -0,0 +1,16 @@ +#ifndef NULLMAILER__DEFINES__H__ +#define NULLMAILER__DEFINES__H__ + +extern const char QUEUE_DIR[]; +extern const char CONFIG_DIR[]; +extern const char PROTOCOLS_DIR[]; +extern const char BIN_DIR[]; +extern const char SBIN_DIR[]; + +#define QUEUE_TEST_DIR "queue" +#define CONFIG_TEST_DIR "conf" +#define PROTOCOLS_TEST_DIR "protocols" +#define BIN_TEST_DIR "bin" +#define SBIN_TEST_DIR "sbin" + +#endif /* NULLMAILER__DEFINES__H__ */ diff --git a/lib/errcodes.cc b/lib/errcodes.cc new file mode 100644 index 0000000..84b9f76 --- /dev/null +++ b/lib/errcodes.cc @@ -0,0 +1,51 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "errcodes.h" + +const char* errorstr(int code) +{ + switch (code) { + case 0: return "No error"; + case ERR_HOST_NOT_FOUND: return "Host not found"; + case ERR_NO_ADDRESS: return "Host has no address"; + case ERR_GHBN_FATAL: return "Fatal error in gethostbyname"; + case ERR_GHBN_TEMP: return "Temporary error in gethostbyname"; + case ERR_SOCKET: return "Socket failed"; + case ERR_CONN_REFUSED: return "Connection refused"; + case ERR_CONN_TIMEDOUT: return "Connection timed out"; + case ERR_CONN_UNREACHABLE: return "Host or network unreachable"; + case ERR_CONN_FAILED: return "Connection failed"; + case ERR_PROTO: return "Protocol error"; + case ERR_MSG_OPEN: return "Could not open message"; + case ERR_MSG_READ: return "Could not read message"; + case ERR_MSG_WRITE: return "Could not write message"; + case ERR_EXEC_FAILED: return "Could not exec program"; + case ERR_MSG_TEMPFAIL: return "Temporary error in sending the message"; + case ERR_CONFIG: return "Could not read config files"; + case ERR_MSG_REFUSED: return "Server refused the message"; + case ERR_MSG_PERMFAIL: return "Permanent error in sending the message"; + case ERR_BIND_FAILED: return "Failed to bind source address"; + } + return (code & ERR_PERMANENT_FLAG) + ? "Unspecified permanent error" + : "Unspecified temporary error"; +} diff --git a/lib/errcodes.h b/lib/errcodes.h new file mode 100644 index 0000000..94564ee --- /dev/null +++ b/lib/errcodes.h @@ -0,0 +1,33 @@ +#ifndef NULLMAILER__ERRCODES__H__ +#define NULLMAILER__ERRCODES__H__ + +// Temporary errors +#define ERR_USAGE 1 // Invalid command-line arguments +#define ERR_HOST_NOT_FOUND 2 // gethostbyname failed with HOST_NOT_FOUND +#define ERR_NO_ADDRESS 3 // gethostbyname failed with NO_ADDRESS +#define ERR_GHBN_TEMP 5 // gethostbyname failed with TRY_AGAIN +#define ERR_SOCKET 6 // socket failed +#define ERR_CONN_REFUSED 7 // connect failed with ECONNREFUSED +#define ERR_CONN_TIMEDOUT 8 // connect failed with ETIMEDOUT +#define ERR_CONN_UNREACHABLE 9 // connect failed with ENETUNREACH +#define ERR_CONN_FAILED 10 // connect failed +#define ERR_PROTO 11 // unexpected result from server +#define ERR_MSG_OPEN 12 // could not open the message +#define ERR_MSG_READ 13 // reading the message failed +#define ERR_MSG_WRITE 14 // writing the message failed +#define ERR_EXEC_FAILED 15 // executing a program failed +#define ERR_MSG_TEMPFAIL 16 // server temporarily failed to receive +#define ERR_UNKNOWN 17 // Arbitrary error code +#define ERR_CONFIG 18 // Error reading a config file +#define ERR_BIND_FAILED 19 // Failed to bind source address + +// Permanent errors +#define ERR_GHBN_FATAL 33 // gethostbyname failed with NO_RECOVERY +#define ERR_MSG_REFUSED 34 // server refused the message +#define ERR_MSG_PERMFAIL 35 // server permanently failed to receive + +#define ERR_PERMANENT_FLAG 32 + +extern const char* errorstr(int); + +#endif // NULLMAILER__ERRCODES__H__ diff --git a/lib/fdbuf/ChangeLog b/lib/fdbuf/ChangeLog new file mode 100644 index 0000000..ba96ad5 --- /dev/null +++ b/lib/fdbuf/ChangeLog @@ -0,0 +1,161 @@ +2000-08-22 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * Added the missing accessor for the internal "errnum" data member + to the fdibuf and fdobuf classes. + +2000-08-10 Bruce Guenter <bruce@bruce-guenter.dyndns.org> + + * fdibuf.cc (read_large): Fixed a bug in the increment of data. + + * fdibuf_mystring.cc (getline): Reduced some of the expressions + into variables. + +2000-04-08 Bruce Guenter <bguenter@bguenter.pointsnorth.com> + + * fdibuf.cc (read_large): Fixed bug: count needed to be + incremented after reading data in. + +2000-04-07 Bruce Guenter <bguenter@bguenter.pointsnorth.com> + + * fdobuf_signed.cc (operator<<): Immediately output a '-' for + negative numbers rather than storing a negative flag for later. + + * fdobuf_unsigned.cc (operator<<): Moved the integer versions of + this operator into their own modules. + + * fdobuf_seek.cc (seek): Moved this routine out of fdobuf.cc + + * fdibuf.cc (read_large): Added this routine to read in a chunk of + data larger than the size of the buffer. + +2000-04-06 Bruce Guenter <bguenter@bguenter.pointsnorth.com> + + * fdibuf_netstring.cc (getnetstring): Moved this routine into its + own source file. + + * fdobuf.cc (write_large): Added this routine to write out a chunk + of data larger than the size of the buffer, to avoid doing extra + copies. + (write): Removed an extraneous code segment. + + * fdobuf.h: Moved the fdobuf declarations here. + + * fdibuf.h: Moved the fdibuf declarations here. + + * fdbuf.h: Removed extraneous fdobuf declaration. + +1999-07-08 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf.cc (write): Optimized this routine better for the case + where the amount of data to be written will fit inside the buffer. + +1999-07-05 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdbuf.h (fdobuf,fdibuf): Made some of the routines here virtual + in order to extend it properly. + Added "tell()" operations to both fdibuf and fdobuf to indicate + the current logical read/write point. + +1999-07-04 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf_chownmod.cc: Added two new routines chown and chmod, + which operate directly on the open fd. + +1999-06-30 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdibuf_mystring.cc (getline): Make this routine return the + number of bytes actually read, including the delimiter, even + though the delimiter is not added to the returned string. + +1999-06-29 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdibuf.cc (fdibuf): Added a 'seekfwd' function to seek forwards + "o" bytes. + + * fdibuf_mystring.cc (getline): Added locking and set the count + properly. + +1999-06-28 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdibuf.cc (get): Make sure count is set for get. + +1999-06-06 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdbuf.cc (fdbuf): Fixed long-standing bug -- I forgot to delete + the buffer in the destructor. + (close): Modified the code to ensure that the fd is not closed + twice (as would happen when destructing the fdbuf). + +1999-05-31 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf.cc: Redefined flush as nflush; added sync code to nflush; + made flush and sync call nflush; added mutex lock calls to all + public methods. + + * fdibuf.cc: Added mutex lock calls to all public methods. + + * fdbuf.cc: Added debugging implementations of lock() and unlock() + mutex operators (to be removed before real use). + +1999-05-28 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf.cc (fdobuf): Fixed missing initialization of bufpos in + one of the two constructors. + +1999-05-01 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf.cc (fdobuf): Added an optional "mode" parameter which + defaults to 0666, which is the permissions for the new file. + (sync): Wrote this function to fsync the file descriptor. + +1999-04-27 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdbuf.h (fdobuf): Removed definition for sync and nonblock mode, + as they won't be handled correctly in the writing code. + +1999-04-03 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf.cc (operator<<): Wrote an operator for signed and + unsigned longs, with overloaded functions for ints and shorts that + promote the parameters to longs. + (write): Wrote a write routine specifically for a single + character. + +1999-04-01 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf.cc: Fixed handling of seek by adding a "bufpos" indicator + that points to the current position in the buffer at which writes + should go. buflength is effectively the maximum value of bufpos + between flushes. + + * fdibuf.cc (get): Renamed getchar to get (to be potentially + overloaded with other types). + + * fdbuf.h (fdbuf): Removed a bunch of write methods and replaced + them with "operator<<", with similar capability to iostreams + methods of the same names. + + * fdobuf.cc (endl): Wrote this manipulator to write an end-of-line + and flush the buffer. + +1999-03-31 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * fdobuf_mystring.cc: Moved all the mystring-specific code from + fdobuf.cc into this module to lessen link problems. + + * fdibuf_mystring.cc: Moved all the mystring-specific code from + fdibuf.cc into this module to lessen link problems. + + * fdobuf.cc (seek): Wrote this seek routine to allow movement in + an output file buffer. + + * fdibuf.cc (seek): Generalized the rewind routine to allow + arbitrary seeks. It also checks to see if the seek point is + within the current buffer and if so just repositions its + pointers. + + * fdbuf.h: If BUFSIZE is not defined, set it here to 4096. + (class fdbuf ): Rename length, start, and size to buflength, + bufstart, and bufsize. Add a new field "offset" to indicate the + current file seek offset. + diff --git a/lib/fdbuf/Makefile.am b/lib/fdbuf/Makefile.am new file mode 100644 index 0000000..152d634 --- /dev/null +++ b/lib/fdbuf/Makefile.am @@ -0,0 +1,29 @@ +noinst_LIBRARIES = libfdbuf.a +AM_CPPFLAGS = -I$(top_srcdir)/lib + +if FDBUF_NO_MYSTRING +mystring_sources = +else +mystring_sources = fdibuf_mystring.cc fdibuf_netstring.cc +endif + +if TLS +tls_sources = tlsibuf.h tlsibuf.cc tlsobuf.h tlsobuf.cc +else +tls_sources = +endif + +libfdbuf_a_SOURCES = \ + fdbuf.h \ + fdbuf.cc \ + fdbuf_copy.cc \ + fdibuf.h \ + fdibuf.cc \ + fdobuf.h \ + fdobuf.cc \ + fdobuf_chownmod.cc \ + fdobuf_seek.cc \ + fdobuf_signed.cc \ + fdobuf_unsigned.cc \ + $(tls_sources) \ + $(mystring_sources) diff --git a/lib/fdbuf/Makefile.in b/lib/fdbuf/Makefile.in new file mode 100644 index 0000000..8c84ce0 --- /dev/null +++ b/lib/fdbuf/Makefile.in @@ -0,0 +1,587 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = lib/fdbuf +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libfdbuf_a_AR = $(AR) $(ARFLAGS) +libfdbuf_a_LIBADD = +am__libfdbuf_a_SOURCES_DIST = fdbuf.h fdbuf.cc fdbuf_copy.cc fdibuf.h \ + fdibuf.cc fdobuf.h fdobuf.cc fdobuf_chownmod.cc fdobuf_seek.cc \ + fdobuf_signed.cc fdobuf_unsigned.cc tlsibuf.h tlsibuf.cc \ + tlsobuf.h tlsobuf.cc fdibuf_mystring.cc fdibuf_netstring.cc +@TLS_TRUE@am__objects_1 = tlsibuf.$(OBJEXT) tlsobuf.$(OBJEXT) +@FDBUF_NO_MYSTRING_FALSE@am__objects_2 = fdibuf_mystring.$(OBJEXT) \ +@FDBUF_NO_MYSTRING_FALSE@ fdibuf_netstring.$(OBJEXT) +am_libfdbuf_a_OBJECTS = fdbuf.$(OBJEXT) fdbuf_copy.$(OBJEXT) \ + fdibuf.$(OBJEXT) fdobuf.$(OBJEXT) fdobuf_chownmod.$(OBJEXT) \ + fdobuf_seek.$(OBJEXT) fdobuf_signed.$(OBJEXT) \ + fdobuf_unsigned.$(OBJEXT) $(am__objects_1) $(am__objects_2) +libfdbuf_a_OBJECTS = $(am_libfdbuf_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libfdbuf_a_SOURCES) +DIST_SOURCES = $(am__libfdbuf_a_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + ChangeLog +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +HAVE_GETADDRINFO = @HAVE_GETADDRINFO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RM = @RM@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libfdbuf.a +AM_CPPFLAGS = -I$(top_srcdir)/lib +@FDBUF_NO_MYSTRING_FALSE@mystring_sources = fdibuf_mystring.cc fdibuf_netstring.cc +@FDBUF_NO_MYSTRING_TRUE@mystring_sources = +@TLS_FALSE@tls_sources = +@TLS_TRUE@tls_sources = tlsibuf.h tlsibuf.cc tlsobuf.h tlsobuf.cc +libfdbuf_a_SOURCES = \ + fdbuf.h \ + fdbuf.cc \ + fdbuf_copy.cc \ + fdibuf.h \ + fdibuf.cc \ + fdobuf.h \ + fdobuf.cc \ + fdobuf_chownmod.cc \ + fdobuf_seek.cc \ + fdobuf_signed.cc \ + fdobuf_unsigned.cc \ + $(tls_sources) \ + $(mystring_sources) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/fdbuf/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/fdbuf/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libfdbuf.a: $(libfdbuf_a_OBJECTS) $(libfdbuf_a_DEPENDENCIES) $(EXTRA_libfdbuf_a_DEPENDENCIES) + $(AM_V_at)-rm -f libfdbuf.a + $(AM_V_AR)$(libfdbuf_a_AR) libfdbuf.a $(libfdbuf_a_OBJECTS) $(libfdbuf_a_LIBADD) + $(AM_V_at)$(RANLIB) libfdbuf.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdbuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdbuf_copy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdibuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdibuf_mystring.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdibuf_netstring.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdobuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdobuf_chownmod.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdobuf_seek.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdobuf_signed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdobuf_unsigned.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlsibuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlsobuf.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/fdbuf/fdbuf.cc b/lib/fdbuf/fdbuf.cc new file mode 100644 index 0000000..2b3d65a --- /dev/null +++ b/lib/fdbuf/fdbuf.cc @@ -0,0 +1,107 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +/////////////////////////////////////////////////////////////////////////////// +// Class fdbuf +/////////////////////////////////////////////////////////////////////////////// +fdbuf::fdbuf(int fdesc, bool dc, unsigned bufsz) + : buf(new char[bufsz]), + buflength(0), + bufstart(0), + offset(0), + errnum(0), + flags(0), + bufsize(bufsz), + fd(fdesc), + do_close(dc) +{ + if(!buf) { + flags = flag_error; + errnum = errno; + } + if(fdesc < 0) + flags |= flag_closed; +#ifdef _REENTRANT + pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; + mutex = tmp; + pthread_mutex_init(&mutex, 0); +#else +#ifdef FDBUF_MUTEX_DEBUG + mutex_count = 0; +#endif +#endif +} + +fdbuf::~fdbuf() +{ + close(); +#ifdef _REENTRANT + pthread_mutex_destroy(&mutex); +#endif + delete buf; +} + +bool fdbuf::error() const +{ + return flags & flag_error; +} + +bool fdbuf::closed() const +{ + return flags & flag_closed; +} + +bool fdbuf::close() +{ + if(do_close && fd >= 0 && !(flags & flag_closed)) { + if(::close(fd) == -1) { + errnum = errno; + flags |= flag_error; + return false; + } + flags |= flag_closed; + } + return true; +} + +#if defined(FDBUF_MUTEX_DEBUG) && !defined(_REENTRANT) +{ + int* null = 0; + (*null)++; + kill(getpid(), 9); +} + +// Debugging code +void fdbuf::lock() +{ + if(mutex) + abort(); + ++mutex; +} + +void fdbuf::unlock() +{ + if(mutex != 1) + abort(); + --mutex; +} +#endif diff --git a/lib/fdbuf/fdbuf.h b/lib/fdbuf/fdbuf.h new file mode 100644 index 0000000..d11682f --- /dev/null +++ b/lib/fdbuf/fdbuf.h @@ -0,0 +1,83 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FDBUF__H__ +#define FDBUF__H__ + +#include "config.h" +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + +#ifdef _REENTRANT +#include <pthread.h> +#endif + +#ifndef FDBUF_SIZE +#define FDBUF_SIZE 4096 +#endif + +class mystring; + +class fdbuf +{ +public: + enum flagbits { flag_eof=1, flag_error=2, flag_closed=4 }; + + fdbuf(int fdesc, bool dc, unsigned bufsz = FDBUF_SIZE); + ~fdbuf(); + bool error() const; + bool closed() const; + bool close(); +#ifdef _REENTRANT + void lock() { pthread_mutex_lock(&mutex); } + void unlock() { pthread_mutex_unlock(&mutex); } +#else +#ifdef FDBUF_MUTEX_DEBUG + void lock(); + void unlock(); +#else + void lock() { } + void unlock() { } +#endif +#endif +protected: + char* const buf; + unsigned buflength; // Length of the data in the buffer + unsigned bufstart; // Start of the data in the buffer + unsigned offset; // Current file read/write offset + int errnum; // Saved error flag + unsigned flags; // Status flags + + const unsigned bufsize; // Total buffer size + const int fd; + const bool do_close; // True to close on destructor + +#ifdef _REENTRANT + pthread_mutex_t mutex; +#else +#ifdef FDBUF_MUTEX_DEBUG + unsigned mutex; +#endif +#endif +}; + +bool fdbuf_copy(class fdibuf&, class fdobuf&, bool noflush = false); + +#include "fdbuf/fdibuf.h" +#include "fdbuf/fdobuf.h" + +#endif // FDBUF__H__ diff --git a/lib/fdbuf/fdbuf_copy.cc b/lib/fdbuf/fdbuf_copy.cc new file mode 100644 index 0000000..a31ab0b --- /dev/null +++ b/lib/fdbuf/fdbuf_copy.cc @@ -0,0 +1,38 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" + +/////////////////////////////////////////////////////////////////////////////// +// Other routines +/////////////////////////////////////////////////////////////////////////////// +bool fdbuf_copy(fdibuf& in, fdobuf& out, bool noflush) +{ + if(in.eof()) + return true; + if(!in || !out) + return false; + do { + char buf[FDBUF_SIZE]; + if(!in.read(buf, FDBUF_SIZE) && in.last_count() == 0) + break; + if(!out.write(buf, in.last_count()) && out.last_count() < in.last_count()) + return false; + } while(!in.eof()); + if(!noflush && !out.flush()) + return false; + return in.eof(); +} diff --git a/lib/fdbuf/fdibuf.cc b/lib/fdbuf/fdibuf.cc new file mode 100644 index 0000000..205d63c --- /dev/null +++ b/lib/fdbuf/fdibuf.cc @@ -0,0 +1,197 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +/////////////////////////////////////////////////////////////////////////////// +// Class fdibuf +/////////////////////////////////////////////////////////////////////////////// +fdibuf::fdibuf(int fdesc, bool dc, unsigned bufsz) + : fdbuf(fdesc, dc, bufsz) +{ +} + +fdibuf::fdibuf(const char* filename, unsigned bufsz) + : fdbuf(open(filename, O_RDONLY), true, bufsz) +{ + if(fd == -1) { + flags = flag_error; + errnum = errno; + } +} + +fdibuf::~fdibuf() +{ +} + +bool fdibuf::eof() const +{ + return (flags & flag_eof) && (bufstart >= buflength); +} + +bool fdibuf::operator!() const +{ + return eof() || error() || closed(); +} + +// refill is protected -- no locking +bool fdibuf::refill() +{ + if(flags) + return false; + if(bufstart != 0) { + if(bufstart < buflength) { + buflength -= bufstart; + memcpy(buf, buf+bufstart, buflength); + } else + buflength = 0; + bufstart = 0; + } + unsigned oldbuflength = buflength; + if(buflength < bufsize) { + ssize_t red = _read(buf+buflength, bufsize-buflength); + if(red < 0) { + errnum = errno; + flags |= flag_error; + } + else if(red == 0) + flags |= flag_eof; + else { + buflength += red; + offset += red; + } + } + return buflength > oldbuflength; +} + +bool fdibuf::get(char& ch) +{ + lock(); + count = 0; + if(bufstart >= buflength) + refill(); + bool r = true; + if(eof() || error()) + r = false; + else { + ch = buf[bufstart++]; + count = 1; + } + unlock(); + return r; +} + +bool fdibuf::read_large(char* data, unsigned datalen) +{ + lock(); + count = 0; + + // If there's any content in the buffer, memcpy it out first. + unsigned len = buflength - bufstart; + if(len > datalen) + len = datalen; + memcpy(data, buf+bufstart, len); + data += len; + datalen -= len; + bufstart += len; + count += len; + + // After the buffer is empty and there's still data to read, + // read it straight from the fd instead of copying it through the buffer. + while(datalen > 0) { + ssize_t red = _read(data, datalen); + if(red < 0) { + errnum = errno; + flags |= flag_error; + break; + } + else if(red == 0) { + flags |= flag_eof; + break; + } + data += red; + datalen -= red; + offset += red; + count += red; + } + unlock(); + return datalen == 0; +} + +bool fdibuf::read(char* data, unsigned datalen) +{ + if(datalen >= bufsize) + return read_large(data, datalen); + lock(); + count = 0; + char* ptr = data; + while(datalen && !eof()) { + if(bufstart >= buflength) + refill(); + unsigned len = buflength-bufstart; + if(len > datalen) + len = datalen; + memcpy(ptr, buf+bufstart, len); + bufstart += len; + datalen -= len; + ptr += len; + count += len; + } + unlock(); + return !datalen; +} + +bool fdibuf::seek(unsigned o) +{ + lock(); + unsigned buf_start = offset - buflength; + if(o >= buf_start && o < offset) { + bufstart = o - buf_start; + } + else { + if(lseek(fd, o, SEEK_SET) != (off_t)o) { + errnum = errno; + flags |= flag_error; + unlock(); + return false; + } + offset = o; + buflength = bufstart = 0; + } + count = 0; + flags &= ~flag_eof; + unlock(); + return true; +} + +bool fdibuf::seekfwd(unsigned o) +{ + return seek(tell() + o); +} + +ssize_t fdibuf::_read(char* buf, ssize_t len) +{ + return ::read(fd, buf, len); +} + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// +fdibuf fin(0); diff --git a/lib/fdbuf/fdibuf.h b/lib/fdbuf/fdibuf.h new file mode 100644 index 0000000..8906b8c --- /dev/null +++ b/lib/fdbuf/fdibuf.h @@ -0,0 +1,53 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FDBUF__FDIBUF__H__ +#define FDBUF__FDIBUF__H__ + +#include "fdbuf.h" + +class fdibuf : protected fdbuf +{ +public: + fdibuf(const char* filename, unsigned bufsz = FDBUF_SIZE); + fdibuf(int fdesc, bool dc = false, unsigned bufsz = FDBUF_SIZE); + virtual ~fdibuf(); + bool close() { lock(); bool r = fdbuf::close(); unlock(); return r; } + bool eof() const; + bool operator!() const ; + operator bool() const { return !operator!(); } + virtual bool get(char& ch); + virtual bool getline(mystring& out, char terminator = '\n'); + virtual bool getnetstring(mystring& out); + virtual bool read(char*, unsigned); + virtual bool read_large(char*, unsigned); + bool read(unsigned char* b, unsigned l) { return read((char*)b, l); } + bool read(signed char* b, unsigned l) { return read((char*)b, l); } + unsigned last_count() { return count; } + bool seek(unsigned o); + bool seekfwd(unsigned o); + bool rewind() { return seek(0); } + unsigned tell() const { return offset-buflength+bufstart; } + int error_number() const { return errnum; } +protected: + unsigned count; // Number of bytes read by last operation + bool refill(); + virtual ssize_t _read(char*, ssize_t); +}; + +extern fdibuf fin; + +#endif // FDBUF__FDIBUF__H__ diff --git a/lib/fdbuf/fdibuf_mystring.cc b/lib/fdbuf/fdibuf_mystring.cc new file mode 100644 index 0000000..3b40bdc --- /dev/null +++ b/lib/fdbuf/fdibuf_mystring.cc @@ -0,0 +1,52 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include <string.h> +#include "fdbuf.h" +#include "mystring/mystring.h" + +bool fdibuf::getline(mystring& out, char terminator) +{ + lock(); + count = 0; + if(bufstart >= buflength) + refill(); + if(eof() || error()) { + unlock(); + return false; + } + out = ""; + while(!eof() && !error()) { + char* ptr = buf+bufstart; + unsigned bufleft = buflength - bufstart; + const char* end = (const char*)memchr(ptr, terminator, bufleft); + if(!end) { + out += mystring(ptr, bufleft); + bufstart = buflength; + count += bufleft; + refill(); + } + else { + unsigned copylen = end - ptr; + out += mystring(ptr, copylen); + bufstart += copylen+1; + count += copylen+1; + break; + } + } + unlock(); + return true; +} diff --git a/lib/fdbuf/fdibuf_netstring.cc b/lib/fdbuf/fdibuf_netstring.cc new file mode 100644 index 0000000..5f16004 --- /dev/null +++ b/lib/fdbuf/fdibuf_netstring.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include <string.h> +#include "fdbuf.h" +#include "mystring/mystring.h" + +bool fdibuf::getnetstring(mystring& out) +{ + // Read in the size + char ch; + unsigned long size = 0; + for(;;) { + if(!get(ch)) + return false; + if(ch == ':') + break; + else if(ch >= '0' && ch <= '9') + size = size*10 + (ch-'0'); + else + return false; + } + char tmp[size]; + if(!read(tmp, size) || !get(ch) || ch != ',') + return false; + out = mystring(tmp, size); + return true; +} diff --git a/lib/fdbuf/fdobuf.cc b/lib/fdbuf/fdobuf.cc new file mode 100644 index 0000000..e1a6031 --- /dev/null +++ b/lib/fdbuf/fdobuf.cc @@ -0,0 +1,214 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// +fdobuf fout(1); +fdobuf ferr(2); + +/////////////////////////////////////////////////////////////////////////////// +// Class fdobuf +/////////////////////////////////////////////////////////////////////////////// +fdobuf::fdobuf(int fdesc, bool dc, unsigned bufsz) + : fdbuf(fdesc, dc, bufsz), + bufpos(0) +{ +} + +fdobuf::fdobuf(const char* filename, int f, int mode, unsigned bufsz) + : fdbuf(open(filename, O_WRONLY | f, mode), true, bufsz), + bufpos(0) +{ + if(fd == -1) { + flags = flag_error; + errnum = errno; + } +} + +fdobuf::~fdobuf() +{ + flush(); +} + +bool fdobuf::close() +{ + if(!flush()) + return false; + lock(); + bool r = fdbuf::close(); + unlock(); + return r; +} + +bool fdobuf::operator!() const +{ + return error() || closed(); +} + +bool fdobuf::nflush(bool withsync) +{ + if(flags) + return false; + while(bufstart < buflength) { + ssize_t written = _write(buf+bufstart, buflength-bufstart); + if(written < 0) { + flags |= flag_error; + errnum = errno; + return false; + } + else { + bufstart += written; + offset += written; + } + } + buflength = 0; + bufstart = 0; + bufpos = 0; + if(withsync && (fsync(fd) == -1)) { + flags |= flag_error; + errnum = errno; + return false; + } + return true; +} + +bool fdobuf::flush() +{ + lock(); + bool r = nflush(false); + unlock(); + return r; +} + +bool fdobuf::sync() +{ + lock(); + bool r = nflush(true); + unlock(); + return r; +} + +bool fdobuf::write(char ch) +{ + if(flags) + return false; + + lock(); + count = 0; + buf[bufpos++] = ch; + //if(buflength >= bufsize && !nflush(false)) { + // unlock(); + // return false; + //} + if(bufpos >= buflength) + buflength = bufpos; + if(buflength >= bufsize && !nflush(false)) { + unlock(); + return false; + } + count = 1; + unlock(); + return true; +} + +bool fdobuf::write_large(const char* data, unsigned datalen) +{ + if(flags) + return false; + + lock(); + count = 0; + + if(!nflush(false)) { + unlock(); + return false; + } + + while(datalen > 0) { + ssize_t written = _write(data, datalen); + if(written < 0) { + flags |= flag_error; + errnum = errno; + unlock(); + return false; + } + datalen -= written; + data += written; + offset += written; + count += written; + } + unlock(); + return true; +} + +bool fdobuf::write(const char* data, unsigned datalen) +{ + if(datalen >= bufsize) + return write_large(data, datalen); + + if(flags) + return false; + + lock(); + const char* ptr = data; + count = 0; + // Amount is the number of bytes available in the buffer + unsigned amount = bufsize-bufpos; + while(datalen >= amount) { + // If we get here, this copy will completely fill the buffer, + // requiring a flush + memcpy(buf+bufpos, ptr, amount); + bufpos = bufsize; + buflength = bufsize; + datalen -= amount; + ptr += amount; + if(!nflush(false)) { + unlock(); + return false; + } + count += amount; + amount = bufsize-bufpos; + } + // At this point, the remaining data will fit into the buffer + memcpy(buf+bufpos, ptr, datalen); + count += datalen; + bufpos += datalen; + if(bufpos > buflength) buflength = bufpos; + unlock(); + return true; +} + +ssize_t fdobuf::_write(const char* buf, ssize_t len) +{ + return ::write(fd, buf, len); +} + +/////////////////////////////////////////////////////////////////////////////// +// Manipulators +/////////////////////////////////////////////////////////////////////////////// +fdobuf& endl(fdobuf& fd) +{ + fd.write("\n", 1); + fd.flush(); + return fd; +} diff --git a/lib/fdbuf/fdobuf.h b/lib/fdbuf/fdobuf.h new file mode 100644 index 0000000..f59f815 --- /dev/null +++ b/lib/fdbuf/fdobuf.h @@ -0,0 +1,92 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FDBUF__FDOBUF__H__ +#define FDBUF__FDOBUF__H__ + +#include "fdbuf.h" + +class fdobuf : protected fdbuf +{ +public: + enum openflags { create=O_CREAT, + excl=O_EXCL, + trunc=O_TRUNC, + append=O_APPEND }; + + fdobuf(const char* filename, int, int mode = 0666, + unsigned bufsz = FDBUF_SIZE); + fdobuf(int fdesc, bool dc=false, unsigned bufsz = FDBUF_SIZE); + virtual ~fdobuf(); + bool close(); + bool operator!() const; + operator bool() const + { + return !operator!(); + } + bool flush(); + bool sync(); + virtual bool write(char); + bool write(unsigned char c) { return write((char)c); } + bool write(signed char c) { return write((char)c); } + virtual bool write(const char*, unsigned); + bool write(const unsigned char* b, unsigned l) { return write((char*)b, l); } + bool write(const signed char* b, unsigned l) { return write((char*)b, l); } + virtual bool write_large(const char*, unsigned); + unsigned last_count() { return count; } + bool seek(unsigned o); + bool rewind() { return seek(0); } + unsigned tell() const { return offset + bufpos; } + + bool chown(uid_t, gid_t) const; + bool chmod(mode_t) const; + + fdobuf& operator<<(const char* str) + { + write(str, strlen(str)); + return *this; + } + fdobuf& operator<<(char ch) + { + write(ch); + return *this; + } + fdobuf& operator<<(fdobuf& (*manip)(fdobuf&)) + { + return manip(*this); + } + fdobuf& operator<<(unsigned long); + fdobuf& operator<<(signed long); + fdobuf& operator<<(unsigned i) { return operator<<((unsigned long)i); } + fdobuf& operator<<(signed i) { return operator<<((signed long)i); } + fdobuf& operator<<(unsigned short i) { return operator<<((unsigned long)i); } + fdobuf& operator<<(signed short i) { return operator<<((signed long)i); } + + int error_number() const { return errnum; } +protected: + virtual bool nflush(bool withsync); + virtual ssize_t _write(const char* buf, ssize_t len); + + unsigned bufpos; // Current write position in the buffer + unsigned count; // Number of bytes written by last operation +}; + +fdobuf& endl(fdobuf& fd); + +extern fdobuf fout; +extern fdobuf ferr; + +#endif // FDBUF__FDOBUF__H__ diff --git a/lib/fdbuf/fdobuf_chownmod.cc b/lib/fdbuf/fdobuf_chownmod.cc new file mode 100644 index 0000000..61bc34a --- /dev/null +++ b/lib/fdbuf/fdobuf_chownmod.cc @@ -0,0 +1,34 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include <string.h> +#include "fdbuf.h" +#include <sys/stat.h> +#include <unistd.h> + +bool fdobuf::chown(uid_t uid, gid_t gid) const +{ + if(error()) + return false; + return fchown(fd, uid, gid) != -1; +} + +bool fdobuf::chmod(mode_t mode) const +{ + if(error()) + return false; + return fchmod(fd, mode) != -1; +} diff --git a/lib/fdbuf/fdobuf_seek.cc b/lib/fdbuf/fdobuf_seek.cc new file mode 100644 index 0000000..ab376ea --- /dev/null +++ b/lib/fdbuf/fdobuf_seek.cc @@ -0,0 +1,48 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <errno.h> +#include <unistd.h> + +bool fdobuf::seek(unsigned o) +{ + if(flags) + return false; + + lock(); + unsigned buf_start = offset; + unsigned buf_end = offset + buflength; + if(o >= buf_start && o < buf_end) { + bufpos = o - buf_start; + } + else { + if(!nflush(false)) { + unlock(); + return false; + } + if(lseek(fd, o, SEEK_SET) != (off_t)o) { + errnum = errno; + flags |= flag_error; + unlock(); + return false; + } + offset = o; + } + count = 0; + unlock(); + return true; +} diff --git a/lib/fdbuf/fdobuf_signed.cc b/lib/fdbuf/fdobuf_signed.cc new file mode 100644 index 0000000..4282ace --- /dev/null +++ b/lib/fdbuf/fdobuf_signed.cc @@ -0,0 +1,38 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <limits.h> + +#define MAXSTRLEN ((sizeof(signed long)*CHAR_BIT)/3) + +fdobuf& fdobuf::operator<<(signed long i) +{ + if(i == 0) + return operator<<('0'); + if(i < 0) { + operator<<('-'); + i = -i; + } + char buf[MAXSTRLEN+1]; + char* ptr = buf+MAXSTRLEN; + *ptr-- = 0; + while(i) { + *ptr-- = i % 10 + '0'; + i /= 10; + } + return operator<<(ptr+1); +} diff --git a/lib/fdbuf/fdobuf_unsigned.cc b/lib/fdbuf/fdobuf_unsigned.cc new file mode 100644 index 0000000..61fc098 --- /dev/null +++ b/lib/fdbuf/fdobuf_unsigned.cc @@ -0,0 +1,34 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "fdbuf.h" +#include <limits.h> + +#define MAXSTRLEN ((sizeof(signed long)*CHAR_BIT)/3) + +fdobuf& fdobuf::operator<<(unsigned long i) +{ + if(i == 0) + return operator<<('0'); + char buf[MAXSTRLEN+1]; + char* ptr = buf+MAXSTRLEN; + *ptr-- = 0; + while(i) { + *ptr-- = i % 10 + '0'; + i /= 10; + } + return operator<<(ptr+1); +} diff --git a/lib/fdbuf/tlsibuf.cc b/lib/fdbuf/tlsibuf.cc new file mode 100644 index 0000000..787c323 --- /dev/null +++ b/lib/fdbuf/tlsibuf.cc @@ -0,0 +1,31 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "tlsibuf.h" + +/////////////////////////////////////////////////////////////////////////////// +// Class tlsibuf +/////////////////////////////////////////////////////////////////////////////// +tlsibuf::tlsibuf(gnutls_session_t s, unsigned bufsz) + : fdibuf(-1, false, bufsz), session(s) +{ + flags &= ~flag_closed; +} + +ssize_t tlsibuf::_read(char* buf, ssize_t len) +{ + return gnutls_record_recv(session, buf, len); +} diff --git a/lib/fdbuf/tlsibuf.h b/lib/fdbuf/tlsibuf.h new file mode 100644 index 0000000..3c74365 --- /dev/null +++ b/lib/fdbuf/tlsibuf.h @@ -0,0 +1,32 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FDBUF__TLSIBUF__H__ +#define FDBUF__TLSIBUF__H__ + +#include "fdibuf.h" +#include <gnutls/gnutls.h> + +class tlsibuf : public fdibuf +{ +public: + tlsibuf(gnutls_session_t, unsigned bufsz = FDBUF_SIZE); +protected: + gnutls_session_t session; + virtual ssize_t _read(char* buf, ssize_t len); +}; + +#endif // FDBUF__TLSIBUF__H__ diff --git a/lib/fdbuf/tlsobuf.cc b/lib/fdbuf/tlsobuf.cc new file mode 100644 index 0000000..83d68f8 --- /dev/null +++ b/lib/fdbuf/tlsobuf.cc @@ -0,0 +1,31 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "tlsobuf.h" + +/////////////////////////////////////////////////////////////////////////////// +// Class tlsobuf +/////////////////////////////////////////////////////////////////////////////// +tlsobuf::tlsobuf(gnutls_session_t s, unsigned bufsz) + : fdobuf(-1, false, bufsz), session(s) +{ + flags &= ~flag_closed; +} + +ssize_t tlsobuf::_write(const char* buf, ssize_t len) +{ + return gnutls_record_send(session, buf, len); +} diff --git a/lib/fdbuf/tlsobuf.h b/lib/fdbuf/tlsobuf.h new file mode 100644 index 0000000..43f68ec --- /dev/null +++ b/lib/fdbuf/tlsobuf.h @@ -0,0 +1,32 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FDBUF__TLSOBUF__H__ +#define FDBUF__TLSOBUF__H__ + +#include "fdobuf.h" +#include <gnutls/gnutls.h> + +class tlsobuf : public fdobuf +{ +public: + tlsobuf(gnutls_session_t, unsigned bufsz = FDBUF_SIZE); +protected: + gnutls_session_t session; + virtual ssize_t _write(const char* buf, ssize_t len); +}; + +#endif // FDBUF__TLSOBUF__H__ diff --git a/lib/forkexec.cc b/lib/forkexec.cc new file mode 100644 index 0000000..a59115d --- /dev/null +++ b/lib/forkexec.cc @@ -0,0 +1,168 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include "cli++/cli++.h" +#include "fdbuf/fdbuf.h" +#include "mystring/mystring.h" +#include "defines.h" +#include "errcodes.h" +#include "forkexec.h" + +#define ERR(MSG) do{ ferr << cli_program << ": " << MSG << ": " << strerror(errno) << endl; } while(0) +#define FAIL(MSG) do{ ERR(MSG); return false; }while(0) + +static int fdnull = -1; + +fork_exec::fork_exec(const char* p) + : pid(-1), name(p) +{ +} + +fork_exec::~fork_exec() +{ + wait(); +} + +bool fork_exec::operator!() const +{ + return pid < 0; +} + +bool fork_exec::start(const char* args[], int redirn, int redirs[]) +{ + autoclose_pipe pipes[redirn]; + for (int i = 0; i < redirn; i++) { + if (redirs[i] == REDIRECT_PIPE_TO || redirs[i] == REDIRECT_PIPE_FROM) + if (!pipes[i].open()) + FAIL("Could not create pipe"); + if (redirs[i] == REDIRECT_NULL) + if (fdnull < 0) + if ((fdnull = open("/dev/null", O_RDWR)) < 0) + FAIL("Could not open \"/dev/null\""); + } + if ((pid = fork()) < 0) + FAIL("Could not fork"); + if (pid == 0) { + // Child process, exec program + for (int i = 0; i < redirn; i++) { + int r = redirs[i]; + if (r == REDIRECT_NULL) + dup2(fdnull, i); + else if (r == REDIRECT_PIPE_FROM) { + dup2(pipes[i][1], i); + pipes[i].close(); + } + else if (r == REDIRECT_PIPE_TO) { + dup2(pipes[i][0], i); + pipes[i].close(); + } + else if (r > 0) { + dup2(r, i); + if (r >= redirn) + close(r); + } + } + execv(args[0], (char**)args); + ERR("Could not exec " << name); + _exit(ERR_EXEC_FAILED); + } + for (int i = 0; i < redirn; i++) { + if (redirs[i] == REDIRECT_PIPE_TO) + redirs[i] = pipes[i].extract(1); + else if (redirs[i] == REDIRECT_PIPE_FROM) + redirs[i] = pipes[i].extract(0); + } + return true; +} + +bool fork_exec::start(const char* program, int redirn, int redirs[]) +{ + const char* args[2] = { program, NULL }; + return start(args, redirn, redirs); +} + +int fork_exec::wait_status() +{ + if (pid > 0) { + int status; + if (waitpid(pid, &status, 0) == pid) { + pid = -1; + return status; + } + } + return -1; +} + +bool fork_exec::wait() +{ + if (pid > 0) { + int status = wait_status(); + if (status < 0) + FAIL("Error catching the return value from " << name); + if (WIFEXITED(status)) { + status = WEXITSTATUS(status); + if (status) { + ferr << cli_program << ": " << name << " failed: " << status << endl; + return false; + } + } + else + FAIL(name << " crashed or was killed"); + } + return true; +} + +mystring program_path(const char* program) +{ + return CONFIG_PATH(BIN, NULL, program); +} + +static const char* nqpath() +{ + static mystring cache; + if (!cache) { + const char* env; + if ((env = getenv("NULLMAILER_QUEUE")) != NULL) + cache = env; + else + cache = CONFIG_PATH(SBIN, NULL, "nullmailer-queue"); + } + return cache.c_str(); +} + +queue_pipe::queue_pipe() + : fork_exec("nullmailer-queue") +{ +} + +int queue_pipe::start() +{ + int redirs[] = { REDIRECT_PIPE_TO, REDIRECT_NULL, REDIRECT_NULL }; + if (!fork_exec::start(nqpath(), 3, redirs)) + return -1; + return redirs[0]; +} diff --git a/lib/forkexec.h b/lib/forkexec.h new file mode 100644 index 0000000..9fdd98b --- /dev/null +++ b/lib/forkexec.h @@ -0,0 +1,43 @@ +#ifndef NULLMAILER__FORK_EXEC__H +#define NULLMAILER__FORK_EXEC__H + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include "mystring/mystring.h" +#include "autoclose.h" +#include "configio.h" + +mystring program_path(const char* program); + +#define REDIRECT_NONE -1 +#define REDIRECT_NULL -2 +#define REDIRECT_PIPE_FROM -3 +#define REDIRECT_PIPE_TO -4 + +class fork_exec +{ + private: + pid_t pid; + const char* name; + + public: + fork_exec(const char*); + ~fork_exec(); + bool operator!() const; + + bool start(const char* args[], int redirn, int redirs[]); + bool start(const char* program, int redirn, int redirs[]); + bool wait(); + int wait_status(); + inline void kill(int sig) { ::kill(pid, sig); } +}; + +class queue_pipe : public fork_exec +{ + public: + queue_pipe(); + int start(); +}; + +#endif diff --git a/lib/hostname.cc b/lib/hostname.cc new file mode 100644 index 0000000..76ac54e --- /dev/null +++ b/lib/hostname.cc @@ -0,0 +1,45 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include "mystring/mystring.h" +#include "configio.h" +#include "hostname.h" +#include "canonicalize.h" + +mystring me; +mystring defaulthost; +mystring defaultdomain; + +void read_hostnames() +{ + int nome; + nome = 0; + if (!config_read("me", me)) { + nome = 1; + me = "me"; + } + if (!config_read("defaultdomain", defaultdomain)) + defaultdomain = nome ? "defaultdomain" : me.c_str(); + if (!config_read("defaulthost", defaulthost)) + defaulthost = nome ? "defaulthost" : me.c_str(); + canonicalize(defaulthost); +} diff --git a/lib/hostname.h b/lib/hostname.h new file mode 100644 index 0000000..f1cfb0c --- /dev/null +++ b/lib/hostname.h @@ -0,0 +1,10 @@ +#ifndef NULLMAILER__HOSTNAME__H__ +#define NULLMAILER__HOSTNAME__H__ + +extern mystring me; +extern mystring defaulthost; +extern mystring defaultdomain; + +void read_hostnames(); + +#endif diff --git a/lib/itoa.cc b/lib/itoa.cc new file mode 100644 index 0000000..54c927d --- /dev/null +++ b/lib/itoa.cc @@ -0,0 +1,23 @@ +#include "itoa.h" + +const char *itoa(long v, int digits) +{ + static char buf[INTLENGTH]; + bool neg = false; + if(v < 0) { + v = -v; + neg = true; + } + char* ptr = buf + INTLENGTH; + *--ptr = '\0'; + do { + *--ptr = (v % 10) + '0'; + v /= 10; + --digits; + } while(v != 0); + while(digits > 0 && ptr > buf-1) + *--ptr = '0', --digits; + if(neg) + *--ptr = '-'; + return ptr; +} diff --git a/lib/itoa.h b/lib/itoa.h new file mode 100644 index 0000000..8458805 --- /dev/null +++ b/lib/itoa.h @@ -0,0 +1,11 @@ +#ifndef ITOA__H__ +#define ITOA__H__ + +#ifndef INTLENGTH +#define INTLENGTH 64 +/* 40 digits is long enough to handle unsigned 128-bit numbers */ +#endif + +const char *itoa(long, int = 0); + +#endif diff --git a/lib/list.h b/lib/list.h new file mode 100644 index 0000000..3bbd750 --- /dev/null +++ b/lib/list.h @@ -0,0 +1,197 @@ +#ifndef LIST__H__ +#define LIST__H__ + +template<class T> struct list_node +{ + list_node* next; + T data; + list_node(T d, list_node* n = 0) : next(n), data(d) { } + ~list_node() { } +}; + +template<class T> class list_iterator; +template<class T> class const_list_iterator; + +template<class T> class list +{ +public: + typedef list_node<T> node; + typedef list_iterator<T> iter; + typedef const_list_iterator<T> const_iter; + friend class list_iterator<T>; + friend class const_list_iterator<T>; + + list() + : head(0), tail(0), cnt(0) + { + } + list(const list&); + + ~list() + { + empty(); + } + + unsigned count() const + { + return cnt; + } + + void empty() + { + while(head) { + node* next = head->next; + delete head; + head = next; + } + tail = 0; + cnt = 0; + } + + bool append(T data) + { + node* n = new node(data); + if(tail) + tail->next = n; + else + head = n; + tail = n; + ++cnt; + return true; + } + bool prepend(T data) + { + head = new node(data, head); + if(!tail) + tail = head; + ++cnt; + return true; + } + bool remove(iter&); +private: + node* head; + node* tail; + unsigned cnt; +}; + +template<class T> class const_list_iterator +{ + friend class list<T>; +private: + inline void go_next() + { + prev = curr; + if(curr) + curr = curr->next; + } +public: + const_list_iterator(const list<T>& l) + : lst(l), prev(0), curr(l.head) + { + } + void operator++() + { + go_next(); + } + void operator++(int) + { + go_next(); + } + T operator*() const + { + return curr->data; + } + bool operator!() const + { + return curr == 0; + } + operator bool() const + { + return !operator!(); + } +private: + const list<T>& lst; + // g++ 3.2 insists + const typename list<T>::node* prev; + const typename list<T>::node* curr; +}; + +template<class T> +list<T>::list(const list<T>& that) + : head(0), tail(0), cnt(0) +{ + for(const_iter i = that; i; ++i) + append(*i); +} + +template<class T> class list_iterator +{ + friend class list<T>; +private: + inline void go_next() + { + prev = curr; + if(curr) + curr = curr->next; + } +public: + list_iterator(list<T>& l) + : lst(l), prev(0), curr(l.head) + { + } + void operator++() + { + go_next(); + } + void operator++(int) + { + go_next(); + } + T operator*() const + { + return curr->data; + } + T& operator*() + { + return curr->data; + } + bool operator!() const + { + return curr == 0; + } + operator bool() const + { + return !operator!(); + } +private: + list<T>& lst; + typename list<T>::node* prev; + typename list<T>::node* curr; +}; + +template<class T> +bool list<T>::remove(list<T>::iter& iter) +{ + if(this != &iter.lst) + return false; + if(!iter.curr) + return false; + if(iter.prev) { + iter.prev->next = iter.curr->next; + if(iter.curr == tail) + tail = iter.prev; + delete iter.curr; + iter.curr = iter.prev->next; + } + else { + head = iter.curr->next; + if(!head) + tail = 0; + delete iter.curr; + iter.curr = head; + } + --cnt; + return true; +} + +#endif // LIST__H__ diff --git a/lib/listtest.cc b/lib/listtest.cc new file mode 100644 index 0000000..fc10c71 --- /dev/null +++ b/lib/listtest.cc @@ -0,0 +1,50 @@ +#include <iostream.h> +#include "list.h" + +typedef list<int> ilist; +typedef ilist::iter iiter; + +void test_remove_first() +{ + ilist l; + l.append(1); + l.append(2); + iiter i(l); + l.remove(i); + if(!i) cout << "After removing first, iter no longer valid\n"; + else if(*i != 2) cout << "After removing first, iter is wrong\n"; + if(l.count() != 1) cout << "After removing first, count is wrong\n"; +} + +void test_remove_mid() +{ + ilist l; + l.append(1); + l.append(2); + l.append(3); + iiter i(l); + i++; + l.remove(i); + if(!i) cout << "After removing middle, iter no longer valid\n"; + else if(*i != 3) cout << "After removing middle, iter is wrong\n"; + if(l.count() != 2) cout << "After removing middle, count is wrong\n"; +} + +void test_remove_last() +{ + ilist l; + l.append(1); + l.append(2); + iiter i(l); + i++; + l.remove(i); + if(i) cout << "After removing last, iter is still valid\n"; + if(l.count() != 1) cout << "After removing last, count is wrong\n"; +} + +int main() { + test_remove_first(); + test_remove_mid(); + test_remove_last(); + return 0; +} diff --git a/lib/make_defines.sh b/lib/make_defines.sh new file mode 100644 index 0000000..405a37d --- /dev/null +++ b/lib/make_defines.sh @@ -0,0 +1,8 @@ +#!/bin/sh +( + echo "extern const char QUEUE_DIR[]=\"$1\";" + echo "extern const char CONFIG_DIR[]=\"$2\";" + echo "extern const char PROTOCOLS_DIR[]=\"$3\";" + echo "extern const char BIN_DIR[]=\"$4\";" + echo "extern const char SBIN_DIR[]=\"$5\";" +) > defines.cc diff --git a/lib/makefield.cc b/lib/makefield.cc new file mode 100644 index 0000000..2af0382 --- /dev/null +++ b/lib/makefield.cc @@ -0,0 +1,89 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include "defines.h" +#include <unistd.h> +#include "ac/time.h" +#include "itoa.h" +#include "mystring/mystring.h" + +mystring make_date(time_t t) +{ + char buf[256]; + if (t == 0) + t = time(0); + struct tm* l = localtime(&t); + strftime(buf, 256, "%a, %d %b %Y %H:%M:%S ", l); +#ifdef TM_HAS_GMTOFF + long tznum = l->TM_HAS_GMTOFF/60; +#else + long tznum = -timezone/60; +#ifdef TM_HAS_ISDST + int daylight = l->TM_HAS_ISDST; +#endif // TM_HAS_ISDST + if(daylight) + tznum += 60; +#endif // TM_HAS_GMTOFF + char tz[6]; + tz[0] = '+'; + if(tznum < 0) { + tznum = -tznum; + tz[0] = '-'; + } + long tzhours = tznum / 60; + tz[1] = (tzhours/10)%10 + '0'; + tz[2] = tzhours%10 + '0'; + long tzmins = tznum % 60; + tz[3] = (tzmins/10)%10 + '0'; + tz[4] = tzmins%10 + '0'; + tz[5] = 0; + return mystringjoin(buf) + tz; +} + +// Message ID strings have the form SECONDS.USEC.PID.nullmailer@HOST +mystring make_messageid(const mystring& idhost) +{ + struct timeval tv; + gettimeofday(&tv, 0); + mystring tmp = "<"; + tmp += itoa(tv.tv_sec); + tmp += '.'; + tmp += itoa(tv.tv_usec, 6); + tmp += '.'; + tmp += itoa(getpid()); + tmp += ".nullmailer@"; + tmp += idhost; + tmp += '>'; + return tmp; +} + +mystring make_boundary() +{ + struct timeval tv; + gettimeofday(&tv, 0); + mystring tmp = itoa(tv.tv_sec); + tmp += '.'; + tmp += itoa(tv.tv_usec, 6); + tmp += '.'; + tmp += itoa(getpid()); + return tmp; +} diff --git a/lib/makefield.h b/lib/makefield.h new file mode 100644 index 0000000..9686ef2 --- /dev/null +++ b/lib/makefield.h @@ -0,0 +1,8 @@ +#ifndef NULLMAILER__MAKEFIELD__H__ +#define NULLMAILER__MAKEFIELD__H__ + +extern mystring make_date(time_t t = 0); +extern mystring make_messageid(const mystring& idhost); +extern mystring make_boundary(); + +#endif // NULLMAILER__MAKEFIELD__H__ diff --git a/lib/mergelib.sh b/lib/mergelib.sh new file mode 100644 index 0000000..f9b7a94 --- /dev/null +++ b/lib/mergelib.sh @@ -0,0 +1,16 @@ +set -e +archive="$1" +shift +tmpdir=".libmerge.$archive.$$.$RANDOM.$USER" +mkdir "$tmpdir" +cd "$tmpdir" +trap 'cd ..; rm -rf "$tmpdir"' EXIT +for input in "$@"; do + dir="`basename "$input"`" + mkdir "$dir" + cd "$dir" + "${AR:-ar}" x ../../"$input" + cd .. +done +"${AR:-ar}" rc ../"$archive" */* +"${RANLIB:-ranlib}" ../"$archive" diff --git a/lib/mystring/ChangeLog b/lib/mystring/ChangeLog new file mode 100644 index 0000000..458067b --- /dev/null +++ b/lib/mystring/ChangeLog @@ -0,0 +1,116 @@ +2000-04-09 Bruce Guenter <bguenter@bguenter.pointsnorth.com> + + * count.cc (count): Added this routine to count the number of + instances of a single character in a string. + +2000-04-06 Bruce Guenter <bguenter@bguenter.pointsnorth.com> + + * fdobuf.cc (operator<<): Moved this routine out of an inline + declaration. + + * iter.h: Moved the mystring_iter declarations into this file. + + * rep.h: Moved the mystringrep declarations into this file. + + * join.h: Moved the mystringjoin declarations into this file. + + * mystring.h (class mystring): Renamed the "find_first" and + "find_last" routines that scan for items in a set to + "find_first_of" and "find_last_of". + +1999-08-15 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * mystring.h (class mystring_iter): Changed the default seperator + for strings to '\0' + (class mystring): Added a NUL constant (single 0 byte string). + +1999-07-26 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * mystring.h (class mystring_iter): Added this new iterator class, + taken from code used in vmailmgr. It is used to iterate over + essentially a token-delimited string. + +1999-07-14 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * Removed all vestiges of mystringtmp support from this library. + +1999-07-13 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * append.cc: Fixed same bug as below in append. + + * assign.cc: Fixed bug in assign and dup where NULL pointers + caused a crash. + + * append.cc, assign.cc: removed the mystringtmp versions of the + append, assign, and dup operations. mystringtmp now only exists + in the cons[2-7].cc files and tmp.cc + + * find.cc: Split this file into find_first, find_first_of, + find_last, and find_last_of. + +1999-07-12 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * join.cc (traverse): This routine traverses the linked list and + builds a mystringrep out of it. + + * mystring.h: Renamed TRACE to MYSTRING_TRACE. + Added a new mystringjoin class. This class is used to turn a list + of calls to "operator+" into a single constructor by building a + linked list on the stack. This will replace mystringtmp. + + * rep.cc (struct _rep_stats): Fixed the percentage function to not + do divide-by-zero; modified the "slack" reporting to report a + percentage of the requested length. + + * assign.cc: Re-added dup and assign functions for "char*" type, + moving the constructors and assignment operators inline. + + * append.cc: Re-added append functions for "char*" type. + +1999-07-08 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * mystring.h (class mystring): Eliminated the "mystring" return + value for the assign and append operators, since this return value + is never used and causes extra operations. + + * operator_plus.cc: Created this new file containing the + "operator+" routine. + + * operator_pleq.cc: Created this new file containing all the + "operator+=" routines. + + * assign.cc: Created this new file containing all the assign and + dup primitives. + + * mystring.h (class mystring): Removed the += operator taking + "mystringtmp" parameter, and replaced it with two routines, one + for "const mystring&", and one for "const char*". This results in + a net code shrinkage. + + * rep.cc (struct _rep_stats): Added this optional statistics + gathering class to determine the effectiveness of the slack space + and string appending. + + * append.cc (append): Use the new rep->append routine. + + * rep.cc (alloc): Allocate an amount of "slack" space when + allocating a string, to allow for later appends. + (append): This new routine appends a string to the current rep if + and only if the current rep has a single reference and the new + length of the string will fit within the current size. If not, it + makes a dup of this+str and returns a pointer to it. + +1999-06-07 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * tmp.cc: Removed the contents of checkstr, and moved the + necessary parts into the constructors. This makes the code more + efficient, as the constructors are adequately specific to omit + some of the branches in the comparisons. It also ensures that len + is always initialized, allowing many of the simple functions to be + moved inline to the header file. + +1999-04-01 Bruce Guenter <bguenter@mikhail.qcc.sk.ca> + + * cons7.cc (mystring): Wrote this constructor to build a string + from 7 inputs. + diff --git a/lib/mystring/Makefile.am b/lib/mystring/Makefile.am new file mode 100644 index 0000000..c9abf42 --- /dev/null +++ b/lib/mystring/Makefile.am @@ -0,0 +1,26 @@ +noinst_LIBRARIES = libmystring.a +EXTRA_DIST = ChangeLog iter.h join.h mystring.h rep.h trace.h + +AM_CPPFLAGS = -I$(top_srcdir)/lib + +libmystring_a_SOURCES = \ + append.cc \ + assign.cc \ + count.cc \ + fdobuf.cc \ + find_first_ch.cc \ + find_first_of.cc \ + find_last_ch.cc \ + find_last_of.cc \ + iter.cc \ + join.cc \ + lower.cc \ + lstrip.cc \ + mystring.cc \ + rep.cc \ + starts_with.cc \ + rstrip.cc \ + sub.cc \ + subst.cc \ + strip.cc \ + upper.cc diff --git a/lib/mystring/Makefile.in b/lib/mystring/Makefile.in new file mode 100644 index 0000000..7c49873 --- /dev/null +++ b/lib/mystring/Makefile.in @@ -0,0 +1,583 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = lib/mystring +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libmystring_a_AR = $(AR) $(ARFLAGS) +libmystring_a_LIBADD = +am_libmystring_a_OBJECTS = append.$(OBJEXT) assign.$(OBJEXT) \ + count.$(OBJEXT) fdobuf.$(OBJEXT) find_first_ch.$(OBJEXT) \ + find_first_of.$(OBJEXT) find_last_ch.$(OBJEXT) \ + find_last_of.$(OBJEXT) iter.$(OBJEXT) join.$(OBJEXT) \ + lower.$(OBJEXT) lstrip.$(OBJEXT) mystring.$(OBJEXT) \ + rep.$(OBJEXT) starts_with.$(OBJEXT) rstrip.$(OBJEXT) \ + sub.$(OBJEXT) subst.$(OBJEXT) strip.$(OBJEXT) upper.$(OBJEXT) +libmystring_a_OBJECTS = $(am_libmystring_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libmystring_a_SOURCES) +DIST_SOURCES = $(libmystring_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + ChangeLog +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +HAVE_GETADDRINFO = @HAVE_GETADDRINFO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR = @MKDIR@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RM = @RM@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libmystring.a +EXTRA_DIST = ChangeLog iter.h join.h mystring.h rep.h trace.h +AM_CPPFLAGS = -I$(top_srcdir)/lib +libmystring_a_SOURCES = \ + append.cc \ + assign.cc \ + count.cc \ + fdobuf.cc \ + find_first_ch.cc \ + find_first_of.cc \ + find_last_ch.cc \ + find_last_of.cc \ + iter.cc \ + join.cc \ + lower.cc \ + lstrip.cc \ + mystring.cc \ + rep.cc \ + starts_with.cc \ + rstrip.cc \ + sub.cc \ + subst.cc \ + strip.cc \ + upper.cc + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/mystring/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/mystring/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libmystring.a: $(libmystring_a_OBJECTS) $(libmystring_a_DEPENDENCIES) $(EXTRA_libmystring_a_DEPENDENCIES) + $(AM_V_at)-rm -f libmystring.a + $(AM_V_AR)$(libmystring_a_AR) libmystring.a $(libmystring_a_OBJECTS) $(libmystring_a_LIBADD) + $(AM_V_at)$(RANLIB) libmystring.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/append.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assign.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/count.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdobuf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_first_ch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_first_of.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_last_ch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/find_last_of.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/join.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lower.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lstrip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mystring.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rstrip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/starts_with.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sub.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upper.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/mystring/append.cc b/lib/mystring/append.cc new file mode 100644 index 0000000..d0120a5 --- /dev/null +++ b/lib/mystring/append.cc @@ -0,0 +1,19 @@ +#include <string.h> +#include "mystring.h" +#include "trace.h" + +void mystring::append(const char* str, size_t len) +{ + if(!str || !len) + return; + if(!*this) + assign(str, len); + else + rep = rep->append(str, len); +} + +void mystring::append(const char* in) +{ + if(in) + append(in, strlen(in)); +} diff --git a/lib/mystring/assign.cc b/lib/mystring/assign.cc new file mode 100644 index 0000000..5a7b6a7 --- /dev/null +++ b/lib/mystring/assign.cc @@ -0,0 +1,55 @@ +#include "mystring.h" +#include "trace.h" +#include <ctype.h> +#include <string.h> + +void mystring::dupnil() +{ + trace(""); + rep = &nil; + rep->attach(); +} + +void mystring::assign(const char* in) +{ + if(in) + assign(in, strlen(in)); + else { + mystringrep* tmp = rep; + dupnil(); + tmp->detach(); + } +} + +void mystring::assign(const char* in, size_t len) +{ + trace("in='" << in << "'"); + if(in != rep->buf) { + mystringrep* tmp = rep; + dup(in, len); + tmp->detach(); + } +} + +void mystring::dup(const char* in, size_t len) +{ + trace("in='" << in << "'"); + rep = mystringrep::dup(in, len); + rep->attach(); +} + +void mystring::dup(const char* in) +{ + if(in) + dup(in, strlen(in)); + else + dupnil(); +} + +void mystring::operator=(const mystringjoin& in) +{ + mystringrep* tmp = rep; + rep = in.traverse(); + rep->attach(); + tmp->detach(); +} diff --git a/lib/mystring/count.cc b/lib/mystring/count.cc new file mode 100644 index 0000000..36b0a04 --- /dev/null +++ b/lib/mystring/count.cc @@ -0,0 +1,25 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "mystring.h" + +unsigned mystring::count(char ch) const +{ + unsigned c = 0; + for(int pos = find_first(ch); pos > 0; pos = find_first(ch, pos+1)) + ++c; + return c; +} diff --git a/lib/mystring/fdobuf.cc b/lib/mystring/fdobuf.cc new file mode 100644 index 0000000..ff967e6 --- /dev/null +++ b/lib/mystring/fdobuf.cc @@ -0,0 +1,9 @@ +#include "mystring.h" +#include "fdbuf/fdbuf.h" + +fdobuf& operator<<(fdobuf& out, const mystring& str) +{ + out.write(str.c_str(), str.length()); + return out; +} + diff --git a/lib/mystring/find_first_ch.cc b/lib/mystring/find_first_ch.cc new file mode 100644 index 0000000..0a652d0 --- /dev/null +++ b/lib/mystring/find_first_ch.cc @@ -0,0 +1,11 @@ +#include "mystring.h" +#include <string.h> + +int mystring::find_first(char ch, size_t offset) const +{ + if(offset >= rep->length) + return -1; + char* ptr = strchr(rep->buf+offset, ch); + return ptr ? ptr-rep->buf : -1; +} + diff --git a/lib/mystring/find_first_of.cc b/lib/mystring/find_first_of.cc new file mode 100644 index 0000000..f23e7f4 --- /dev/null +++ b/lib/mystring/find_first_of.cc @@ -0,0 +1,22 @@ +#include "mystring.h" +#include <string.h> + +int mystring::find_first_of(const char* setstr, size_t setlen, + size_t offset) const +{ + for(; offset < rep->length; offset++) { + if(memchr(setstr, rep->buf[offset], setlen)) + return offset; + } + return -1; +} + +int mystring::find_first_of(const char* setstr, size_t offset) const +{ + return find_first_of(setstr, strlen(setstr), offset); +} + +int mystring::find_first_of(const mystring& setstr, size_t offset) const +{ + return find_first_of(setstr.rep->buf, setstr.rep->length, offset); +} diff --git a/lib/mystring/find_last_ch.cc b/lib/mystring/find_last_ch.cc new file mode 100644 index 0000000..20d620a --- /dev/null +++ b/lib/mystring/find_last_ch.cc @@ -0,0 +1,14 @@ +#include "mystring.h" + +int mystring::find_last(char ch, size_t offset) const +{ + if(offset == (size_t)-1) + offset = rep->length-1; + const char* ptr = rep->buf + offset; + while(ptr >= rep->buf) { + if(*ptr == ch) + return ptr - rep->buf; + --ptr; + } + return -1; +} diff --git a/lib/mystring/find_last_of.cc b/lib/mystring/find_last_of.cc new file mode 100644 index 0000000..b8f8831 --- /dev/null +++ b/lib/mystring/find_last_of.cc @@ -0,0 +1,24 @@ +#include "mystring.h" +#include <string.h> + +int mystring::find_last_of(const char* setstr, size_t setlen, + size_t offset) const +{ + if(offset == (size_t)-1) + offset = rep->length-1; + for(int i = offset; i >= 0; --i) { + if(memchr(setstr, rep->buf[i], setlen)) + return i; + } + return -1; +} + +int mystring::find_last_of(const char* setstr, size_t offset) const +{ + return find_last_of(setstr, strlen(setstr), offset); +} + +int mystring::find_last_of(const mystring& setstr, size_t offset) const +{ + return find_last_of(setstr.rep->buf, setstr.rep->length, offset); +} diff --git a/lib/mystring/iter.cc b/lib/mystring/iter.cc new file mode 100644 index 0000000..3a84d1c --- /dev/null +++ b/lib/mystring/iter.cc @@ -0,0 +1,48 @@ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "mystring.h" + +mystring_iter::mystring_iter(const mystring& s, char e) + : str(s), sep(e), pos(0) +{ + advance(); +} + +mystring_iter::~mystring_iter() +{ +} + +void mystring_iter::advance() +{ + if(pos == -1) + return; + int i = str.find_first(sep, pos); + if(i == -1) { + if(pos >= 0 && pos < (int)str.length()) { + part = str.right(pos); + pos = str.length(); + } + else { + part = ""; + pos = -1; + } + } + else { + part = str.sub(pos, i-pos); + pos = i + 1; + } +} diff --git a/lib/mystring/iter.h b/lib/mystring/iter.h new file mode 100644 index 0000000..308c854 --- /dev/null +++ b/lib/mystring/iter.h @@ -0,0 +1,39 @@ +/* $Id: iter.h 616 2005-08-19 20:11:01Z bruce $ */ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef MYSTRING__ITER__H__ +#define MYSTRING__ITER__H__ + +class mystring_iter +{ + const mystring str; + const char sep; + int pos; + mystring part; + + void advance(); +public: + mystring_iter(const mystring&, char = '\0'); + ~mystring_iter(); + + operator bool() const { return pos >= 0; } + bool operator!() const { return pos < 0; } + mystring operator*() const { return part; } + void operator++() { advance(); } +}; + +#endif diff --git a/lib/mystring/join.cc b/lib/mystring/join.cc new file mode 100644 index 0000000..435765f --- /dev/null +++ b/lib/mystring/join.cc @@ -0,0 +1,61 @@ +#include "mystring.h" +#include <string.h> + +// This "join" class relies on one fairly obscure detail in the C++ +// standard: temporaries are destructed only after the entire +// "full-expression" has completed. That is, if the sequence: +// f(f(f(x))) creates three temporary objects, the inner objects are +// destroyed only after the execution has completed. This allows us +// to build a complete linked-list on the stack. Tricky, but efficient! + +struct tmpitem +{ + const char* str; + unsigned len; +}; + +mystringrep* mystringjoin::traverse() const +{ + // At first glance, a recursive implementation would be the most logical + // way of doing this, but it turned out to be a significant loss. This + // method traverses the pointer chain to first determine the length, and + // then to do the actual copying. + + // Note the use of do/while loops to avoid a test at the head of the loop + // which will always succeed (there is always at least one node or item). + unsigned count = 0; + const mystringjoin* node = this; + do { + ++count; + } while((node = node->prev) != 0); + + // The use of a temporary array avoids re-traversing the pointer + // chain, which is a slight performance win. + tmpitem items[count]; + + unsigned length = 0; + node = this; + tmpitem* item = items; + do { + unsigned l = node->rep ? node->rep->length : strlen(node->str); + length += l; + item->str = node->str; + item->len = l; + ++item; + } while((node = node->prev) != 0); + + // Since the chain is constructed such that the last item is the + // first node, the string gets constructed in reverse order. + mystringrep* rep = mystringrep::alloc(length); + char* buf = rep->buf + length; + item = items; + do { + unsigned l = item->len; + buf -= l; + memcpy(buf, item->str, l); + ++item; + } while(--count != 0); + + rep->buf[length] = 0; + return rep; +} diff --git a/lib/mystring/join.h b/lib/mystring/join.h new file mode 100644 index 0000000..0c73176 --- /dev/null +++ b/lib/mystring/join.h @@ -0,0 +1,76 @@ +/* $Id: join.h 616 2005-08-19 20:11:01Z bruce $ */ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef MYSTRING__JOIN__H__ +#define MYSTRING__JOIN__H__ + +class mystringjoin +{ +private: + const mystringjoin* prev; + mystringrep* rep; + const char* str; + + mystringjoin(); +public: + mystringjoin(const mystringjoin& j) + : prev(j.prev), rep(j.rep), str(j.str) + { + rep->attach(); + } + mystringjoin(const mystring& s) + : prev(0), rep(s.rep), str(s.rep->buf) + { + rep->attach(); + } + mystringjoin(const char* s) + : prev(0), rep(0), str(s) + { + } + mystringjoin(const mystringjoin& p, const mystring& s) + : prev(&p), rep(s.rep), str(s.rep->buf) + { + rep->attach(); + } + mystringjoin(const mystringjoin& p, const char* s) + : prev(&p), rep(0), str(s) + { + } + ~mystringjoin() + { + if(rep) rep->detach(); + } + mystringrep* traverse() const; +}; + +inline mystring::mystring(const mystringjoin& j) + : rep(j.traverse()) +{ + rep->attach(); +} + +inline mystringjoin operator+(const mystringjoin& a, const mystring& b) +{ + return mystringjoin(a, b); +} + +inline mystringjoin operator+(const mystringjoin& a, const char* b) +{ + return mystringjoin(a, b); +} + +#endif diff --git a/lib/mystring/lower.cc b/lib/mystring/lower.cc new file mode 100644 index 0000000..b51b51d --- /dev/null +++ b/lib/mystring/lower.cc @@ -0,0 +1,19 @@ +#include "mystring.h" +#include <ctype.h> + +mystring mystring::lower() const +{ + const unsigned length = rep->length; + char buf[length+1]; + const char* in = rep->buf + length; + bool changed = false; + for(char* out = buf+length; out >= buf; in--, out--) + if(isupper(*in)) + *out = tolower(*in), changed = true; + else + *out = *in; + if(!changed) + return *this; + else + return mystring(buf, length); +} diff --git a/lib/mystring/lstrip.cc b/lib/mystring/lstrip.cc new file mode 100644 index 0000000..66ee2d0 --- /dev/null +++ b/lib/mystring/lstrip.cc @@ -0,0 +1,10 @@ +#include "mystring.h" +#include <ctype.h> + +mystring mystring::lstrip() const +{ + const char* ptr = rep->buf; + while(*ptr && isspace(*ptr)) + ++ptr; + return ptr; +} diff --git a/lib/mystring/mystring.cc b/lib/mystring/mystring.cc new file mode 100644 index 0000000..c04bf2f --- /dev/null +++ b/lib/mystring/mystring.cc @@ -0,0 +1,28 @@ +#include "mystring.h" +#include "trace.h" +#include <ctype.h> +#include <string.h> + +#ifdef MYSTRING_TRACE +mystring::~mystring() +{ + trace("rep=" << (void*)rep); + rep->detach(); +} +#endif + +int mystring::operator!=(const char* in) const +{ + if(rep->buf == in) + return 0; + return strcmp(rep->buf, in); +} + +int mystring::operator!=(const mystring& in) const +{ + if(rep->buf == in.rep->buf) + return 0; + return strcmp(rep->buf, in.rep->buf); +} + +const mystring mystring::NUL("", 1); diff --git a/lib/mystring/mystring.h b/lib/mystring/mystring.h new file mode 100644 index 0000000..1752772 --- /dev/null +++ b/lib/mystring/mystring.h @@ -0,0 +1,131 @@ +/* $Id: mystring.h 635 2005-11-02 17:37:50Z bruce $ */ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef MYSTRING__H__ +#define MYSTRING__H__ + +#include <sys/types.h> +#include "mystring/rep.h" + +class mystringjoin; +class mystring +{ + friend class mystringtmp; + friend class mystringjoin; +private: + mystringrep* rep; + +protected: + void dupnil(); + void dup(const char*, size_t); + void dup(const char*); + void assign(const char*); + void assign(const char*, size_t); +public: + static const mystring NUL; + + mystring() { dupnil(); } + mystring(const char* s) { dup(s); } + mystring(const mystring& s) { dup(s.rep->buf, s.rep->length); } + mystring(const char* str, size_t len) { dup(str, len); } + mystring(const mystringjoin&); + ~mystring(); + + const char* c_str() const { return rep->buf; } + + bool operator!() const { return empty(); } + + char operator[](size_t i) const { return rep->buf[i]; } + + size_t length() const { return rep->length; } + + bool empty() const { return rep->length == 0; } + + int operator!=(const char* in) const; + int operator!=(const mystring& in) const; + bool operator==(const char* in) const + { + return !operator!=(in); + } + bool operator==(const mystring& in) const + { + return !operator!=(in); + } + + void operator=(const char* in) { assign(in); } + void operator=(const mystring& in) { assign(in.rep->buf, in.rep->length); } + void operator=(const mystringjoin& in); + + mystring subst(char from, char to) const; + + mystring lower() const; + mystring upper() const; + + bool starts_with(const mystring&) const; + bool starts_with(const char*) const; + bool starts_with(const char*, size_t) const; + + int find_first(char, size_t = 0) const; + int find_first_of(const mystring&, size_t = 0) const; + int find_first_of(const char*, size_t = 0) const; + int find_first_of(const char*, size_t, size_t) const; + + int find_last(char, size_t = (size_t)-1) const; + int find_last_of(const mystring&, size_t = (size_t)-1) const; + int find_last_of(const char*, size_t = 0) const; + int find_last_of(const char*, size_t, size_t) const; + + mystring left(size_t) const; + mystring right(size_t) const; + mystring sub(size_t, size_t) const; + + mystring lstrip() const; + mystring rstrip() const; + mystring strip() const; + + unsigned count(char ch) const; + + void append(const char*); + void append(const char*, size_t); + + void operator+=(const mystring& str) {append(str.rep->buf, str.rep->length);} + void operator+=(const char* str) { append(str); } + void operator+=(char ch) + { + char str[2] = { ch, 0 }; + append(str, 1); + } +}; + +#ifndef MYSTRING_TRACE +inline mystring::~mystring() +{ + rep->detach(); +} +#endif + +#include "mystring/iter.h" +#include "mystring/join.h" + +class fdobuf; +fdobuf& operator<<(fdobuf& out, const mystring& str); + +//istream& operator>>(istream& in, mystring& str); + +typedef mystring string; + +#endif diff --git a/lib/mystring/rep.cc b/lib/mystring/rep.cc new file mode 100644 index 0000000..bc939d7 --- /dev/null +++ b/lib/mystring/rep.cc @@ -0,0 +1,157 @@ +#include "mystring.h" +#include "trace.h" +#include <ctype.h> +#include <string.h> + +mystringrep nil = { 0, 1, 1, "" }; + +static const unsigned replength = sizeof(unsigned)*3; + +static const unsigned sizestep = sizeof(unsigned); +static const unsigned slackdiv = 4; +static const unsigned slackmax = 16; + +#ifdef MYSTRINGREP_STATS + +#include "fdbuf.h" + +struct _rep_stats +{ + unsigned allocs; + unsigned alloc_size; + unsigned alloc_len; + + unsigned appends; + unsigned appends_dup; + + _rep_stats() + : allocs(0) + { + } + + void stat(const char* name, unsigned value) + { + ferr << "mystringrep: " << name << ": " << value << '\n'; + } + void pcnt(const char* name, unsigned denom, unsigned divis) + { + ferr << "mystringrep: " << name << ": " + << denom << '/' << divis << '='; + if(divis) ferr << denom * 100 / divis << '%'; + else ferr << "N/A"; + ferr << '\n'; + } + + ~_rep_stats() + { + stat(" size step", sizestep); + stat(" slack divisor", slackdiv); + stat(" slack maximum", slackmax); + stat(" allocs", allocs); + stat(" alloc length", alloc_len); + stat(" alloc size", alloc_size); + pcnt(" alloc slack", alloc_size-alloc_len, alloc_len); + stat("alloc overhead", allocs*replength); + pcnt(" appends->dup", appends_dup, appends); + } +}; + +static _rep_stats stats; + +#define ACCOUNT(NAME,VALUE) stats. NAME += VALUE + +#else // MYSTRINGREP_STATS + +#define ACCOUNT(NAME,VALUE) + +#endif // MYSTRINGREP_STATS + +/////////////////////////////////////////////////////////////////////////////// +// class mystringrep +/////////////////////////////////////////////////////////////////////////////// +mystringrep* mystringrep::alloc(unsigned length) +{ + ACCOUNT(allocs, 1); + trace_static("length=" << length); + if(length == 0) + return &nil; + + ACCOUNT(alloc_len, length); + unsigned slack = length / slackdiv; + if(slack > slackmax) + slack = slackmax; + unsigned size = length+1 + sizestep-1 + slack; + size = size - size % sizestep; + ACCOUNT(alloc_size, size); + + mystringrep* ptr = (mystringrep*)new char[size+replength]; + ptr->length = length; + ptr->references = 0; + ptr->size = size; + return ptr; +} + +mystringrep* mystringrep::dup(const char* str, unsigned length) +{ + trace_static("str=" << (void*)str << " length=" << length); + if(length == 0) + return &nil; + mystringrep* ptr = alloc(length); + memcpy(ptr->buf, str, length); + ptr->buf[length] = 0; + return ptr; +} + +mystringrep* mystringrep::dup(const char* str1, unsigned length1, + const char* str2, unsigned length2) +{ + trace_static(""); + if(length1+length2 == 0) + return &nil; + mystringrep* ptr = alloc(length1+length2); + memcpy(ptr->buf, str1, length1); + memcpy(ptr->buf+length1, str2, length2); + ptr->buf[length1+length2] = 0; + return ptr; +} + +mystringrep* mystringrep::append(const char* str, unsigned len) +{ + ACCOUNT(appends, 1); + unsigned newlen = length + len; + // If there are more than one references, always make a duplicate + // Also, if this does not have enough space to add the new string, dup it + if(references > 1 || newlen >= size) { + ACCOUNT(appends_dup, 1); + mystringrep* tmp = dup(buf, length, str, len); + tmp->attach(); + detach(); + return tmp; + } + // Otherwise, just add the new string to the end of this + else { + memcpy(buf+length, str, len); + buf[newlen] = 0; + length = newlen; + return this; + } +} + +#ifdef MYSTRING_TRACE +void mystringrep::attach() +{ + trace("references=" << references); + ++references; +} +#endif + +void mystringrep::detach() +{ + trace("references=" << references); + + --references; + if(!references) { + trace("deleting this"); + delete this; + } +} diff --git a/lib/mystring/rep.h b/lib/mystring/rep.h new file mode 100644 index 0000000..4ca0bff --- /dev/null +++ b/lib/mystring/rep.h @@ -0,0 +1,47 @@ +/* $Id: rep.h 616 2005-08-19 20:11:01Z bruce $ */ +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef MYSTRING__REP__H__ +#define MYSTRING__REP__H__ + +struct mystringrep +{ + unsigned length; + unsigned references; + unsigned size; + char buf[1]; + + void attach(); + void detach(); + mystringrep* append(const char*, unsigned); + + static mystringrep* alloc(unsigned); + static mystringrep* dup(const char*, unsigned); + static mystringrep* dup(const char*, unsigned, + const char*, unsigned); +}; + +#ifndef MYSTRING_TRACE +inline void mystringrep::attach() +{ + references++; +} +#endif + +extern mystringrep nil; + +#endif diff --git a/lib/mystring/rstrip.cc b/lib/mystring/rstrip.cc new file mode 100644 index 0000000..b66f8ce --- /dev/null +++ b/lib/mystring/rstrip.cc @@ -0,0 +1,10 @@ +#include "mystring.h" +#include <ctype.h> + +mystring mystring::rstrip() const +{ + const char* ptr = rep->buf + rep->length - 1; + while(ptr >= rep->buf && isspace(*ptr)) + --ptr; + return mystring(rep->buf, ptr-rep->buf+1); +} diff --git a/lib/mystring/starts_with.cc b/lib/mystring/starts_with.cc new file mode 100644 index 0000000..180fcbe --- /dev/null +++ b/lib/mystring/starts_with.cc @@ -0,0 +1,17 @@ +#include "mystring.h" +#include <string.h> + +bool mystring::starts_with(const char* that, size_t len) const +{ + return len <= rep->length && memcmp(that, rep->buf, len) == 0; +} + +bool mystring::starts_with(const char* that) const +{ + return starts_with(that, strlen(that)); +} + +bool mystring::starts_with(const mystring& that) const +{ + return starts_with(that.rep->buf, that.rep->length); +} diff --git a/lib/mystring/strip.cc b/lib/mystring/strip.cc new file mode 100644 index 0000000..7ee251f --- /dev/null +++ b/lib/mystring/strip.cc @@ -0,0 +1,13 @@ +#include "mystring.h" +#include <ctype.h> + +mystring mystring::strip() const +{ + const char* start = rep->buf; + while(*start && isspace(*start)) + ++start; + const char* end = rep->buf + rep->length - 1; + while(end >= start && isspace(*end)) + --end; + return mystring(start, end-start+1); +} diff --git a/lib/mystring/sub.cc b/lib/mystring/sub.cc new file mode 100644 index 0000000..c0918db --- /dev/null +++ b/lib/mystring/sub.cc @@ -0,0 +1,37 @@ +#include "mystring.h" + +// return the sub-string ending at 'offset' +mystring mystring::left(size_t offset) const +{ + if(offset > rep->length) + return *this; + else + return mystring(rep->buf, offset); +} + +// return the sub-string starting at 'offset' +mystring mystring::right(size_t offset) const +{ + if(offset >= rep->length) + return mystring(); + else if(offset == 0) + return *this; + else + return mystring(rep->buf+offset, rep->length-offset); +} + +// return the 'len' characters of the string starting at 'offset' +mystring mystring::sub(size_t offset, size_t len) const +{ + // return right(offset).left(len); + if(len == 0) + return mystring(); + else if(offset == 0 && len >= rep->length) + return *this; + else { + if(len+offset >= rep->length) + len = rep->length - offset; + return mystring(rep->buf+offset, len); + } +} + diff --git a/lib/mystring/subst.cc b/lib/mystring/subst.cc new file mode 100644 index 0000000..f5172cc --- /dev/null +++ b/lib/mystring/subst.cc @@ -0,0 +1,18 @@ +#include "mystring.h" + +mystring mystring::subst(char from, char to) const +{ + const unsigned length = rep->length; + char buf[length+1]; + const char* in = rep->buf + length; + bool changed = true; + for(char* out = buf+length; out >= buf; in--, out--) + if(*in == from) + *out = to, changed = true; + else + *out = *in; + if(!changed) + return *this; + else + return mystring(buf, length); +} diff --git a/lib/mystring/trace.h b/lib/mystring/trace.h new file mode 100644 index 0000000..d6942a7 --- /dev/null +++ b/lib/mystring/trace.h @@ -0,0 +1,11 @@ +/* $Id: trace.h 616 2005-08-19 20:11:01Z bruce $ */ +#include "mystring.h" + +#ifdef MYSTRING_TRACE +ostream& operator<<(ostream& out, const mystringtmp& s); +#define trace(X) cerr << (void*)this << "->" << __PRETTY_FUNCTION__ << X << endl +#define trace_static(X) cerr << __PRETTY_FUNCTION__ << X << endl +#else +#define trace(X) do { } while(0) +#define trace_static(X) do { } while(0) +#endif diff --git a/lib/mystring/upper.cc b/lib/mystring/upper.cc new file mode 100644 index 0000000..5015770 --- /dev/null +++ b/lib/mystring/upper.cc @@ -0,0 +1,21 @@ +#include "mystring.h" +#include <ctype.h> + +mystring mystring::upper() const +{ + const unsigned length = rep->length; + char buf[length+1]; + const char* in = rep->buf + length; + bool changed = false; + for(char* out = buf+length; out >= buf; in--, out--) + if(islower(*in)) { + *out = toupper(*in); + changed = true; + } + else + *out = *in; + if(!changed) + return *this; + else + return mystring(buf, length); +} diff --git a/lib/netstring.cc b/lib/netstring.cc new file mode 100644 index 0000000..784d941 --- /dev/null +++ b/lib/netstring.cc @@ -0,0 +1,13 @@ +#include "config.h" +#include "netstring.h" +#include "itoa.h" + +mystring str2net(const mystring& s) +{ + return mystringjoin(itoa(s.length())) + ":" + s + ","; +} + +mystring strnl2net(const mystring& s) +{ + return mystringjoin(itoa(s.length()+1)) + ":" + s + "\012,"; +} diff --git a/lib/netstring.h b/lib/netstring.h new file mode 100644 index 0000000..d5a1a19 --- /dev/null +++ b/lib/netstring.h @@ -0,0 +1,8 @@ +#ifndef NULLMAILER__NETSTRING__H__ +#define NULLMAILER__NETSTRING__H__ + +#include "mystring/mystring.h" +mystring str2net(const mystring&); +mystring strnl2net(const mystring&); + +#endif // NULLMAILER__NETSTRING__H__ diff --git a/lib/selfpipe.cc b/lib/selfpipe.cc new file mode 100644 index 0000000..83b390c --- /dev/null +++ b/lib/selfpipe.cc @@ -0,0 +1,104 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include "selfpipe.h" + +static int fds[2] = { -1, -1 }; + +static int fcntl_fl_on(int fd, int flag) +{ + int flags; + int newflags; + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) + return 0; + if ((newflags = flags | flag) != flags) + if (fcntl(fd, F_SETFL, newflags)) + return 0; + return 1; +} + +selfpipe::selfpipe() +{ + if (fds[0] < 0) { + if (pipe(fds) != -1) { + if (fcntl_fl_on(fds[0], O_NONBLOCK) + && fcntl_fl_on(fds[1], O_NONBLOCK) + && fcntl_fl_on(fds[1], FD_CLOEXEC) + && fcntl_fl_on(fds[1], FD_CLOEXEC)) + return; + + close(fds[0]); + close(fds[1]); + } + fds[0] = fds[1] = -1; + } +} + +selfpipe::operator bool() const +{ + return fds[0] >= 0; +} + +static void catcher(int sig) +{ + signal(sig, catcher); + write(fds[1], &sig, sizeof sig); +} + +void selfpipe::catchsig(int sig) +{ + signal(sig, catcher); +} + +int selfpipe::caught() +{ + int buf; + if (read(fds[0], &buf, sizeof buf) == sizeof buf) + return buf; + return 0; +} + +int selfpipe::waitsig(int timeout) +{ + if (timeout > 0) { + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(fds[0], &fdset); + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + int s; + while ((s = select(fds[0] + 1, &fdset, 0, 0, + (timeout <= 0) ? 0 : &tv)) == -1) { + if (errno != EINTR) + return -1; + } + if (s != 1) + return 0; + } + return caught(); +} diff --git a/lib/selfpipe.h b/lib/selfpipe.h new file mode 100644 index 0000000..1ad2136 --- /dev/null +++ b/lib/selfpipe.h @@ -0,0 +1,16 @@ +#ifndef NULLMAILER_SELFPIPE__H__ +#define NULLMAILER_SELFPIPE__H__ + +class selfpipe +{ + public: + selfpipe(); + + operator bool() const; + + void catchsig(int sig); + int caught(); + int waitsig(int timeout = 0); +}; + +#endif // NULLMAILER_SELFPIPE__H__ diff --git a/lib/setenv.cc b/lib/setenv.cc new file mode 100644 index 0000000..6fcfeb0 --- /dev/null +++ b/lib/setenv.cc @@ -0,0 +1,41 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include "setenv.h" + +#ifndef HAVE_SETENV +// This is not really a full emulation of setenv, but close enough +int setenv(const char* var, const char* val, int overwrite) +{ + size_t varlen = strlen(var); + size_t vallen = strlen(val); + char* str = (char*)malloc(varlen+vallen+2); + if (str == 0) return -1; + memcpy(str, var, varlen); + str[varlen] = '='; + memcpy(str+varlen+1, val, vallen); + str[varlen+vallen+1] = 0; + return putenv(str); +} +#endif diff --git a/lib/setenv.h b/lib/setenv.h new file mode 100644 index 0000000..771ee27 --- /dev/null +++ b/lib/setenv.h @@ -0,0 +1,6 @@ +#ifndef NULLMAILER__SETENV__H__ +#define NULLMAILER__SETENV__H__ + +extern "C" int setenv(const char*, const char*, int); + +#endif diff --git a/lib/tcpconnect.cc b/lib/tcpconnect.cc new file mode 100644 index 0000000..d4cf9a9 --- /dev/null +++ b/lib/tcpconnect.cc @@ -0,0 +1,175 @@ +// nullmailer -- a simple relay-only MTA +// Copyright (C) 2017 Bruce Guenter <bruce@untroubled.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// 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 +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact me at <bruce@untroubled.org>. There is also a mailing list +// available to discuss this package. To subscribe, send an email to +// <nullmailer-subscribe@lists.untroubled.org>. + +#include "config.h" +#include <errno.h> +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +#include "errcodes.h" +#include "itoa.h" +#include "connect.h" + +static int err_return(int errn, int dflt) +{ + if (errn == HOST_NOT_FOUND) + return -ERR_HOST_NOT_FOUND; + if (errn == NO_ADDRESS) + return -ERR_NO_ADDRESS; + if (errn == NO_RECOVERY || errn == EAI_FAIL) + return -ERR_GHBN_FATAL; + if (errn == TRY_AGAIN || errn == EAI_AGAIN) + return -ERR_GHBN_TEMP; + if (errn == EAI_NONAME) + return -ERR_HOST_NOT_FOUND; + if (errn == ECONNREFUSED) + return -ERR_CONN_REFUSED; + if (errn == ETIMEDOUT) + return -ERR_CONN_TIMEDOUT; + if (errn == ENETUNREACH) + return -ERR_CONN_UNREACHABLE; + return -dflt; +} + +#ifdef HAVE_GETADDRINFO + +static int getaddr(const char* hostname, int port, struct addrinfo** result) +{ + const char *service = itoa(port, 6); + struct addrinfo req; + memset(&req, 0, sizeof(req)); + req.ai_flags = AI_NUMERICSERV; + req.ai_socktype = SOCK_STREAM; + int e = getaddrinfo(hostname, service, &req, result); + return e ? err_return(e, ERR_GHBN_TEMP) : 0; +} + +static bool canbind(int family, const struct addrinfo* ai) +{ + for (; ai; ai = ai->ai_next) + if (ai->ai_family == family) + return true; + return false; +} + +static bool bindit(int fd, int family, const struct addrinfo* ai) +{ + for (; ai; ai = ai->ai_next) + if (ai->ai_family == family) + if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0) + return true; + return false; +} + +int tcpconnect(const char* hostname, int port, const char* source) +{ + struct addrinfo* res; + int err = getaddr(hostname, port, &res); + if (err) + return err; + struct addrinfo* source_addr = NULL; + if (source) { + err = getaddr(source, 0, &source_addr); + if (err) + return err; + } + int s = -1; + err = ERR_CONN_FAILED; + struct addrinfo* orig_res = res; + + if (source_addr) + // Check if some address is the same family as the source + for (; res != NULL; res = res->ai_next) + if (canbind(res->ai_family, source_addr)) + break; + if (res == NULL) + return -ERR_BIND_FAILED; + + for (; res != NULL; res = res->ai_next) { + if (!source_addr || canbind(res->ai_family, source_addr)) { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if(s > 0) { + if(source_addr && !bindit(s, res->ai_family, source_addr)) { + close(s); + err = ERR_BIND_FAILED; + s = -1; + break; + } + if(connect(s, res->ai_addr, res->ai_addrlen) == 0) + break; + close(s); + s = -1; + } + } + } + + freeaddrinfo(orig_res); + if (source_addr) + freeaddrinfo(source_addr); + + if(s < 0) + return err_return(errno, err); + return s; +} + +#else + +static int sethostbyname(const char* hostname, struct sockaddr_in& sa) +{ + struct hostent *he = gethostbyname(hostname); + if(!he) + return err_return(h_errno, ERR_GHBN_TEMP); + memcpy(&sa.sin_addr, he->h_addr, he->h_length); + return 0; +} + +int tcpconnect(const char* hostname, int port, const char* source) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + int e = sethostbyname(hostname, sa); + if(e) return e; + struct sockaddr_in source_sa; + memset(&source_sa, 0, sizeof source_sa); + if(source) { + e = sethostbyname(source, source_sa); + if(e) return e; + } + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + int s = socket(PF_INET, SOCK_STREAM, 0); + if(s == -1) + return -ERR_SOCKET; + if(source && bind(s, (sockaddr*)&source_sa, sizeof source_sa) != 0) { + close(s); + return err_return(errno, ERR_BIND_FAILED); + } + if(connect(s, (sockaddr*)&sa, sizeof(sa)) != 0) { + close(s); + return err_return(errno, ERR_CONN_FAILED); + } + return s; +} + +#endif |