daemon-0.8.4/0000755000175000017500000000000014471644200011072 5ustar rafrafdaemon-0.8.4/Makefile0000644000175000017500000001345014471644177012552 0ustar rafraf# # daemon - https://libslack.org/daemon # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # CC := gcc # CC := cc # CC := other AR := ar RANLIB := ranlib POD2MAN := pod2man POD2HTML := pod2html GZIP := gzip -f -9 DESTDIR := PREFIX := /usr/local # PREFIX := /usr/pkg APP_INSDIR := $(PREFIX)/bin LIB_INSDIR := $(PREFIX)/lib MAN_SYSDIR := $(PREFIX)/share/man MAN_LOCDIR := $(PREFIX)/share/man ifeq ($(PREFIX),/usr) MAN_INSDIR := $(MAN_SYSDIR) else MAN_INSDIR := $(MAN_LOCDIR) endif HDR_INSDIR := $(PREFIX)/include DATA_INSDIR := $(PREFIX)/share CONF_INSDIR := /etc APP_MANSECT := 1 LIB_MANSECT := 3 FMT_MANSECT := 5 APP_MANDIR := $(MAN_INSDIR)/man$(APP_MANSECT) LIB_MANDIR := $(MAN_INSDIR)/man$(LIB_MANSECT) FMT_MANDIR := $(MAN_INSDIR)/man$(FMT_MANSECT) APP_MANSECTNAME := User Commands LIB_MANSECTNAME := C Library Functions - libslack FMT_MANSECTNAME := File Formats MAN_GZIP := 1 CCFLAGS += -O3 CCFLAGS += -Wall -pedantic # CCFLAGS += -xO4 CLEAN_FILES += tags core Makefile.bak .makefile.bak pod2htm* DAEMON_SRCDIR := . DAEMON_INCDIRS := libslack # Uncomment this for Linux systems without systemd but with elogind (e.g. Slackware) # DAEMON_INCDIRS += /usr/include/elogind DAEMON_LIBDIRS := libslack include $(DAEMON_SRCDIR)/macros.mk .PHONY: all ready test check man html install uninstall dist rpm deb sol obsd fbsd nbsd osx all: ready $(ALL_TARGETS) ready: $(READY_TARGETS) check test: all $(TEST_TARGETS) man: $(MAN_TARGETS) html: $(HTML_TARGETS) install: all $(INSTALL_TARGETS) uninstall: $(UNINSTALL_TARGETS) dist: $(DIST_TARGETS) rpm: $(RPM_TARGETS) deb: $(DEB_TARGETS) sol: $(SOL_TARGETS) obsd: $(OBSD_TARGETS) fbsd: $(FBSD_TARGETS) nbsd: $(NBSD_TARGETS) osx: $(OSX_TARGETS) .PHONY: help help-macros depend clean clobber distclean help:: @echo "This makefile provides the following targets."; \ echo; \ echo " help -- shows this list of targets"; \ echo " help-macros -- shows the values of all make macros"; \ echo " all -- makes $(DAEMON_TARGET) and $(DAEMON_SUBTARGETS) (default)"; \ echo " ready -- prepares the source directory for make"; \ echo " test -- makes and runs library unit tests"; \ echo " check -- same as test"; \ echo " man -- generates all manpages"; \ echo " html -- generates all manpages in html"; \ echo " install -- installs everything under $(PREFIX)"; \ echo " uninstall -- uninstalls everything from $(PREFIX)"; \ echo " depend -- generates source dependencies using makedepend"; \ echo " tags -- generates a tags file using ctags"; \ echo " clean -- removes output files, tags, tests, and de-configures"; \ echo " clobber -- same as clean"; \ echo " distclean -- same as clean"; \ echo " dist -- makes the distribution: ../$(DAEMON_DIST)"; \ echo " rpm -- makes source and binary rpm packages [OLD]"; \ echo " deb -- makes binary debian package [OLD]"; \ echo " sol -- makes binary solaris package [OLD]"; \ echo " obsd -- makes binary openbsd package [OLD]"; \ echo " fbsd -- makes binary freebsd package [OLD]"; \ echo " nbsd -- makes binary netbsd package [OLD]"; \ echo " osx -- makes binary macosx package [OLD]"; \ echo help-macros:: @echo "CC = $(CC)"; \ echo "PREFIX = $(PREFIX)"; \ echo "APP_INSDIR = $(APP_INSDIR)"; \ echo "LIB_INSDIR = $(LIB_INSDIR)"; \ echo "MAN_INSDIR = $(MAN_INSDIR)"; \ echo "HDR_INSDIR = $(HDR_INSDIR)"; \ echo "DATA_INSDIR = $(DATA_INSDIR)"; \ echo "CONF_INSDIR = $(CONF_INSDIR)"; \ echo "APP_MANSECT = $(APP_MANSECT)"; \ echo "LIB_MANSECT = $(LIB_MANSECT)"; \ echo "FMT_MANSECT = $(FMT_MANSECT)"; \ echo "APP_MANDIR = $(APP_MANDIR)"; \ echo "LIB_MANDIR = $(LIB_MANDIR)"; \ echo "FMT_MANDIR = $(FMT_MANDIR)"; \ echo "TAG_FILES = $(TAG_FILES)"; \ echo "DEPEND_CFILES = $(DEPEND_CFILES)"; \ echo "DEPEND_HFILES = $(DEPEND_HFILES)"; \ echo "CCFLAGS = $(CCFLAGS)"; \ echo "READY_TARGETS = $(READY_TARGETS)"; \ echo "ALL_TARGETS = $(ALL_TARGETS)"; \ echo "TEST_TARGETS = $(TEST_TARGETS)"; \ echo "MAN_TARGETS = $(MAN_TARGETS)"; \ echo "HTML_TARGETS = $(HTML_TARGETS)"; \ echo "INSTALL_TARGETS = $(INSTALL_TARGETS)"; \ echo "UNINSTALL_TARGETS = $(UNINSTALL_TARGETS)"; \ echo "CLEAN_FILES = $(CLEAN_FILES)"; \ echo "CLOBBER_FILES = $(CLOBBER_FILES)"; \ echo "DIST_TARGETS = $(DIST_TARGETS)"; \ echo "RPM_TARGETS = $(RPM_TARGETS)"; \ echo "DEB_TARGETS = $(DEB_TARGETS)"; \ echo "SOL_TARGETS = $(SOL_TARGETS)"; \ echo "OBSD_TARGETS = $(OBSD_TARGETS)"; \ echo "FBSD_TARGETS = $(FBSD_TARGETS)"; \ echo "NBSD_TARGETS = $(NBSD_TARGETS)"; \ echo "OSX_TARGETS = $(OSX_TARGETS)"; \ echo tags: $(TAG_FILES) @ctags $(TAG_FILES) depend: ready $(DEPEND_CFILES) $(DEPEND_HFILES) @makedepend $(DAEMON_CPPFLAGS) $(DEPEND_CFILES) clean:: rm -rf $(CLEAN_FILES) $(CLOBBER_FILES) perl -pi -e 'last if /[D]O NOT DELETE/;' $(patsubst %, %/Makefile, $(DAEMON_SRCDIR) $(DAEMON_SUBDIRS)) ./configure --default clobber:: clean distclean:: clean include $(DAEMON_SRCDIR)/rules.mk daemon-0.8.4/example/0000755000175000017500000000000014471543700012530 5ustar rafrafdaemon-0.8.4/example/daemon.initd0000755000175000017500000000707014471543700015033 0ustar rafraf#!/bin/sh # # daemon - https://libslack.org/daemon # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # This is an example /etc/init.d script that shows how to use daemon(1) # in that context. Note that this would need to be modified quite a bit # to meet the usual conventions of any specific system but if you aren't # concerned about that it should be usable. At least it's a starting point. # The daemon's name (to ensure uniqueness and for stop, restart and status) name="EXAMPLE" # The path of the client executable command="/usr/bin/EXAMPLE" # Any command line arguments for the client executable command_args="" # The path of the daemon executable daemon="/usr/bin/daemon" [ -x "$daemon" ] || exit 0 [ -x "$command" ] || exit 0 # Note: The following daemon option arguments could be in /etc/daemon.conf # instead. That would probably be better because if the command itself were # there as well then we could just use the name here to start the daemon. # Here's some code to do it here in case you prefer that. # Any command line arguments for the daemon executable (when starting) daemon_start_args="" # e.g. --inherit --env="ENV=VAR" --unsafe # The pidfile directory (need to force this so status works for normal users) pidfiles="/var/run" # The user[:group] to run as (if not to be run as root) user="" # The path to chroot to (otherwise /) chroot="" # The path to chdir to (otherwise /) chdir="" # The umask to adopt, if any umask="" # The syslog facility or filename for the client's stdout (otherwise discarded) stdout="daemon.info" # The syslog facility or filename for the client's stderr (otherwise discarded) stderr="daemon.err" case "$1" in start) # This if statement isn't strictly necessary but it's user friendly if "$daemon" --running --name "$name" --pidfiles "$pidfiles" then echo "$name is already running." else echo -n "Starting $name..." "$daemon" --respawn $daemon_start_args \ --name "$name" --pidfiles "$pidfiles" \ ${user:+--user $user} ${chroot:+--chroot $chroot} \ ${chdir:+--chdir $chdir} ${umask:+--umask $umask} \ ${stdout:+--stdout $stdout} ${stderr:+--stderr $stderr} \ -- \ "$command" $command_args echo done. fi ;; stop) # This if statement isn't strictly necessary but it's user friendly if "$daemon" --running --name "$name" --pidfiles "$pidfiles" then echo -n "Stopping $name..." "$daemon" --stop --name "$name" --pidfiles "$pidfiles" echo done. else echo "$name is not running." fi ;; restart|reload) if "$daemon" --running --name "$name" --pidfiles "$pidfiles" then echo -n "Restarting $name..." "$daemon" --restart --name "$name" --pidfiles "$pidfiles" echo done. else echo "$name is not running." exit 1 fi ;; status) "$daemon" --running --name "$name" --pidfiles "$pidfiles" --verbose ;; *) echo "usage: $0 " >&2 exit 1 esac exit 0 # vi:set ts=4 sw=4: daemon-0.8.4/INSTALL0000644000175000017500000002413114471543635012136 0ustar rafrafINSTALL ======= This version is only known to work on the following systems: Linux 3.x/4.x/... (i386, x86_64, debian-{7,8,9,10,...}, ubuntu-{14.04,16.04}, ...) Linux 2.6 (i386, x86_64, debian-5.0.4, ubuntu-10.04, fedora-13) Solaris 10 10/09 (i386, amd64), 11 (amd64) OpenSolaris 2009/06 (i386, amd64) OpenBSD 4.7 (i386, amd64), 6.6 (amd64) FreeBSD 8.0 (i386, amd64), 12.1 (amd64) NetBSD 5.0.2 (i386, amd64), 8.1 (amd64) MacOSX/macOS 10.{4,5,6,11,14,15}, 11 (ppc, i386, x86_64, arm64) kFreeBSD 20090729 (i386) GNU/Hurd (i386) For these systems (or later), just run the "configure" script in the source directory. It will run the appropriate script in the "conf" directory for the current host. Perl and GNU make are required for building. Note: There isn't a real configure script so you will no doubt encounter problems on other systems. An ISO C and POSIX/XPG4 environment will help greatly. If your system doesn't have snprintf(3), GNU getopt_long(3), vsscanf(3), strcasecmp(3), strncasecmp(3), strlcpy(3) or strlcat(3), uncomment the relevant lines in the libslack/config.h file to include them in libslack. If your system doesn't have POSIX 1003.2 compliant regex functions, or they are buggy, either: install the GNU implementation, ftp://ftp.gnu.org/gnu/regex/regex-0.12.tar.gz [290K] (doesn't support internationalisation); or install Henry Spencer's implementation, ftp://ftp.zoo.toronto.edu/pub/regex.shar [157K]. If you really, really, really don't want the regular expression functions, uncomment HAVE_REGEX_H in libslack/config.h to enable the rest of the str module to be compiled. If you have a linux-2.2.x system, you must have LinuxThreads-0.8 or LinuxThreads-0.9 and the latest corresponding version of glibc. If you have a linux-2.4.x system, you must have at least glibc-2.2.1 and glibc-linuxthreads-2.2.1. They are available from http://ftp.gnu.org/pub/gnu/glibc/. Older versions can be used but they have some very nasty bugs. First, uninstall any previous version (optional): cd /usr/local/src/daemon-0.8.3 make uninstall To build and test: tar xzf daemon-0.8.4.tar.gz cd daemon-0.8.4 ./configure # iff linux, openbsd, freebsd, netbsd, macosx, solaris, gnuhurd or kfreebsd make # must be gnu make make test # only tests libslack. to test daemon, see test/README Note that the configure script is not a GNU autoconf script. It just has hard-coded settings for certain platforms. It does not accept all of the usual command line options. However, it does accept certain options: --help Print a help message showing these options, then exit. --prefix=/path Changes the installation prefix to a non-standard one (needed for macports). This also changes the directory that the config file is installed in. When --prefix=/usr or --prefix=default, the config file is installed in /etc. Otherwise (e.g. --prefix=/usr/local or --prefix=/opt/local), the config file is installed under the prefix. --destdir=/path Changes the makefile $(DESTDIR) for building packages. It doesn't affect any paths in the final binary. --enable-logind On Linux systems where libsystemd or libelogind headers and libraries are installed, this option will use them for daemon's --bind option. It isn't enabled by default. --disable-logind This option undoes the effects of --enable-logind. --enable-mail-test This option enables testing libslack's mail() function (default). --disable-mail-test This option suppresses testing libslack's mail() function which tries to send an email via the localhost's mail server on port 25 (if any). This is useful when building packages. --enable-test Includes backup implementations of several functions (unnecessary). --default This option restores everything to its default state. It is used by "make dist". --platform=platform This option overrides the current platform. It is used by "./configure --default" and "make dist". See "./configure --help" for possible values. To install daemon and its manpage (in /usr/local by default): make install To install into somewhere other than /usr/local: make PREFIX=/opt/daemon install To install an empty /etc/daemon.conf file and /etc/daemon.conf.d directory: make install-daemon-conf To uninstall daemon: make uninstall To install libslack and its manpages (into /usr/local by default): make install-slack To uninstall libslack: make uninstall-slack For more details: ./configure --help make help The manpage for daemon is daemon(1). There is one manpage for each module in libslack (as well as a symlink for each function). The module manpages are agent(3), coproc(3), daemon(3), err(3), fio(3), hsort(3), lim(3), link(3), list(3), locker(3), map(3), mem(3), msg(3), net(3), prog(3), prop(3), pseudo(3), sig(3) and str(3). If necessary, the manpages getopt(3), snprintf(3) and vsscanf(3) are created as well. PACKAGING SYSTEMS ================= Daemon can be installed and kept up-to-date more conveniently on some systems via their packaging systems. To install on Debian/Ubuntu Linux systems: apt install daemon To install on macOS systems with MacPorts: port install daemon To install on macOS or Linux systems with Homebrew: brew install daemon # homebrew/core version Or: brew install rafpkg/tap/daemon # my version To install on Arch Linux systems with yay: yay daemon To install on Slackware Linux systems: upgradepkg --install-new daemon-0.8.4-i586-1.txz upgradepkg --install-new daemon-0.8.4-x86_64-1.txz There might be others. BINARY PACKAGES =============== Binary packages are no longer available on daemon's website. The Makefile contains rules for creating them for several systems (i.e. Debian, Fedora, OpenBSD, FreeBSD, NetBSD, OSX/macOS, Solaris10) but they might need updating and they might need to be changed to incorporate signing the package. The rest of this section shows installation from binary packages if they are created. To install from the Fedora RPM binary package (into /usr by default): rpm -i daemon-X.Y-1.x86_64.rpm # or rpm -i daemon-X.Y-1.i686.rpm To install from the OpenBSD binary package (into /usr/local by default): mv daemon-X.Y-openbsd-amd64.tgz daemon-X.Y.tgz # or mv daemon-X.Y-openbsd-i386.tgz daemon-X.Y.tgz # then pkg_add daemon-X.Y.tar.gz To install from the FreeBSD binary package (into /usr/local by default): pkg_add daemon-X.Y-freebsd-amd64.tbz # or pkg_add daemon-X.Y-freebsd-i386.tbz To install from the NetBSD binary package (into /usr/local by default): pkg_add daemon-X.Y-netbsd-amd64.tgz # or pkg_add daemon-X.Y-netbsd-i386.tgz To install from the Mac OS X binary package (into /usr/local by default): cd /usr/local tar xzf /usr/local/src/daemon-X.Y-macosx-universal.tar.gz # or tar xzf /usr/local/src/daemon-X.Y-macosx-x86_64.tar.gz # or tar xzf /usr/local/src/daemon-X.Y-macosx-i386.tar.gz # or tar xzf /usr/local/src/daemon-X.Y-macosx-powerpc.tar.gz To install from the Solaris10 binary package (into /usr/local by default): gunzip daemon-X.Y-solaris-amd64.pkg.gz pkgadd -d daemon-X.Y-solaris-amd64.pkg or gunzip daemon-X.Y-solaris-i386.pkg.gz pkgadd -d daemon-X.Y-solaris-i386.pkg REQUIREMENTS ============ Requires GNU make to compile. Requires perl to run the scripts in the conf directory. Requires perl to install per-function manpage links. Requires an ISO C compiler like gcc to compile the source. Requires pod2man (comes with perl) to make the manpages. Requires pod2html (comes with perl) to make the html manpages. Requires POSIX 1003.2 compliant regex functions. See INSTALL. Requires libpthread. See INSTALL. PLATFORM NOTES ============== These platform notes are quite old and probably mostly irrelevant. Linux ----- Linux 2.2 always returns 0.0.0.0 on getsockopt(IP_MULTICAST_IF) so net_multicast_get_interface() always returns 0 under Linux 2.2. Linux 2.4 does not have this bug. Make sure you have a recent glibc (at least 2.1.3) and libpthread (at least 0.8) (See INSTALL). Linux 2.2 and 2.4 have a bug-like feature in poll(2). It always times out 10ms later than specified. Libslack corrects for this as best as it can (if > 10ms -= 10ms) but it's not good enough when you need timers with a granularity of 10ms. In this case, you have to use agent_create_with_select() instead of agent_create() under Linux because select() doesn't have this bug. However, scalable I/O is impossible with select(). So, if you need timers with a granularity of 10ms *and* scalable I/O, you need an agent that uses select() in one thread for the timers and separate agents that use poll() in other threads for the I/O. Solaris ------- Solaris (at least 2.6 and 2.7) doesn't return the hardware address or index of network interfaces from ioctl() with a SIOCGIFINDEX command argument. Libslack fills in the index in net_interfaces(). UNIX domain datagram sockets aren't supported very nicely. An actual filesystem entry is needed for the client and it must be unlinked after use. It's also possible for a malicious local user to deny a client access to the server. The solution is to always use UNIX domain stream sockets. Solaris has an inadequate snprintf() function so libslack provides its own implementation. When configured for Solaris, this snprintf() function will format exactly like the system's sprintf() function, even though it has incorrect behaviour with respect to the ISO C standard. I thought this was better than having thousands of module tests apparently "fail". OpenBSD ------- Has the same UNIX domain datagram socket problem as Solaris. FreeBSD ------- Has the same UNIX domain datagram socket problem as Solaris. Can't lock fifos so fifo_open() can't guarantee a unique reader. Mac OS X -------- Probably has the same UNIX domain datagram problem as Solaris. -------------------------------------------------------------------------------- URL: https://libslack.org/daemon URL: https://raf.org/daemon GIT: https://github.com/raforg/daemon GIT: https://codeberg.org/raforg/daemon Date: 20230824 Author: raf daemon-0.8.4/macros.mk0000644000175000017500000001274214471644200012715 0ustar rafraf# # daemon - https://libslack.org/daemon # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # Uncomment this to override the default value of 300 seconds # as the minimum amount of time that a non-root client can live # if it is to be respawned # # DAEMON_DEFINES += -DRESPAWN_ACCEPTABLE=300 # Uncomment this to override the default configuration file path # # DAEMON_DEFINES += -DCONFIG_PATH=\"/etc/daemon.conf\" # DAEMON_DEFINES += -DCONFIG_PATH=\"$(PREFIX)/etc/daemon.conf\" # Uncomment this to disable debugging completely # # DAEMON_DEFINES += -DNDEBUG DAEMON_NAME := daemon DAEMON_VERSION := 0.8.4 DAEMON_DATE := 20230824 DAEMON_URL := https://libslack.org/daemon DAEMON_ID := $(DAEMON_NAME)-$(DAEMON_VERSION) DAEMON_DIST := $(DAEMON_ID).tar.gz DAEMON_HTML_ID := $(DAEMON_ID)-html DAEMON_HTML_DIST := $(DAEMON_HTML_ID).tar.gz DAEMON_DEFINES += -DDAEMON_NAME=\"$(DAEMON_NAME)\" DAEMON_DEFINES += -DDAEMON_VERSION=\"$(DAEMON_VERSION)\" DAEMON_DEFINES += -DDAEMON_DATE=\"$(DAEMON_DATE)\" DAEMON_DEFINES += -DDAEMON_URL=\"$(DAEMON_URL)\" # DAEMON_DEFINES += -DPATH_SEP=\'/\' # DAEMON_DEFINES += -DROOT_DIR=\"/\" # DAEMON_DEFINES += -DETC_DIR=\"/etc\" # DAEMON_DEFINES += -DROOT_PID_DIR=\"/var/run\" # DAEMON_DEFINES += -DUSER_PID_DIR=\"/tmp\" # Uncomment this if your system has POSIX threads reader/writer locks. # DAEMON_DEFINES += -DHAVE_PTHREAD_RWLOCK=1 # Uncomment these as appropriate # DAEMON_DEFINES += -DHAVE_SNPRINTF=1 DAEMON_DEFINES += -DHAVE_VSSCANF=1 DAEMON_DEFINES += -DHAVE_GETOPT_LONG=1 # DAEMON_DEFINES += -DNO_POSIX_C_SOURCE=1 # DAEMON_DEFINES += -DNO_POSIX_SOURCE=1 # DAEMON_DEFINES += -DNO_XOPEN_SOURCE=1 DAEMON_TARGET := $(DAEMON_SRCDIR)/$(DAEMON_NAME) DAEMON_MODULES := daemon DAEMON_HTMLDIR := $(DATA_INSDIR)/$(DAEMON_NAME)/doc DAEMON_CONF_INSDIR := $(CONF_INSDIR) DAEMON_CFILES := $(patsubst %, $(DAEMON_SRCDIR)/%.c, $(DAEMON_MODULES)) DAEMON_OFILES := $(patsubst %, $(DAEMON_SRCDIR)/%.o, $(DAEMON_MODULES)) DAEMON_PODFILES := $(DAEMON_CFILES) DAEMON_MANFILES := $(patsubst %.c, %.$(APP_MANSECT), $(DAEMON_PODFILES)) DAEMON_HTMLFILES := $(patsubst %.c, %.$(APP_MANSECT).html, $(DAEMON_PODFILES)) DAEMON_CONFFILE := $(DAEMON_NAME).conf DAEMON_CONFDIR := $(DAEMON_CONFFILE).d DAEMON_MANLINK := $(DAEMON_CONFFILE).$(FMT_MANSECT) ifeq ($(MAN_GZIP), 1) DAEMON_MANFILES := $(patsubst %, %.gz, $(DAEMON_MANFILES)) DAEMON_MANLINK := $(patsubst %, %.gz, $(DAEMON_MANLINK)) endif TAG_FILES += $(DAEMON_HFILES) $(DAEMON_CFILES) DEPEND_CFILES += $(DAEMON_CFILES) DEPEND_HFILES += $(DAEMON_HFILES) ifeq ($(DAEMON_SRCDIR), .) DAEMON_MAIN := 1 endif ALL_TARGETS += daemon MAN_TARGETS += man-daemon HTML_TARGETS += html-daemon ifeq ($(DAEMON_MAIN), 1) INSTALL_TARGETS += install-daemon UNINSTALL_TARGETS += uninstall-daemon endif DIST_TARGETS += dist-daemon RPM_TARGETS += rpm-daemon DEB_TARGETS += deb-daemon SOL_TARGETS += sol-daemon OBSD_TARGETS += obsd-daemon FBSD_TARGETS += fbsd-daemon NBSD_TARGETS += nbsd-daemon OSX_TARGETS += osx-daemon CLEAN_FILES += $(DAEMON_OFILES) $(DAEMON_MANFILES) $(DAEMON_HTMLFILES) $(DAEMON_SRCDIR)/$(DAEMON_CONFFILE) CLOBBER_FILES += $(DAEMON_TARGET) $(DAEMON_SRCDIR)/tags # $(DAEMON_SRCDIR)/debian configure-stamp build-stamp DAEMON_RPM_FILES += $(patsubst %, $(APP_INSDIR)/%, $(notdir $(DAEMON_TARGET))) DAEMON_RPM_DOCFILES += $(patsubst %, $(APP_MANDIR)/%, $(notdir $(DAEMON_MANFILES))) DAEMON_RPM_DOCFILES += $(patsubst %, $(FMT_MANDIR)/%, $(notdir $(DAEMON_MANLINK))) DAEMON_SOL := RAFOdmn # Uncomment these on MacOSX to create universal binaries # # DAEMON_CCFLAGS += -arch x86_64 -arch i386 -arch ppc # -arch ppc64 # DAEMON_LDFLAGS += -arch x86_64 -arch i386 -arch ppc # -arch ppc64 # Uncomment these on 64-bit OpenSolaris or Solaris10 to compile for 64-bit # # DAEMON_CCFLAGS += -m64 # DAEMON_LDFLAGS += -m64 DAEMON_CPPFLAGS += $(DAEMON_DEFINES) $(patsubst %, -I%, $(DAEMON_INCDIRS)) DAEMON_CCFLAGS += $(CCFLAGS) DAEMON_CCFLAGS += -Wno-comment # DAEMON_CCFLAGS += -Wno-pointer-bool-conversion # DAEMON_LDFLAGS += -pthread DAEMON_CFLAGS += $(DAEMON_CPPFLAGS) $(DAEMON_CCFLAGS) DAEMON_LIBS += slack DAEMON_LIBS += pthread DAEMON_LIBS += util # Uncomment this for --bind on Linux systems with systemd (e.g. Debian) # DAEMON_LIBS += systemd # Uncomment this for --bind on Linux systems with elogind (e.g. Slackware) # DAEMON_LIBS += elogind # Uncomment these on Solaris for sockets # # DAEMON_LIBS += xnet # DAEMON_LIBS += socket # DAEMON_LIBS += nsl DAEMON_LDFLAGS += $(patsubst %, -L%, $(DAEMON_LIBDIRS)) $(patsubst %, -l%, $(DAEMON_LIBS)) # Inherit $(CPPFLAGS), $(CFLAGS) and $(LDFLAGS) from the caller # DAEMON_CPPFLAGS += $(CPPFLAGS) DAEMON_CFLAGS += $(CFLAGS) DAEMON_LDFLAGS += $(LDFLAGS) # Include the libslack sub-target # SLACK_SRCDIR := libslack SLACK_INCDIRS := $(SLACK_SRCDIR) SLACK_LIBDIRS := $(SLACK_SRCDIR) include $(SLACK_SRCDIR)/macros.mk DAEMON_SUBTARGETS := $(SLACK_TARGET) DAEMON_SUBDIRS := $(SLACK_SRCDIR) $(SLACK_SUBDIRS) daemon-0.8.4/libslack/0000755000175000017500000000000014471644200012656 5ustar rafrafdaemon-0.8.4/libslack/tools/0000755000175000017500000000000014471644177014033 5ustar rafrafdaemon-0.8.4/libslack/tools/Makefile0000644000175000017500000000502114471644177015471 0ustar rafraf# # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # prefix := /usr/local help: @echo "This makefile provides the following targets"; \ echo; \ echo " help -- shows this list of targets"; \ echo " install-analyse-debug-locker -- installs analyse-debug-locker"; \ echo " install-prefix -- installs prefix"; \ echo " install-prefix-h -- installs prefix.h"; \ echo " install-remove-prefix-h -- installs remove_prefix.h"; \ echo " uninstall-analyse-debug-locker -- uninstalls analyse-debug-locker"; \ echo " uninstall-prefix -- uninstalls prefix"; \ echo " uninstall-prefix-h -- uninstalls prefix.h"; \ echo " uninstall-remove-prefix-h -- uninstalls remove_prefix.h"; \ echo; \ echo "Note: There should be no reason to install prefix"; \ echo " except perhaps to install its manpage."; \ echo analyse-debug-locker.1: analyse-debug-locker pod2man --center='User Commands' --section=1 $< > $@ prefix.1: prefix pod2man --center='User Commands' --section=1 $< > $@ install-analyse-debug-locker: analyse-debug-locker.1 install -m 755 analyse-debug-locker $(prefix)/bin install -m 644 analyse-debug-locker.1 $(prefix)/man/man1 install-prefix: prefix.1 install -m 755 prefix $(prefix)/bin install -m 644 prefix.1 $(prefix)/man/man1 install-prefix-h: install -m 644 prefix.h $(prefix)/include/slack install-remove-prefix-h: install -m 644 remove_prefix.h $(prefix)/include/slack uninstall-analyse-debug-locker: rm -f $(prefix)/bin/analyse-debug-locker rm -f $(prefix)/man/man1/analyse-debug-locker.1 uninstall-prefix: rm -f $(prefix)/bin/prefix rm -f $(prefix)/man/man1/prefix.1 uninstall-prefix-h: rm -f $(prefix)/include/slack/prefix.h uninstall-remove-prefix-h: rm -f $(prefix)/include/slack/remove_prefix.h # vi:set ts=4 sw=4: daemon-0.8.4/libslack/tools/README0000644000175000017500000000602014014141214014662 0ustar rafrafThis directory contains several utilities. Most of them are only useful to me during development. The last few might be useful to libslack users. For libslack development ------------------------ check-pod-prototypes # check description = source check-pod-header # check header = description check-pod-synopsis # check synopsis = header check-pod # check source = description = header = synopsis These scripts help to guarantee that at least the formal parts of the libslack documentation accurately reflect the code. The first checks that the function prototypes that will appear in the DESCRIPTION sections of each module's manpage exactly matches the header of the function itself. The second checks that the typedefs and function prototypes in the header files exactly match the same information in the SYNOPSIS section of the corresponding module's manpage. The third checks that the function prototypes in each header file exactly matches the function prototypes in the DESCRIPTION section of the corresponding module's manpage. Together, these three scripts guarantee that the typedefs and function prototypes in the manpages match the header files and the source code. The fourth combines the above checks. check-examples # check the examples in the manpages This script extracts the example programs in the module manpages and compiles and executes them to make sure that they are correct. It asks the user for command line arguments for each example. Html.pm [not included in the distribution (43KB) - available on request] There's a butchered version of Html.pm that I use to generate the HTML versions of the documentation. It is used by pod2html. The original version tries to make hyperlinks to things that it shouldn't because it assumes that the POD being converted into HTML is a perl related manpage. The assumptions that it makes are wrong for C library manpages. viconf There's a script to help me maintain the stupid configuration files at least until I start using autoconf. For client development ---------------------- analyse-debug-locker This script might be useful to users. It reads the output of the debug lockers defined in the locker module and shows where any deadlocks have occurred. prefix This script might also be useful to users. It adds a prefix to all identifiers (or selected identifiers) in libslack in case you need to guarantee local uniqueness of C identifiers on your system. Makefile The Makefile lets you install and uninstall analyse-debug-locker and the header files generated by prefix onto your system. Type "make help" for details. migrate-properties Version 0.6.2 changed the format of properties files. Before then, leading spaces in values used to be kept, now they're stripped unless quoted so this quotes them. Spaces before the '=' character used to be stripped from the property name as well and now they're only stripped if not quoted. This does nothing about that because it is assumed that nobody was doing things like "a\ =b" when the "\" meant nothing. daemon-0.8.4/libslack/tools/check-pod0000755000175000017500000000172014471544136015610 0ustar rafraf#!/bin/sh # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # Perform all the other pod checks on all modules. [ "${0%/*}" = "$0" ] && path="./" || path="${0%/*}/" ${path}check-pod-prototypes *.c ${path}check-pod-header *.c ${path}check-pod-synopsis *.c daemon-0.8.4/libslack/tools/analyse-debug-locker0000755000175000017500000000522314471544133017747 0ustar rafraf#!/usr/bin/env perl use warnings; use strict; # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # =head1 NAME I - scan debug locker output to locate deadlocks =head1 SYNOPSIS analyse-debug-locker =head1 DESCRIPTION The I module of I provides decoupled synchronisation of data shared between multiple threads. Application developers (rather than library developers) and users (rather than application developers) can specify synchronisation strategies. There are versions of the mutex and readers/writer lock lockers and produce debugging information. This program reads the output of these lockers, keeping track of which thread owns which lock. When the input stops, this program prints out which threads still own locks. Such output indicates a deadlock. You should be able to compare the original debugging information with the output of this program to locate deadlocks in client code. =head1 SEE ALSO I, I =head1 AUTHOR 20020916 raf =cut my %rwlock_thread; my %rwlock_state; while (<>) { if ($_ =~ /^\[(\d+)\]\s+rwlock_rdlock (\w+)\.\.\./) { $rwlock_thread{$2} = $1; $rwlock_state{$2} = 'rdlock...'; } elsif ($_ =~ /^\[(\d+)\]\s+rwlock_rdlock (\w+) locked/) { $rwlock_thread{$2} = $1; $rwlock_state{$2} = 'rdlock'; } elsif ($_ =~ /^\[(\d+)\]\s+rwlock_wrlock (\w+)\.\.\./) { $rwlock_thread{$2} = $1; $rwlock_state{$2} = 'wrlock...'; } elsif ($_ =~ /^\[(\d+)\]\s+rwlock_wrlock (\w+) locked/) { $rwlock_thread{$2} = $1; $rwlock_state{$2} = 'wrlock'; } elsif ($_ =~ /^\[(\d+)\]\s+rwlock_unlock (\w+)\.\.\./) { $rwlock_thread{$2} = $1; $rwlock_state{$2} = 'unlock...'; } elsif ($_ =~ /^\[(\d+)\]\s+rwlock_unlock (\w+) unlocked/) { delete $rwlock_thread{$2}; delete $rwlock_state{$2}; } } for (keys %rwlock_state) { print 'rwlock ', $_, ' ', $rwlock_state{$_}, ' [', $rwlock_thread{$_} , ']', "\n"; } # vi:set ts=4 sw=4: daemon-0.8.4/libslack/tools/check-pod-prototypes0000755000175000017500000000635714471544140020044 0ustar rafraf#!/usr/bin/env perl use warnings; use strict; # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # Compares the function prototypes in the pod manpage source against the # actual function header itself. All differences are reported. die("usage: $0 *.c\n") if $#ARGV == -1; my $state = 0; my $doc; my $src; my $jnk; my $line = 0; my $debug = 0; while ($_ = <>) { ++$line; print("line $line state $state $_") if $debug; if ($state == 0 && /^=item C[<](.*)[>]$/) # pod function prototype doco { next if $1 =~ /^E/ || $1 =~ / #def/; $state = 1; $doc = $1; } elsif ($state == 1 && /^=back$/) # bail out { $state = 0; } elsif ($state == 1 && /^=cut$/) # end of pod section { $state = 2; } elsif ($state == 2 && /^\*\/$/) # end of pod comment { do { $_ = <>; ++$line; } while /^$/; # skip blank lines while (/^static / || /^#/) # skip static functions and macros { if (/^static.*;$/) # skip forward declaration { do { $_ = <>; ++$line; } while /^$/; # skip blank lines } elsif (/^static/) # static functions or forward declarations { do { $_ = <>; ++$line; } until /^}$/; # end of static helper function do { $_ = <>; ++$line; } while /^$/; # blank lines between functions } elsif (/^#define/) # macros { while (/\\$/) { $_ = <>; ++$line; } # skip multi line macros do { $_ = <>; ++$line; } while /^$/; # skip blank lines } else # other cpp directives { do { $_ = <>; ++$line; } while /^$/; # skip blank lines } } chop(); $_ =~ s/\((\w+)\)([^;])/$1$2/; # handle special case of avoiding cpp expansion if ($doc ne $_) { my ($doc_type, $doc_name, $doc_args) = $doc =~ /^(\w+ \*?)([a-zA-Z_0-9]+)\((.*)\)/; my ($src_type, $src_name, $src_args) = $_ =~ /^(\w+ \*?)([()a-zA-Z_0-9]+)\((.*)\)/; if ((!defined $doc_type || !defined $doc_name || !defined $doc_args || !defined $src_type || !defined $src_name || !defined $src_args)) { print STDERR 'doc = ', $doc, "\n"; print STDERR 'src = ', $_, "\n"; print STDERR 'line = ', $line, "\n"; print STDERR 'doc_type = ', $doc_type, "\n"; print STDERR 'doc_name = ', $doc_name, "\n"; print STDERR 'doc_args = ', $doc_args, "\n"; print STDERR 'src_type = ', $src_type, "\n"; print STDERR 'src_name = ', $src_name, "\n"; print STDERR 'src_args = ', $src_args, "\n"; } print("doc = '$doc'\nsrc = '$_'\n\n") if $src_type ne $doc_type || $src_name ne "($doc_name)" || $src_args ne $doc_args; } else { print(" ok $doc\n") if $debug; } $state = 0; $doc = undef; } } # vi:set ts=4 sw=4: daemon-0.8.4/libslack/tools/prefix0000755000175000017500000003530214471544147015255 0ustar rafraf#!/usr/bin/env perl use warnings; use strict; # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # =head1 NAME I - add a prefix to libslack's public identifiers =head1 SYNOPSIS prefix [options] prefix [identifier...] options: -h - Print the help message then exit -d - Print debug messages -n - Don't modify anything. Run diff instead -r - Remove the prefix from identifiers -a - Include all public identifiers (no exceptions) -m module - Modify identifiers in the specified module only -s srcdir - Specify the source directory (".." by default) -P - Generage prefix.h (adds prefixes in client code) -R - Generate remove_prefix.h (removes prefixes in client code) =head1 DESCRIPTION There is little attempt to make the public identifiers in I unique across the entire C identifier space. Proper libraries usually insert a prefix to all public identifiers. Unfortunately, this doesn't actually guarantee uniqueness and things can get rather ugly. Without a global prefix registry, any number of developers could choose the same prefix. To quarantee uniqueness, system administrators who install libraries must define locally unique prefixes to libraries when necessary. This program adds the specified prefix to public identifiers in libslack. If no identifiers are specified on the command line, all public identifiers are modified. Otherwise, just those that are specified on the command line are modified. Note that the manual pages are modified as well as the source code and header files. If the C<-r> option is supplied, the prefix is removed. This makes it possible to change an existing prefix. Note that this program must be run before the library is compiled and before the manual pages are generated. It has no effect on an already installed instance of libslack. =head1 OPTIONS =over 4 =item C<-h> Print the help message then exit. =item C<-d> Print debug messages. =item C<-n> Don't modify any files. Run diff instead. =item C<-r> Remove the prefix from identifiers. =item C<-a> Include all public identifiers (no exceptions). See the EXCEPTIONS section for more information. This option only makes sense when no identifiers are supplied on the command line. =item C<-m module> Modify identifiers in the specified module only. =item C<-s srcdir> Specify the source directory (".." by default). =item C<-P> Generate the C header file which contains macros that translate the original I identifiers into the prefixed identifiers. Client code that includes this header file can use the original I identifiers when compiled on systems that have I installed with prefixed identifiers. Can't be used if the C<-r> option is supplied. =item C<-R> Generate the C header file which contains macros that translate prefixed identifiers into the original I identifiers. Client code written on a system that installed I with prefixes can use those identifiers when compiled on systems that have I installed with the original identifiers. Can't be used if the C<-r> option is supplied. =back =head1 EXCEPTIONS By default, this program does not alter the identifiers of functions that are only included in I as a backup in case your system doesn't already have them. These are I, I, I, I, I, I, I, I, I and the entire I module. This is so I will use your system's implementations if they exist. If they don't exist, then there's little harm in defining them here. Also excluded from renaming are C and C since these are only defined if they have not already been defined so they won't clash with an existing C<#define> as long as the I headers are included last. If you require any of these to be renamed, supply them on the command line. If you require all of them to be renamed, supply the C<-a> option. =head1 EXAMPLES prefix org_libslack_ # prefix everything with "org_libslack_" prefix s_ error fatal debug # prefix a few identifiers with "s_" prefix -r s_ debug # remove "s_" prefix from debug prefix -r -m daemon daemon_ # remove "daemon_" prefix from daemon module prefix -n -r -m daemon daemon_ # see what removing "daemon_" would do prefix -P libslack_ # prefix with "libslack.h" and make prefix.h prefix -n -R libslack_ # just make remove_prefix.h =head1 ADVICE If you need to use this program, use C<"libslack_"> as the prefix. It would be best if everyone used the same prefix whenever possible. Technically, the prefix should probably be C<"org_libslack_libslack_"> but that's ridiculous. Also generate the C header file and install it. If client code can separate uses of I from uses of other libraries with clashing names into separate translation units, then it is possible to never see the I prefixes and still have everything link. =head1 NOTES This program only renames functions, not modules. If you don't like this, install I into a directory under C so the module names can't clash with anything else. This program works on the I source code. Don't expect it to work on anything else. =head1 SEE ALSO I, C =head1 AUTHOR 20020916 raf =cut use Getopt::Std; $| = 1; my $srcdir = '..'; my ($name) = $0 =~ /([^\/]+)$/; my $usage = << "USAGE"; usage: $name [options] prefix [identifier...] options: -h - Print the help message then exit -d - Print debug messages -n - Don't modify anything. Run diff instead -r - Remove the prefix from identifiers -a - Include all public identifiers (no exceptions) -m module - Modify identifiers in the specified module only -s srcdir - Specify the source directory ("$srcdir" by default) -P - Generage prefix.h (adds prefixes in client code) -R - Generate remove_prefix.h (removes prefixes in client code) Type \"perldoc -F `which $name`\" for the manpage. USAGE sub usage { my ($msg) = @_; die "$msg\n$usage"; } # check the arguments my %opt; usage('Illegal option') unless getopts('hdnram:s:PR', \%opt); usage('') if defined $opt{h}; my $debug = defined $opt{d}; my $test = defined $opt{n}; my $reverse = defined $opt{r}; my $all = defined $opt{a}; my @modules = split / /, $opt{m} if defined $opt{m}; $srcdir = $opt{s} if defined $opt{s}; my $prefix_h = defined $opt{P}; my $remove_prefix_h = defined $opt{R}; usage('Must specify prefix') unless defined $ARGV[0]; my $prefix = shift @ARGV; usage("Not a valid identifier prefix: '$prefix'") if $prefix =~ /(^[0-9])|([^a-zA-Z0-9_])/; my @targets = @ARGV; my %identifiers; my $total = $#modules == -1 && $#targets == -1; # build the module list my %exclude_modules = (getopt => 1) unless $all; if ($#modules != -1) { # check module names supplied on the command line for (my $i = 0; $i <= $#modules; ++$i) { usage("No such module: '$srcdir/$modules[$i]'") unless -f "$srcdir/$modules[$i].h" && -f "$srcdir/$modules[$i].c"; } } else { # find all module names (i.e. header files with corresponding source files) opendir(SRCDIR, $srcdir) || die "failed to open $srcdir: $!\n"; @modules = grep { /.*\.h$/ && s/\.h$// && -f "$srcdir/$_.c" && !defined $exclude_modules{$_} } readdir(SRCDIR); closedir SRCDIR; } print("modules:\n\n ", join("\n ", sort @modules), "\n\n") if $debug; # build the identifier list my %exclude_identifiers = ( snprintf => 1, # use system implementation if possible vsnprintf => 1, # use system implementation if possible asprintf => 1, # use system implementation if possible vasprintf => 1, # use system implementation if possible strcasecmp => 1, # use system implementation if possible strncasecmp => 1, # use system implementation if possible strlcpy => 1, # use system implementation if possible strlcat => 1, # use system implementation if possible vsscanf => 1, # use system implementation if possible option => 1, # use system implementation if possible null => 1, # only defined if not already defined nul => 1 # only defined if not already defined ) unless $all; for (my $i = 0; $i <= $#modules; ++$i) { my $module = "$srcdir/$modules[$i].h"; my $hdr_state = ''; open(HDR, $module) or die("failed to open $module\n"); while () { if ($_ =~ /^#ifndef HAVE_PTHREAD_RWLOCK/) { my $skip; do { $skip = ; } while $skip !~ /^#endif$/; } next if $_ =~ /^$/; next if $_ =~ /^#/ && $_ !~ /^#\s*define\s+[a-z_0-9]+/; next if $_ =~ /^\//; next if $_ =~ /^\*/; $hdr_state = 'decls', next if $_ =~ /^_begin_decls$/; last if $_ =~ /^_end_decls$/; if (/^#\s*define\s+[a-z_0-9]+(\s|\()/) { my $macro = $_; chop($macro); $macro =~ s/^#\s*define\s+//; $macro =~ s/^([a-z_0-9]+).*$/$1/; $identifiers{$macro} = 1 unless defined $exclude_identifiers{$macro}; } elsif (/^typedef.*;$/) { my $type = $_; chop($type); $type =~ s/;$//; $type =~ s/\([^)]*\)$//; $type =~ s/^[^(]+\(\*(\w+)\)$/$1/ if $type =~ /\(\*\w+\)$/; $type =~ s/^.*\W(\w+)$/$1/; $identifiers{$type} = 1 unless defined $exclude_identifiers{$type}; } elsif ($hdr_state eq 'decls') { my $proto = $_; chop($proto); #warn "$module: _args missing: $proto\n" if $proto =~ /\);\s*$/ && $proto !~ / _args /; #$proto =~ s/ _args.*$//; $proto =~ s/\(.*$//; $proto =~ s/^.*\W(\w+)$/$1/; $proto = $1 if $proto =~ /^extern\s+\w+\s+(\w+)/; $identifiers{$proto} = 1 unless defined $exclude_identifiers{$proto}; } } close(HDR); } print("identifiers:\n\n ", join("\n ", sort keys %identifiers), "\n\n") if $debug; # set up the target list if ($#targets != -1) { # check identifiers supplied on the command line for (my $i = 0; $i <= $#targets; ++$i) { usage("Not an identifier: '$targets[$i]'") if $targets[$i] =~ /(^[0-9])|([^a-zA-Z0-9_])/; usage("No such identifier: '$targets[$i]'") unless defined $identifiers{$targets[$i]}; } } else { # use all identifiers as targets @targets = keys %identifiers; } print("targets:\n\n ", join("\n ", sort @targets), "\n\n") if $debug; # modify every instance of the public identifiers opendir(SRCDIR, $srcdir) || die "failed to open $srcdir: $!\n"; my @sources = grep { (/.*\.h$/ || /.*\.c$/) && -f "$srcdir/$_" } readdir(SRCDIR); closedir SRCDIR; my %patterns; for my $target (@targets) { if ($reverse) { usage("Identifier $target does not begin with $prefix") unless $target =~ /^$prefix/; my ($sans_prefix) = $target =~ /^$prefix([a-zA-Z0-9_]+)$/; usage("Result will not be an identifier: '$target' -> '$sans_prefix'") unless defined $sans_prefix && $sans_prefix =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/; $patterns{$target} = $sans_prefix; } else { $patterns{$target} = $prefix . $target; } } print("pattern:\n\n ", join("\n ", map { "s/\\b$_\\b/$patterns{$_}/" } sort keys %patterns), "\n\n") if $debug; my %reverse_patterns = map { ($patterns{$_}, $_) } keys %patterns unless $reverse; print("reverse pattern:\n\n ", join("\n ", map { "s/\\b$_\\b/$reverse_patterns{$_}/" } sort keys %reverse_patterns), "\n\n") if $debug && !$reverse; # modify every instance of every target in every source file $/ = undef; for (my $i = 0; $i <= $#sources; ++$i) { my $src = "$srcdir/$sources[$i]"; open(SRC, $src) or die("failed to open $src\n"); my $text = ; my $copy = $text; close(SRC); $text =~ s/(? $out") or die("failed to open $out for writing\n"); print SRC $text; close(SRC); print("diff -du $src $out\n") if $test; system("diff -du $src $out") if $test; unlink($out) if $test; } } # generate prefix.h hdr('prefix.h', map { "#define $_ $patterns{$_}\n" } sort keys %patterns) if $prefix_h && !$reverse; # generate remove_prefix.h hdr('remove_prefix.h', map { "#define $patterns{$_} $_\n" } sort keys %patterns) if $remove_prefix_h && !$reverse; # generate a header file with a macro for each changed identifier sub hdr { my ($name, @macros) = @_; my $name_macro = $name; $name_macro =~ tr/a-z./A-Z_/; open(HDR, "> $name") or die "failed to open $name for writing\n"; print HDR << "EOT"; /* * libslack - https://libslack.org * * Copyright (C) 1999-2004, 2010, 2020-2023 raf * * 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, see . * * 20230824 raf */ #ifndef LIBSLACK_$name_macro #define LIBSLACK_$name_macro EOT print HDR @macros, "\n#endif\n"; close(HDR); } # undo prefix changes on ordinary words in the manpages # ordinary words do not appear inside C<...> or I<...> sub fixdoco { my ($text) = @_; my (@lines) = split(/\n/, $text); my $pod = 0; for (my $i = 0; $i <= $#lines; ++$i) { $pod = 0, next if $lines[$i] =~ /^=cut$/; # pod start $pod = 1 if $lines[$i] =~ /^=/; # pod stop next if $lines[$i] =~ /^=\w+\s+[CI] # # 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, see . # # 20230824 raf # # Edit the configuration files and keep them in sync case "`pwd`" in *libslack) cd ..;; esac gvim -f conf/* cp conf/* libslack/conf perl -pi -e 's/^# daemon - https:\/\/libslack.org\/daemon$/# libslack - https:\/\/libslack.org/' libslack/conf/* daemon-0.8.4/libslack/tools/check-pod-synopsis0000755000175000017500000000603614471544143017500 0ustar rafraf#!/usr/bin/env perl use warnings; use strict; # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # Compares the contents of each module's header file against the synposis # section of the corresponding manpage which should contain all typedefs # and function prototypes that appear in the header file. All differences # are reported. die("usage: $0 *.c\n") if $#ARGV == -1; my @src; my @hdr; for my $src (@ARGV) { next unless $src =~ /\.c$/; next if $src eq 'getopt.c'; my $hdr = $src; $hdr =~ s/\.c$/.h/; my $src_state = ''; my $hdr_state = ''; @src = (); @hdr = (); my $done_rwlock_skip = 0; open(SRC, $src) or die("failed to open $src\n"); while () { next if $_ =~ /^$/; next if $_ =~ /^\s+#/; $src_state = $1, next if $_ =~ /^=head1 (.*)$/; last if $src_state eq 'DESCRIPTION'; if ($src_state eq 'SYNOPSIS') { my $line = $_; $line =~ s/^ //; chop($line); push(@src, $line); } } close(SRC); open(HDR, $hdr) or die("failed to open $hdr\n"); while () { if ($_ =~ /^#ifndef HAVE_PTHREAD_RWLOCK/ && $done_rwlock_skip == 0) { my $jnk; do { $jnk = ; } while $jnk !~ /^#endif$/; $done_rwlock_skip = 1; } next if $_ =~ /^$/; next if $_ =~ /^#/; next if $_ =~ /^\//; next if $_ =~ /^ ?\*/; $hdr_state = 'decls', next if $_ =~ /^_begin_decls$/; last if $_ =~ /^_end_decls$/; if ($hdr_state eq '') { my $line = $_; chop($line); $line =~ s/ / /g; $line =~ s/\/\*.*\*\/$//; $line =~ s/\s+$//; push(@hdr, $line); } if ($hdr_state eq 'decls') { my $line = $_; # $line =~ s/ _args \(//; # $line =~ s/\)\);/);/; chop($line); push(@hdr, $line); } } close(HDR); show("$src != $hdr (line number mismatch)"), next if $#src != $#hdr; my $i; my $first = 1; for ($i = 0; $i <= $#src; ++$i) { $src[$i] =~ s/\((\w+)\)([^;])/$1$2/; # handle special case of avoiding cpp expansion if ($src[$i] ne $hdr[$i]) { print("$src != $hdr\n") if $first; print("syn = $src[$i]\n"); print("hdr = $hdr[$i]\n"); $first = 0; } } print("\n") unless $first; } sub show { my ($msg) = @_; print("$msg\n"); print("-- syn ---\n"); my $i; for ($i = 0; $i <= $#src; ++$i) { print("$src[$i]\n"); } print("-- hdr ---\n"); for ($i = 0; $i <= $#hdr; ++$i) { print("$hdr[$i]\n"); } print("----------\n\n"); } # vi:set ts=4 sw=4: daemon-0.8.4/libslack/tools/check-pod-header0000755000175000017500000000563114471544137017044 0ustar rafraf#!/usr/bin/env perl use warnings; use strict; # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # Compares the function prototypes in each module's header file against the # function prototypes in the pod manpage source. All differences are reported. die("usage: $0 *.c\n") if $#ARGV == -1; my @src; my @hdr; for my $src (@ARGV) { next unless $src =~ /\.c$/; next if $src eq 'getopt.c'; next if $src eq 'hsort.c'; next if $src eq 'vsscanf.c'; my $hdr = $src; $hdr =~ s/\.c$/.h/; my $src_state = ''; my $hdr_state = ''; @src = (); @hdr = (); my $done_rwlock_skip = 0; open(SRC, $src) or die("failed to open $src\n"); while () { next if $_ =~ /^$/; next if $_ =~ /^\s+#/; $src_state = $1, next if $_ =~ /^=head1 (.*)$/; if ($src_state eq 'DESCRIPTION' && $_ =~ /^=item [CI]<(.*)>$/) { my $proto = $1 . ';'; next if $proto =~ /^ #define/; next unless $proto =~ /^extern/ || $proto =~ /\);$/; push(@src, $proto); } } close(SRC); open(HDR, $hdr) or die("failed to open $hdr\n"); while () { if ($_ =~ /^#ifndef HAVE_PTHREAD_RWLOCK/ && $done_rwlock_skip == 0) { my $jnk; do { $jnk = ; } while $jnk !~ /^#endif$/; $done_rwlock_skip = 1; } next if $_ =~ /^$/; next if $_ =~ /^#/; next if $_ =~ /^\//; next if $_ =~ /^\*/; $hdr_state = 'decls', next if $_ =~ /^_begin_decls$/; last if $_ =~ /^_end_decls$/; if ($hdr_state eq 'decls') { my $proto = $_; #warn "$hdr: _args missing: $proto\n" if $proto =~ /\);\s*$/ && $proto !~ / _args /; #$proto =~ s/ _args \(//; #$proto =~ s/\)\);/);/; chop($proto); push(@hdr, $proto); } } close(HDR); show("$src != $hdr (line number mismatch)"), next if $#src != $#hdr; my $i; my $first = 1; for ($i = 0; $i <= $#src; ++$i) { if ($src[$i] ne $hdr[$i]) { print("$src != $hdr\n") if $first; print("doc = $src[$i]\n"); print("hdr = $hdr[$i]\n"); $first = 0; } } print("\n") unless $first; } sub show { my ($msg) = @_; print("$msg\n"); print("-- doc ---\n"); my $i; for ($i = 0; $i <= $#src; ++$i) { print("$src[$i]\n"); } print("-- hdr ---\n"); for ($i = 0; $i <= $#hdr; ++$i) { print("$hdr[$i]\n"); } print("----------\n\n"); } # vi:set ts=4 sw=4: daemon-0.8.4/libslack/tools/check-examples0000755000175000017500000000460114471544134016643 0ustar rafraf#!/usr/bin/env perl use warnings; use strict; # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # $| = 1; # Compiles and executes the examples from the manpages. # Run this from the source directory. die("usage: $0 *.c\n") if $#ARGV == -1; my $total = 0; for my $src (@ARGV) { next unless $src =~ /\.c$/; #next if $src =~ /getopt\.c$/; warn("Failed to open $src for reading: $!\n"), next unless open SRC, $src; my $state = ''; my $desc = ''; my $code = ''; my $num = 0; while () { $state = 'X', next if /^=head\d EXAMPLE/; $state = '', next if $state eq 'X' && (/^=head/ || /^=cut/); next unless $state eq 'X'; s/\s*(?; return if $response =~ /n/i; warn("Failed to open example.c for writing: $!\n"), return unless open(EXAMPLE, "> example.c"); print EXAMPLE $code; close EXAMPLE; print 'Arguments: '; chop(my $args = ); system("echo Compiling...; cc -o example example.c `libslack-config --cflags --libs` && echo Executing... && ./example $args; echo rc=\$?"); unlink 'example', 'example.c'; print 'Press return to continue or Ctrl-C to quit.'; ; } # vi:set ts=4 sw=4: daemon-0.8.4/libslack/tools/migrate-properties0000755000175000017500000000224214471544145017575 0ustar rafraf#!/bin/sh # # libslack - https://libslack.org # # Copyright (C) 1999-2004, 2010, 2020-2023 raf # # 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, see . # # 20230824 raf # # Convert an old libslack property file into a libslack-0.5.2 property file. # Previously, leading spaces were kept. Now they're stripped unless quoted. # This script quotes leading spaces in old properties files so that they'll # be preserved by newer versions of libslack. if [ $# = 0 ] then echo usage: $0 '/etc/properties/* ~/.properties/*' >&2 exit 1 fi exec perl -p -i -e 's/(? # # 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, see . # # 20230824 raf # CC := gcc # CC := cc # CC := other AR := ar RANLIB := ranlib POD2MAN := pod2man POD2HTML := pod2html GZIP := gzip -f -9 DESTDIR := PREFIX := /usr/local # PREFIX := /usr/pkg APP_INSDIR := $(PREFIX)/bin LIB_INSDIR := $(PREFIX)/lib MAN_SYSDIR := $(PREFIX)/share/man MAN_LOCDIR := $(PREFIX)/share/man ifeq ($(PREFIX),/usr) MAN_INSDIR := $(MAN_SYSDIR) else MAN_INSDIR := $(MAN_LOCDIR) endif HDR_INSDIR := $(PREFIX)/include DATA_INSDIR := $(PREFIX)/share APP_MANSECT := 1 LIB_MANSECT := 3 APP_MANDIR := $(MAN_INSDIR)/man$(APP_MANSECT) LIB_MANDIR := $(MAN_INSDIR)/man$(LIB_MANSECT) APP_MANSECTNAME := User Commands LIB_MANSECTNAME := C Library Functions - libslack MAN_GZIP := 1 CCFLAGS += -O3 CCFLAGS += -Wall -pedantic # CCFLAGS += -xO4 CLEAN_FILES += tags core Makefile.bak .makefile.bak pod2htm* SLACK_SRCDIR := . SLACK_INCDIRS := . SLACK_LIBDIRS := . include $(SLACK_SRCDIR)/macros.mk .PHONY: all ready test check man html install uninstall dist rpm deb sol all: ready $(ALL_TARGETS) ready: $(READY_TARGETS) check test: all $(TEST_TARGETS) man: $(MAN_TARGETS) html: $(HTML_TARGETS) install: all $(INSTALL_TARGETS) uninstall: $(UNINSTALL_TARGETS) dist: $(DIST_TARGETS) rpm: $(RPM_TARGETS) deb: $(DEB_TARGETS) sol: $(SOL_TARGETS) obsd: $(OBSD_TARGETS) fbsd: $(FBSD_TARGETS) nbsd: $(NBSD_TARGETS) osx: $(OSX_TARGETS) .PHONY: help help-macros depend clean clobber distclean help:: @echo "This makefile provides the following targets."; \ echo; \ echo " help -- shows this list of targets"; \ echo " help-macros -- shows the values of all make macros"; \ echo " all -- makes $(SLACK_TARGET) (default)"; \ echo " ready -- prepares the source directory for compilation"; \ echo " test -- makes and runs library unit tests"; \ echo " check -- same as test"; \ echo " man -- generates all manpages"; \ echo " html -- generates all manpages in html"; \ echo " install -- installs everything under $(PREFIX)"; \ echo " uninstall -- uninstalls everything from $(PREFIX)"; \ echo " depend -- generates source dependencies using makedepend"; \ echo " tags -- generates a tags file using ctags"; \ echo " clean -- removes output files, tags, tests, and de-configures"; \ echo " clobber -- same as clean"; \ echo " distclean -- same as clean"; \ echo " dist -- makes the distribution: ../$(SLACK_DIST)"; \ echo " rpm -- makes source and binary rpm packages [OLD]"; \ echo " deb -- makes source and binary debian packages [OLD]"; \ echo " sol -- makes binary solaris package [OLD]"; \ echo " obsd -- makes binary openbsd package [OLD]"; \ echo " fbsd -- makes binary freebsd package [OLD]"; \ echo " nbsd -- makes binary netbsd package [OLD]"; \ echo " osx -- makes binary macosx package [OLD]"; \ echo; \ echo " slack.swig -- makes SWIG input file for libslack"; \ echo help-macros:: @echo "CC = $(CC)"; \ echo "PREFIX = $(PREFIX)"; \ echo "APP_INSDIR = $(APP_INSDIR)"; \ echo "LIB_INSDIR = $(LIB_INSDIR)"; \ echo "MAN_INSDIR = $(MAN_INSDIR)"; \ echo "HDR_INSDIR = $(HDR_INSDIR)"; \ echo "DATA_INSDIR = $(DATA_INSDIR)"; \ echo "APP_MANSECT = $(APP_MANSECT)"; \ echo "LIB_MANSECT = $(LIB_MANSECT)"; \ echo "APP_MANDIR = $(APP_MANDIR)"; \ echo "LIB_MANDIR = $(LIB_MANDIR)"; \ echo "TAG_FILES = $(TAG_FILES)"; \ echo "DEPEND_CFILES = $(DEPEND_CFILES)"; \ echo "DEPEND_HFILES = $(DEPEND_HFILES)"; \ echo "CCFLAGS = $(CCFLAGS)"; \ echo "READY_TARGETS = $(READY_TARGETS)"; \ echo "ALL_TARGETS = $(ALL_TARGETS)"; \ echo "TEST_TARGETS = $(TEST_TARGETS)"; \ echo "MAN_TARGETS = $(MAN_TARGETS)"; \ echo "HTML_TARGETS = $(HTML_TARGETS)"; \ echo "INSTALL_TARGETS = $(INSTALL_TARGETS)"; \ echo "UNINSTALL_TARGETS = $(UNINSTALL_TARGETS)"; \ echo "CLEAN_FILES = $(CLEAN_FILES)"; \ echo "CLOBBER_FILES = $(CLOBBER_FILES)"; \ echo "DIST_TARGETS = $(DIST_TARGETS)"; \ echo "RPM_TARGETS = $(RPM_TARGETS)"; \ echo "DEB_TARGETS = $(DEB_TARGETS)"; \ echo "SOL_TARGETS = $(SOL_TARGETS)"; \ echo "OBSD_TARGETS = $(OBSD_TARGETS)"; \ echo "FBSD_TARGETS = $(FBSD_TARGETS)"; \ echo "NBSD_TARGETS = $(NBSD_TARGETS)"; \ echo "OSX_TARGETS = $(OSX_TARGETS)"; \ echo tags: $(TAG_FILES) @ctags $(TAG_FILES) depend: ready $(DEPEND_CFILES) $(DEPEND_HFILES) @makedepend $(SLACK_CPPFLAGS) $(DEPEND_CFILES) clean:: @rm -rf $(CLEAN_FILES) $(CLOBBER_FILES) @perl -pi -e 'last if /[D]O NOT DELETE/;' $(patsubst %, %/Makefile, $(SLACK_SRCDIR) $(SLACK_SUBDIRS)) ./configure --default clobber:: clean distclean:: clean include $(SLACK_SRCDIR)/rules.mk daemon-0.8.4/libslack/prog.h0000644000175000017500000001105214471544106014001 0ustar rafraf/* * libslack - https://libslack.org * * Copyright (C) 1999-2004, 2010, 2020-2023 raf * * 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, see . * * 20230824 raf */ #ifndef LIBSLACK_PROG_H #define LIBSLACK_PROG_H #include #include #include #ifndef PATH_SEP #define PATH_SEP '/' #endif #ifndef HAVE_GETOPT_LONG #include #else #include #endif typedef struct option option; typedef struct Option Option; typedef struct Options Options; typedef void opt_action_int_t(int arg); typedef void opt_action_optional_int_t(int *arg); typedef void opt_action_string_t(const char *arg); typedef void opt_action_optional_string_t(const char *arg); typedef void opt_action_none_t(void); typedef void func_t(void); enum OptionArgument { OPT_NONE, OPT_INTEGER, OPT_STRING }; enum OptionAction { OPT_NOTHING, OPT_VARIABLE, OPT_FUNCTION }; typedef enum OptionArgument OptionArgument; typedef enum OptionAction OptionAction; struct Option { const char *name; char short_name; const char *argname; const char *desc; int has_arg; OptionArgument arg_type; OptionAction action; void *object; func_t *function; }; struct Options { Options *parent; Option *options; }; _begin_decls void prog_init(void); const char *prog_set_name(const char *name); Options *prog_set_options(Options *options); const char *prog_set_syntax(const char *syntax); const char *prog_set_desc(const char *desc); const char *prog_set_version(const char *version); const char *prog_set_date(const char *date); const char *prog_set_author(const char *author); const char *prog_set_contact(const char *contact); const char *prog_set_vendor(const char *vendor); const char *prog_set_url(const char *url); const char *prog_set_legal(const char *legal); Msg *prog_set_out(Msg *out); Msg *prog_set_err(Msg *err); Msg *prog_set_dbg(Msg *dbg); Msg *prog_set_alert(Msg *alert); ssize_t prog_set_debug_level(size_t debug_level); ssize_t prog_set_verbosity_level(size_t verbosity_level); int prog_set_locker(Locker *locker); const char *prog_name(void); const Options *prog_options(void); const char *prog_syntax(void); const char *prog_desc(void); const char *prog_version(void); const char *prog_date(void); const char *prog_author(void); const char *prog_contact(void); const char *prog_vendor(void); const char *prog_url(void); const char *prog_legal(void); Msg *prog_out(void); Msg *prog_err(void); Msg *prog_dbg(void); Msg *prog_alert(void); size_t prog_debug_level(void); size_t prog_verbosity_level(void); int prog_out_fd(int fd); int prog_out_stdout(void); int prog_out_file(const char *path); int prog_out_syslog(const char *ident, int option, int facility, int priority); int prog_out_push_filter(msg_filter_t *filter); int prog_out_none(void); int prog_err_fd(int fd); int prog_err_stderr(void); int prog_err_file(const char *path); int prog_err_syslog(const char *ident, int option, int facility, int priority); int prog_err_push_filter(msg_filter_t *filter); int prog_err_none(void); int prog_dbg_fd(int fd); int prog_dbg_stdout(void); int prog_dbg_stderr(void); int prog_dbg_file(const char *path); int prog_dbg_syslog(const char *id, int option, int facility, int priority); int prog_dbg_push_filter(msg_filter_t *filter); int prog_dbg_none(void); int prog_alert_fd(int fd); int prog_alert_stdout(void); int prog_alert_stderr(void); int prog_alert_file(const char *path); int prog_alert_syslog(const char *id, int option, int facility, int priority); int prog_alert_push_filter(msg_filter_t *filter); int prog_alert_none(void); int prog_opt_process(int ac, char **av); void prog_usage_msg(const char *format, ...); void prog_help_msg(void); void prog_version_msg(void); const char *prog_basename(const char *path); extern Options prog_options_table[1]; int opt_process(int argc, char **argv, Options *options, char *msgbuf, size_t bufsize); char *opt_usage(char *buf, size_t size, Options *options); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.8.4/libslack/lim.h0000644000175000017500000000357014471544010013613 0ustar rafraf/* * libslack - https://libslack.org * * Copyright (C) 1999-2004, 2010, 2020-2023 raf * * 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, see . * * 20230824 raf */ #ifndef LIBSLACK_LIM_H #define LIBSLACK_LIM_H #include _begin_decls long limit_arg(void); long limit_child(void); long limit_tick(void); long limit_group(void); long limit_open(void); long limit_stream(void); long limit_tzname(void); long limit_job(void); long limit_save_ids(void); long limit_version(void); long limit_pcanon(const char *path); long limit_fcanon(int fd); long limit_canon(void); long limit_pinput(const char *path); long limit_finput(int fd); long limit_input(void); long limit_pvdisable(const char *path); long limit_fvdisable(int fd); long limit_vdisable(void); long limit_plink(const char *path); long limit_flink(int fd); long limit_link(void); long limit_pname(const char *path); long limit_fname(int fd); long limit_name(void); long limit_ppath(const char *path); long limit_fpath(int fd); long limit_path(void); long limit_ppipe(const char *path); long limit_fpipe(int fd); long limit_pnotrunc(const char *path); long limit_fnotrunc(int fd); long limit_notrunc(void); long limit_pchown(const char *path); long limit_fchown(int fd); long limit_chown(void); _end_decls #endif /* vi:set ts=4 sw=4: */ daemon-0.8.4/libslack/err.c0000644000175000017500000010164314471543766013636 0ustar rafraf/* * libslack - https://libslack.org * * Copyright (C) 1999-2004, 2010, 2020-2023 raf * * 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, see . * * 20230824 raf */ /* =head1 NAME I - message/error/debug/verbosity/alert messaging module =head1 SYNOPSIS #include #include void msg(const char *format, ...); void vmsg(const char *format, va_list args); void verbose(size_t level, const char *format, ...); void vverbose(size_t level, const char *format, va_list args); void debugf(size_t level, const char *format, ...); void vdebugf(size_t level, const char *format, va_list args); int error(const char *format, ...); int verror(const char *format, va_list args); void fatal(const char *format, ...); void vfatal(const char *format, va_list args); void dump(const char *format, ...); void vdump(const char *format, va_list args); void alert(int priority, const char *format, ...); void valert(int priority, const char *format, va_list args); void debugsysf(size_t level, const char *format, ...); void vdebugsysf(size_t level, const char *format, va_list args); int errorsys(const char *format, ...); int verrorsys(const char *format, va_list args); void fatalsys(const char *format, ...); void vfatalsys(const char *format, va_list args); void dumpsys(const char *format, ...); void vdumpsys(const char *format, va_list args); void alertsys(int priority, const char *format, ...); void valertsys(int priority, const char *format, va_list args); int set_errno(int errnum); void *set_errnull(int errnum); void (*(set_errnullf)(int errnum))(); #define debug(args) #define vdebug(args) #define debugsys(args) #define vdebugsys(args) #define check(test, mesg) =head1 DESCRIPTION This module works with the I and I modules to provide functions for emitting various types of message with simple call syntax and flexible behaviour. The message types catered for are: normal, verbose, debug, error, fatal error, dump and alert messages. All messages are created and sent with I-like syntax. The destinations for these messages are configurable by the client. Calling I causes normal and verbose messages to be sent to standard output; and debug, error, fatal error, dump and alert messages to be sent to standard error. Calls to I, I, I, I, I and I cause normal and verbose messages to be sent to the specified destination. Calls to I, I, I, I, I and I cause error, fatal error and dump messages to be sent to the specified destination. Calls to I, I, I, I, I, I, I cause debug messages to be sent to the specified destination. Calls to I, I, I, I, I, I, I cause alert messages to be sent to the specified destination. Calls to the generic functions I, I, I and I cause their respective message types to be sent to the specified destination or destinations (multiplexing messages is only possible via these functions). See I for more details. =over 4 =cut */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* For snprintf() on OpenBSD-4.7 */ #endif #ifndef _DEFAULT_SOURCE #define _DEFAULT_SOURCE /* New name for _BSD_SOURCE */ #endif #include "config.h" #include "std.h" #include "msg.h" #include "prog.h" #include "err.h" #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif #ifndef TEST /* =item C Outputs a message to the program's normal message destination. C is a I-like format string which processes any remaining arguments in the same way as I. B msg(buf); // EVIL msg("%s", buf); // GOOD =cut */ void msg(const char *format, ...) { va_list args; va_start(args, format); vmsg(format, args); va_end(args); } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vmsg(const char *format, va_list args) { vmsg_out(prog_out(), format, args); } /* =item C Outputs a verbose message to the program's normal message destination if C is less than or equal to the program's current verbosity level. If the program's name has been supplied using I, the message will be preceded by the name, a colon, and a space. The message is also preceded by as many spaces as the message level. This indents messages according to their verbosity. C is a I-like format string which processes any remaining arguments in the same way as I. The message is followed by a newline. =cut */ void verbose(size_t level, const char *format, ...) { if (prog_verbosity_level() >= level) { va_list args; va_start(args, format); vverbose(level, format, args); va_end(args); } } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vverbose(size_t level, const char *format, va_list args) { if (prog_verbosity_level() >= level) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); if (prog_name()) msg_out(prog_out(), "%s: %*s%s\n", prog_name(), level, "", mesg); else msg_out(prog_out(), "%*s%s\n", level, "", mesg); } } /* =item C Outputs a debug message to the program's debug message destination if C satisfies the program's current debug level. The debug level is broken into two components. The low byte specifies the level. The next three bytes specify a section within which the level applies. Debug messages with a section value whose bits overlap those of the program's current debug section, and with a level that is less than or equal to the program's current debug level, are emitted. As a convenience, if the program's current debug section is zero, debug messages with a sufficiently small level are emitted, regardless of the message section. See I for examples. If the program's name has been supplied using I, the message will be preceded by the name, a colon, and a space. This is followed by the string C<"debug:">. If C specifies a non-zero section, it is included in the debug message, after a space and surrounded by square brackets. This is followed by as many spaces as the debug level. This indents debug messages according to their debug level. C is a I-like format string which processes any remaining arguments in the same way as I. The message is followed by a newline. =cut */ static int debug_level_match(size_t level) { size_t debug_level, debug_section, section; debug_level = prog_debug_level(); debug_section = debug_level & 0xffffff00; debug_level &= 0x000000ff; section = level & 0xffffff00; level &= 0x000000ff; return (!debug_section || debug_section & section) && debug_level >= level; } void debugf(size_t level, const char *format, ...) { if (debug_level_match(level)) { va_list args; va_start(args, format); vdebugf(level, format, args); va_end(args); } } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdebugf(size_t level, const char *format, va_list args) { if (debug_level_match(level)) { char mesg[MSG_SIZE], prefix[32] = ""; vsnprintf(mesg, MSG_SIZE, format, args); if (level & 0xffffff00) snprintf(prefix, 32, " [%d]", (int)((level & 0xffffff00) >> 8)); if (prog_name()) msg_out(prog_dbg(), "%s: debug:%s%*s%s\n", prog_name(), prefix, level & 0xff, "", mesg); else msg_out(prog_dbg(), "debug:%s%*s%s\n", prefix, level & 0xff, "", mesg); } } /* =item C Outputs an error message to the program's error message destination. If the program's name has been supplied using I, the message will be preceded by the name, a colon, and a space. C is a I-like format string which processes any remaining arguments in the same way as I. The message is followed by a newline. Returns -1. =cut */ int error(const char *format, ...) { va_list args; va_start(args, format); verror(format, args); va_end(args); return -1; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ int verror(const char *format, va_list args) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); if (prog_name()) msg_out(prog_err(), "%s: %s\n", prog_name(), mesg); else msg_out(prog_err(), "%s\n", mesg); return -1; } /* =item C Outputs an error message to the program's error message destination, and then calls I with a return code of C. If the program's name was supplied using I, the message will be preceded by the name, a colon, and a space. This is followed by the string C<"fatal: ">. C is a I-like format string which processes any remaining arguments in the same way as I. The message is followed by a newline. B Never use this in a library. Only an application can decide which errors are fatal. =cut */ void fatal(const char *format, ...) { va_list args; va_start(args, format); vfatal(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vfatal(const char *format, va_list args) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); error("fatal: %s", mesg); exit(EXIT_FAILURE); } /* =item C Outputs an error message to the program's error message destination and then calls I. If the program's name was supplied using I, the message will be preceded by the name, a colon, and a space. This is followed by the string C<"dump: ">. C is a I-like format string which processes any remaining arguments in the same way as I. The message is followed by a newline. B Never use this in a library. Only an application can decide which errors are fatal. =cut */ void dump(const char *format, ...) { va_list args; va_start(args, format); vdump(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdump(const char *format, va_list args) { char mesg[MSG_SIZE]; vsnprintf(mesg, MSG_SIZE, format, args); error("dump: %s", mesg); abort(); } /* =item C Outputs an alert message of the given C to the program's alert message destination. If the program's name has been supplied using I, the message will be preceded by the name, a colon, and a space. C is a I-like format string which processes any remaining arguments in the same way as I. The message is followed by a newline. Note that this only works when the program's alert message destination is a simple syslog destination. If the alert message destination is anything else (including a multiplexing message destination containing syslog destinations), C is ignored. =cut */ void alert(int priority, const char *format, ...) { va_list args; va_start(args, format); valert(priority, format, args); va_end(args); } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void valert(int priority, const char *format, va_list args) { Msg *alert; char mesg[MSG_SIZE]; int err; vsnprintf(mesg, MSG_SIZE, format, args); alert = prog_alert(); if ((err = msg_wrlock(alert))) { set_errno(err); return; } msg_syslog_set_priority_unlocked(alert, priority); if (prog_name()) msg_out_unlocked(alert, "%s: %s\n", prog_name(), mesg); else msg_out_unlocked(alert, "%s\n", mesg); if ((err = msg_unlock(alert))) set_errno(err); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C, and a newline (rather than just a newline). =cut */ void debugsysf(size_t level, const char *format, ...) { if (debug_level_match(level)) { va_list args; va_start(args, format); vdebugsysf(level, format, args); va_end(args); } } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdebugsysf(size_t level, const char *format, va_list args) { if (debug_level_match(level)) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); debugf(level, "%s: %s", mesg, strerror(errno_saved)); } } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C, and a newline (rather than just a newline). =cut */ int errorsys(const char *format, ...) { va_list args; va_start(args, format); verrorsys(format, args); va_end(args); return -1; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ int verrorsys(const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); return error("%s: %s", mesg, strerror(errno_saved)); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C, and a newline (rather than just a newline). =cut */ void fatalsys(const char *format, ...) { va_list args; va_start(args, format); vfatalsys(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vfatalsys(const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); fatal("%s: %s", mesg, strerror(errno_saved)); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C, and a newline (rather than just a newline). =cut */ void dumpsys(const char *format, ...) { va_list args; va_start(args, format); vdumpsys(format, args); va_end(args); /* unreached */ } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void vdumpsys(const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); dump("%s: %s", mesg, strerror(errno_saved)); } /* =item C Equivalent to I except that the message is followed by a colon, a space, the string representation of C, and a newline (rather than just a newline). =cut */ void alertsys(int priority, const char *format, ...) { va_list args; va_start(args, format); valertsys(priority, format, args); va_end(args); } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ void valertsys(int priority, const char *format, va_list args) { char mesg[MSG_SIZE]; int errno_saved = errno; vsnprintf(mesg, MSG_SIZE, format, args); alert(priority, "%s: %s", mesg, strerror(errno_saved)); } /* =item C Sets C to C and returns -1. =cut */ int (set_errno)(int errnum) { errno = errnum; return -1; } /* =item C Sets C to C and returns C. =cut */ void *(set_errnull)(int errnum) { errno = errnum; return NULL; } /* =item C Sets C to C and returns C as a function pointer. This is useful because I doesn't like casting normal pointers into function pointers. =cut */ void (*(set_errnullf)(int errnum))() { errno = errnum; return NULL; } /* =item C< #define debug(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. debug((1, "rc=%d", rc)) Note that the I macro always generates its own semicolon. This used to only be the case when C was not defined, but it is now consistent. =item C< #define vdebug(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. vdebug((1, format, args)) Note that the I macro always generates its own semicolon. This used to only be the case when C was not defined, but it is now consistent. =item C< #define debugsys(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. debugsys((1, "fd=%d", fd)) Note that the I macro always generates its own semicolon. This used to only be the case when C was not defined, but it is now consistent. =item C< #define vdebugsys(args)> Calls I unless C is defined. C must be supplied with extra parentheses. e.g. vdebugsys((1, format, args)) Note that the I macro always generates its own semicolon. This used to only be the case when C was not defined, but it is now consistent. =item C< #define check(cond, mesg)> Like I but includes a string argument, C, for including an explanation of the condition tested and then calls I to terminate the program. This means the message will be sent to the right place(s) rather than just to C. B Like I, this function is largely useless. It should never be left in production code (because it's rude), so you need to write code to handle error conditions properly anyway. You might as well not bother using I or I in the first place. =back =head1 MT-Level I =head1 EXAMPLES Send a range of messages to default locations: #include #include #include int main(int ac, char **av) { prog_init(); prog_set_debug_level(1); prog_set_verbosity_level(1); msg("This is a %s\n", "message"); verbose(0, "This is a %s message (level %d)", "verbose", 0); verbose(1, "This is a %s message (level %d)", "verbose", 1); verbose(2, "This is a %s message (level %d)", "verbose", 2); debug((0, "This is a %s message (level %d)", "debug", 0)) debug((1, "This is a %s message (level %d)", "debug", 1)) debug((2, "This is a %s message (level %d)", "debug", 2)) alert(LOG_ERR, "This is an %s message", "alert"); error("This is an %s message", "error"); fatal("This is a %s message", "fatal error"); return EXIT_SUCCESS; } =head1 SEE ALSO I, I, I, I =head1 AUTHOR 20230824 raf =cut */ #endif #ifdef TEST #include #include #include #include #include #include "str.h" int verify(int test, const char *name, const char *result) { char buf[BUFSIZ]; int fd; ssize_t bytes; if ((fd = open(name, O_RDONLY)) == -1) { printf("Test%d: failed to create err file: %s (%s)\n", test, name, strerror(errno)); return 1; } memset(buf, 0, BUFSIZ); bytes = read(fd, buf, BUFSIZ); close(fd); unlink(name); if (bytes == -1) { printf("Test%d: failed to read err file: %s (%s)\n", test, name, strerror(errno)); return 1; } if (!strstr(buf, result)) { printf("Test%d: err file produced incorrect input:\nshould contain:\n%s\nwas:\n%s\n", test, result, buf); return 1; } return 0; } int verifysys(int test, const char *name, const char *result, int err) { char buf[BUFSIZ]; snprintf(buf, BUFSIZ, result, strerror(err)); return verify(test, name, buf); } int msg_filter_prefix(void **mesgp, const void *mesg, size_t mesglen) { return asprintf((char **)mesgp, "[%d] %.*s", 12345, (int)mesglen, (char *)mesg); } int main(int ac, char **av) { const char * const out = "err.out"; const char * const err = "err.err"; const char * const dbg = "err.dbg"; const char * const alertfile = "err.alert"; const char * const core = "core"; const char * const core2 = "err.core"; /* OpenBSD */ char buf[BUFSIZ]; int rci; void *rcp; void (*rcfp)(); const char *results[12] = { "msg\n", "verbosemsg\n", "debug: debugmsg\n", "errormsg\n", "fatal: fatalmsg\n", "dump: dumpmsg\n", "debug: debugsysmsg: %s\n", "errorsysmsg: %s\n", "fatal: fatalsysmsg: %s\n", "dump: dumpsysmsg: %s\n", "debug: [1] lexer debugmsg\n" "debug: [2] parser debugmsg\n" "debug: [1] lexer debugmsg\n" "debug: [2] parser debugmsg\n" "debug: [4] interp debugmsg\n" "debug: global debugmsg\n", "alertmsg\n" "alertsysmsg: " /* followed by "Success" or "Error 0" */ }; pid_t pid; int errors = 0; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "err"); /* Test debug, verbose and error */ prog_set_debug_level(1); prog_set_verbosity_level(1); prog_out_file(out); msg("msg\n"); errors += verify(1, out, results[0]); prog_out_file(out); verbose(1, "verbosemsg"); errors += verify(2, out, results[1]); prog_dbg_file(dbg); debugf(1, "debugmsg"); errors += verify(3, dbg, results[2]); prog_err_file(err); error("errormsg"); errors += verify(4, err, results[3]); /* Test filtered debug */ prog_dbg_file(dbg); if (prog_dbg_push_filter(msg_filter_prefix) == -1) ++errors, printf("Test5: prog_dbg_push_filter() failed\n"); else { debugf(1, "filteredmsg"); errors += verify(6, dbg, "[12345] debug: filteredmsg"); } /* Test fatal */ switch (pid = fork()) { case 0: { prog_err_file(err); fatal("fatalmsg"); } case -1: { ++errors; printf("Test7: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors, printf("Test7: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_FAILURE) ++errors, printf("Test7: failed: %s %d (expected: exit code %d)\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status), EXIT_FAILURE); } } errors += verify(7, err, results[4]); /* Test dump */ switch (pid = fork()) { case 0: { prog_err_file(err); unlink(core); unlink(core2); dump("dumpmsg"); } case -1: { ++errors; printf("Test8: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors, printf("Test8: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) { ++errors, printf("Test8: failed: %s %d (expected: received signal %d)\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status), SIGABRT); } /* Check for core dump, but ulimit might prevent them */ #ifdef WCOREDUMP if (WIFSIGNALED(status) && !WCOREDUMP(status)) { struct rlimit limit[1] = {{ 0, 0 }}; if (getrlimit(RLIMIT_CORE, limit) != -1 && limit->rlim_cur != 0) printf("Warning: dump() produced no core file (even though ulimit -c %d)\n", (int)limit->rlim_cur); } #endif /* Core files might be anywhere these days, but they might be here */ unlink(core); unlink(core2); } } errors += verify(8, err, results[5]); /* Test debugsys, errorsys */ prog_dbg_file(dbg); set_errno(EPERM); debugsysf(1, "debugsysmsg"); errors += verifysys(9, dbg, results[6], EPERM); prog_err_file(err); set_errno(ENOENT); errorsys("errorsysmsg"); errors += verifysys(10, err, results[7], ENOENT); /* Test fatalsys */ switch (pid = fork()) { case 0: { prog_err_file(err); set_errno(EPERM); fatalsys("fatalsysmsg"); } case -1: { ++errors; printf("Test11: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test11: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_FAILURE) ++errors, printf("Test11: failed: %s %d (expected: exit code %d)\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status), EXIT_FAILURE); } } errors += verifysys(11, err, results[8], EPERM); /* Test dumpsys */ switch (pid = fork()) { case 0: { prog_err_file(err); unlink(core); unlink(core2); set_errno(ENOENT); dumpsys("dumpsysmsg"); } case -1: { ++errors; printf("Test12: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test12: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) ++errors, printf("Test12: failed: %s %d (expected: received signal %d)\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status), SIGABRT); /* Check for core dump, but ulimit might prevent them */ #ifdef WCOREDUMP if (WIFSIGNALED(status) && !WCOREDUMP(status)) { struct rlimit limit[1] = {{ 0, 0 }}; if (getrlimit(RLIMIT_CORE, limit) != -1 && limit->rlim_cur != 0) printf("Warning: dump() produced no core file (even though ulimit -c %d)\n", (int)limit->rlim_cur); } #endif /* Core files might be anywhere these days, but they might be here */ unlink(core); unlink(core2); } } errors += verifysys(12, err, results[9], ENOENT); /* Test check true */ switch (pid = fork()) { case 0: { int i = 1; prog_err_file(err); unlink(core); unlink(core2); set_errno(ENOENT); check(i == 1, "checkmsg"); _exit(EXIT_SUCCESS); } case -1: { ++errors; printf("Test13: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test13: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) ++errors, printf("Test13: failed: %s %d (expected: exit code %d)\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status), EXIT_SUCCESS); } } /* Test check false */ switch (pid = fork()) { case 0: { int i = 1; prog_err_file(err); unlink(core); unlink(core2); set_errno(ENOENT); check(i == 0, "checkmsg"); _exit(EXIT_SUCCESS); } case -1: { ++errors; printf("Test14: failed to perform test - fork() failed (%s)\n", strerror(errno)); break; } default: { int status; if (waitpid(pid, &status, 0) == -1) { ++errors; printf("Test14: failed to wait for test - waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); break; } if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) ++errors, printf("Test14: failed: %s %d (expected: received signal %d)\n", WIFSIGNALED(status) ? "received signal" : "exit code", WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status), SIGABRT); /* Check for core dump, but ulimit might prevent them */ #ifdef WCOREDUMP if (WIFSIGNALED(status) && !WCOREDUMP(status)) { struct rlimit limit[1] = {{ 0, 0 }}; if (getrlimit(RLIMIT_CORE, limit) != -1 && limit->rlim_cur != 0) printf("Warning: dump() produced no core file (even though ulimit -c %d)\n", (int)limit->rlim_cur); } #endif /* Core files might be anywhere these days, but they might be here */ unlink(core); unlink(core2); } } snprintf(buf, BUFSIZ, "dump: Internal Error: %s: %s [", "i == 0", "checkmsg"); errors += verify(14, err, buf); /* Test debug sections */ #define LEXER_SECTION (1 << 8) #define PARSER_SECTION (2 << 8) #define INTERP_SECTION (4 << 8) prog_set_debug_level(LEXER_SECTION | PARSER_SECTION | 1); prog_dbg_file(dbg); msg_set_timestamp_format(""); debugf(LEXER_SECTION | 1, "lexer debugmsg"); /* yes */ debugf(LEXER_SECTION | 4, "lexer debugmsg"); /* no (level too high) */ debugf(PARSER_SECTION | 1, "parser debugmsg"); /* yes */ debugf(INTERP_SECTION | 1, "interp debugmsg"); /* no (wrong section) */ debugf(1, "global debugmsg"); /* no (no section to match) */ prog_set_debug_level(1); debugf(LEXER_SECTION | 1, "lexer debugmsg"); /* yes */ debugf(LEXER_SECTION | 4, "lexer debugmsg"); /* no (level too high) */ debugf(PARSER_SECTION | 1, "parser debugmsg"); /* yes */ debugf(INTERP_SECTION | 1, "interp debugmsg"); /* yes */ debugf(1, "global debugmsg"); /* yes */ debugf(4, "global debugmsg"); /* no (level too high) */ errors += verify(15, dbg, results[10]); #undef LEXER_SECTION #undef PARSER_SECTION #undef INTERP_SECTION /* Test alert() and alertsys() */ prog_alert_file(alertfile); alert(LOG_INFO, "alertmsg"); errno = 0; alertsys(LOG_INFO, "alertsysmsg"); errors += verify(16, alertfile, results[11]); prog_out_none(); prog_err_none(); prog_dbg_none(); prog_alert_none(); /* Test set_errno() and set_errnull() and set_errnullf() */ errno = 0; if ((rci = set_errno(EINVAL)) != -1) ++errors, printf("Test17: set_errno(EINVAL) failed (returned %d, not %d)\n", rci, -1); else if (errno != EINVAL) ++errors, printf("Test17: set_errno(EINVAL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); errno = 0; if ((rcp = set_errnull(EINVAL)) != NULL) ++errors, printf("Test18: set_errnull(EINVAL) failed (returned %p, not %p)\n", rcp, (void *)NULL); else if (errno != EINVAL) ++errors, printf("Test18: set_errnull(EINVAL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); errno = 0; if ((rcfp = set_errnullf(EINVAL)) != NULL) /* ++errors, printf("Test19: set_errnullf(EINVAL) failed (returned %p, not %p)\n", rcfp, (void *)NULL); */ ++errors, printf("Test19: set_errnullf(EINVAL) failed (returned something other than null)\n"); else if (errno != EINVAL) ++errors, printf("Test19: set_errnullf(EINVAL) failed (errno = %s, not %s)\n", strerror(errno), strerror(EINVAL)); if (errors) printf("%d/19 tests failed\n", errors); else printf("All tests passed\n"); return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* vi:set ts=4 sw=4: */ daemon-0.8.4/libslack/socks.h0000644000175000017500000000366114471544123014162 0ustar rafraf/* * libslack - https://libslack.org * * Copyright (C) 1999-2004, 2010, 2020-2023 raf * * 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, see . * * 20230824 raf */ #ifndef LIBSLACK_SOCKS_H #define LIBSLACK_SOCKS_H #ifdef SOCKS #ifdef SOCKS4 #define SOCKS_PREFIX R #else #define SOCKS_PREFIX SOCKS #endif #define connect SOCKS_PREFIX ## connect #define getsockname SOCKS_PREFIX ## getsockname #define getpeername SOCKS_PREFIX ## getpeername #define bind SOCKS_PREFIX ## bind #define accept SOCKS_PREFIX ## accept #define listen SOCKS_PREFIX ## listen #define select SOCKS_PREFIX ## select #define recvfrom SOCKS_PREFIX ## recvfrom #define sendto SOCKS_PREFIX ## sendto #define recv SOCKS_PREFIX ## recv #define send SOCKS_PREFIX ## send #define read SOCKS_PREFIX ## read #define write SOCKS_PREFIX ## write #define rresvport SOCKS_PREFIX ## rresvport #define shutdown SOCKS_PREFIX ## shutdown #define listen SOCKS_PREFIX ## listen #define close SOCKS_PREFIX ## close #define dup SOCKS_PREFIX ## dup #define dup2 SOCKS_PREFIX ## dup2 #define fclose SOCKS_PREFIX ## fclose #define gethostbyname SOCKS_PREFIX ## gethostbyname #undef SOCKS_PREFIX #endif #endif /* vi:set ts=4 sw=4: */ daemon-0.8.4/libslack/str.c0000644000175000017500000100401414471544126013640 0ustar rafraf/* * libslack - https://libslack.org * * Copyright (C) 1999-2004, 2010, 2020-2023 raf * * 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, see . * * 20230824 raf */ /* * $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ * $OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $ * Modified by raf * * Copyright (c) 1998 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* =head1 NAME I - string module =head1 SYNOPSIS #include #include typedef struct String String; typedef struct StringTR StringTR; enum StringAlignment { ALIGN_LEFT = '<', ALIGN_RIGHT = '>', ALIGN_CENTRE = '|', ALIGN_CENTER = '|', ALIGN_FULL = '=' }; enum StringTROption { TR_COMPLEMENT = 1, TR_DELETE = 2, TR_SQUASH = 4 }; typedef enum StringAlignment StringAlignment; typedef enum StringTROption StringTROption; String *str_create(const char *format, ...); String *str_create_with_locker(Locker *locker, const char *format, ...); String *str_vcreate(const char *format, va_list args); String *str_vcreate_with_locker(Locker *locker, const char *format, va_list args); String *str_create_sized(size_t size, const char *format, ...); String *str_create_with_locker_sized(Locker *locker, size_t size, const char *format, ...); String *str_vcreate_sized(size_t size, const char *format, va_list args); String *str_vcreate_with_locker_sized(Locker *locker, size_t size, const char *format, va_list args); String *str_copy(const String *str); String *str_copy_unlocked(const String *str); String *str_copy_with_locker(Locker *locker, const String *str); String *str_copy_with_locker_unlocked(Locker *locker, const String *str); String *str_fgetline(FILE *stream); String *str_fgetline_with_locker(Locker *locker, FILE *stream); void str_release(String *str); void *str_destroy(String **str); int str_rdlock(const String *str); int str_wrlock(const String *str); int str_unlock(const String *str); int str_empty(const String *str); int str_empty_unlocked(const String *str); ssize_t str_length(const String *str); ssize_t str_length_unlocked(const String *str); char *cstr(const String *str); ssize_t str_set_length(String *str, size_t length); ssize_t str_set_length_unlocked(String *str, size_t length); ssize_t str_recalc_length(String *str); ssize_t str_recalc_length_unlocked(String *str); String *str_clear(String *str); String *str_clear_unlocked(String *str); String *str_remove(String *str, ssize_t index); String *str_remove_unlocked(String *str, ssize_t index); String *str_remove_range(String *str, ssize_t index, ssize_t range); String *str_remove_range_unlocked(String *str, ssize_t index, ssize_t range); String *str_insert(String *str, ssize_t index, const char *format, ...); String *str_insert_unlocked(String *str, ssize_t index, const char *format, ...); String *str_vinsert(String *str, ssize_t index, const char *format, va_list args); String *str_vinsert_unlocked(String *str, ssize_t index, const char *format, va_list args); String *str_insert_str(String *str, ssize_t index, const String *src); String *str_insert_str_unlocked(String *str, ssize_t index, const String *src); String *str_append(String *str, const char *format, ...); String *str_append_unlocked(String *str, const char *format, ...); String *str_vappend(String *str, const char *format, va_list args); String *str_vappend_unlocked(String *str, const char *format, va_list args); String *str_append_str(String *str, const String *src); String *str_append_str_unlocked(String *str, const String *src); String *str_prepend(String *str, const char *format, ...); String *str_prepend_unlocked(String *str, const char *format, ...); String *str_vprepend(String *str, const char *format, va_list args); String *str_vprepend_unlocked(String *str, const char *format, va_list args); String *str_prepend_str(String *str, const String *src); String *str_prepend_str_unlocked(String *str, const String *src); String *str_replace(String *str, ssize_t index, ssize_t range, const char *format, ...); String *str_replace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, ...); String *str_vreplace(String *str, ssize_t index, ssize_t range, const char *format, va_list args); String *str_vreplace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, va_list args); String *str_replace_str(String *str, ssize_t index, ssize_t range, const String *src); String *str_replace_str_unlocked(String *str, ssize_t index, ssize_t range, const String *src); String *str_substr(const String *str, ssize_t index, ssize_t range); String *str_substr_unlocked(const String *str, ssize_t index, ssize_t range); String *str_substr_with_locker(Locker *locker, const String *str, ssize_t index, ssize_t range); String *str_substr_with_locker_unlocked(Locker *locker, const String *str, ssize_t index, ssize_t range); String *substr(const char *str, ssize_t index, ssize_t range); String *substr_with_locker(Locker *locker, const char *str, ssize_t index, ssize_t range); String *str_splice(String *str, ssize_t index, ssize_t range); String *str_splice_unlocked(String *str, ssize_t index, ssize_t range); String *str_splice_with_locker(Locker *locker, String *str, ssize_t index, ssize_t range); String *str_splice_with_locker_unlocked(Locker *locker, String *str, ssize_t index, ssize_t range); String *str_repeat(size_t count, const char *format, ...); String *str_repeat_with_locker(Locker *locker, size_t count, const char *format, ...); String *str_vrepeat(size_t count, const char *format, va_list args); String *str_vrepeat_with_locker(Locker *locker, size_t count, const char *format, va_list args); int str_tr(String *str, const char *from, const char *to, int option); int str_tr_unlocked(String *str, const char *from, const char *to, int option); int str_tr_str(String *str, const String *from, const String *to, int option); int str_tr_str_unlocked(String *str, const String *from, const String *to, int option); int tr(char *str, const char *from, const char *to, int option); StringTR *tr_compile(const char *from, const char *to, int option); StringTR *tr_compile_with_locker(Locker *locker, const char *from, const char *to, int option); StringTR *str_tr_compile(const String *from, const String *to, int option); StringTR *str_tr_compile_unlocked(const String *from, const String *to, int option); StringTR *str_tr_compile_with_locker(Locker *locker, const String *from, const String *to, int option); StringTR *str_tr_compile_with_locker_unlocked(Locker *locker, const String *from, const String *to, int option); void tr_release(StringTR *table); void *tr_destroy(StringTR **table); int str_tr_compiled(String *str, StringTR *table); int str_tr_compiled_unlocked(String *str, StringTR *table); int tr_compiled(char *str, StringTR *table); List *str_regexpr(const char *pattern, const String *text, int cflags, int eflags); List *str_regexpr_unlocked(const char *pattern, const String *text, int cflags, int eflags); List *str_regexpr_with_locker(Locker *locker, const char *pattern, const String *text, int cflags, int eflags); List *str_regexpr_with_locker_unlocked(Locker *locker, const char *pattern, const String *text, int cflags, int eflags); List *regexpr(const char *pattern, const char *text, int cflags, int eflags); List *regexpr_with_locker(Locker *locker, const char *pattern, const char *text, int cflags, int eflags); int regexpr_compile(regex_t *compiled, const char *pattern, int cflags); void regexpr_release(regex_t *compiled); List *str_regexpr_compiled(const regex_t *compiled, const String *text, int eflags); List *str_regexpr_compiled_unlocked(const regex_t *compiled, const String *text, int eflags); List *str_regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const String *text, int eflags); List *str_regexpr_compiled_with_locker_unlocked(Locker *locker, const regex_t *compiled, const String *text, int eflags); List *regexpr_compiled(const regex_t *compiled, const char *text, int eflags); List *regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const char *text, int eflags); String *str_regsub(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all); String *str_regsub_unlocked(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all); String *str_regsub_compiled(const regex_t *compiled, const char *replacement, String *text, int eflags, int all); String *str_regsub_compiled_unlocked(const regex_t *compiled, const char *replacement, String *text, int eflags, int all); List *str_fmt(const String *str, size_t line_width, StringAlignment alignment); List *str_fmt_unlocked(const String *str, size_t line_width, StringAlignment alignment); List *str_fmt_with_locker(Locker *locker, const String *str, size_t line_width, StringAlignment alignment); List *str_fmt_with_locker_unlocked(Locker *locker, const String *str, size_t line_width, StringAlignment alignment); List *fmt(const char *str, size_t line_width, StringAlignment alignment); List *fmt_with_locker(Locker *locker, const char *str, size_t line_width, StringAlignment alignment); List *str_split(const String *str, const char *delim); List *str_split_unlocked(const String *str, const char *delim); List *str_split_with_locker(Locker *locker, const String *str, const char *delim); List *str_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim); List *split(const char *str, const char *delim); List *split_with_locker(Locker *locker, const char *str, const char *delim); List *str_regexpr_split(const String *str, const char *delim, int cflags, int eflags); List *str_regexpr_split_unlocked(const String *str, const char *delim, int cflags, int eflags); List *str_regexpr_split_with_locker(Locker *locker, const String *str, const char *delim, int cflags, int eflags); List *str_regexpr_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim, int cflags, int eflags); List *regexpr_split(const char *str, const char *delim, int cflags, int eflags); List *regexpr_split_with_locker(Locker *locker, const char *str, const char *delim, int cflags, int eflags); String *str_join(const List *list, const char *delim); String *str_join_unlocked(const List *list, const char *delim); String *str_join_with_locker(Locker *locker, const List *list, const char *delim); String *str_join_with_locker_unlocked(Locker *locker, const List *list, const char *delim); String *join(const List *list, const char *delim); String *join_with_locker(Locker *locker, const List *list, const char *delim); int str_soundex(const String *str); int str_soundex_unlocked(const String *str); int soundex(const char *str); String *str_trim(String *str); String *str_trim_unlocked(String *str); char *trim(char *str); String *str_trim_left(String *str); String *str_trim_left_unlocked(String *str); char *trim_left(char *str); String *str_trim_right(String *str); String *str_trim_right_unlocked(String *str); char *trim_right(char *str); String *str_squeeze(String *str); String *str_squeeze_unlocked(String *str); char *squeeze(char *str); String *str_quote(const String *str, const char *quotable, char quote_char); String *str_quote_unlocked(const String *str, const char *quotable, char quote_char); String *str_quote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char); String *str_quote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char); String *quote(const char *str, const char *quotable, char quote_char); String *quote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char); String *str_unquote(const String *str, const char *quotable, char quote_char); String *str_unquote_unlocked(const String *str, const char *quotable, char quote_char); String *str_unquote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char); String *str_unquote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char); String *unquote(const char *str, const char *quotable, char quote_char); String *unquote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char); String *str_encode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_encode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_encode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_encode_with_locker_unlocked(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_decode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_decode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_decode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_decode_with_locker_unlocked(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable); String *encode(const char *str, const char *uncoded, const char *coded, char quote_char, int printable); String *encode_with_locker(Locker *locker, const char *str, const char *uncoded, const char *coded, char quote_char, int printable); String *decode(const char *str, const char *uncoded, const char *coded, char quote_char, int printable); String *decode_with_locker(Locker *locker, const char *str, const char *uncoded, const char *coded, char quote_char, int printable); String *str_lc(String *str); String *str_lc_unlocked(String *str); char *lc(char *str); String *str_lcfirst(String *str); String *str_lcfirst_unlocked(String *str); char *lcfirst(char *str); String *str_uc(String *str); String *str_uc_unlocked(String *str); char *uc(char *str); String *str_ucfirst(String *str); String *str_ucfirst_unlocked(String *str); char *ucfirst(char *str); int str_chop(String *str); int str_chop_unlocked(String *str); int chop(char *str); int str_chomp(String *str); int str_chomp_unlocked(String *str); int chomp(char *str); int str_bin(const String *str); int str_bin_unlocked(const String *str); int bin(const char *str); int str_hex(const String *str); int str_hex_unlocked(const String *str); int hex(const char *str); int str_oct(const String *str); int str_oct_unlocked(const String *str); int oct(const char *str); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); size_t strlcpy(char *dst, const char *src, size_t size); size_t strlcat(char *dst, const char *src, size_t size); char *cstrcpy(char *dst, const char *src); char *cstrcat(char *dst, const char *src); char *cstrchr(const char *str, int c); char *cstrpbrk(const char *str, const char *brk); char *cstrrchr(const char *str, int c); char *cstrstr(const char *str, const char *srch); int asprintf(char **str, const char *format, ...); int vasprintf(char **str, const char *format, va_list args); =head1 DESCRIPTION This module provides text strings that grow and shrink automatically, and functions for manipulating them. Some of the functions were modelled on the I module. Others were modelled on the string functions and operators in I and I. Others came from I. =over 4 =cut */ #ifndef _BSD_SOURCE #define _BSD_SOURCE /* For snprintf() on OpenBSD-4.7 */ #endif #ifndef _DEFAULT_SOURCE #define _DEFAULT_SOURCE /* New name for _BSD_SOURCE */ #endif #ifndef __BSD_VISIBLE #define __BSD_VISIBLE 1 /* For ntohl() on FreeBSD-8.0 */ #endif #ifndef _NETBSD_SOURCE #define _NETBSD_SOURCE /* For ntohl() on NetBSD-5.0.2 */ #endif #include "config.h" #include "std.h" #include #include "err.h" #include "str.h" #include "mem.h" #include "fio.h" #ifndef HAVE_SNPRINTF #include "snprintf.h" #endif struct String { size_t size; /* number of bytes allocated */ size_t length; /* number of bytes used (including nul) */ char *str; /* vector of characters */ Locker *locker; /* locking strategy for this string */ }; #define CHARSET 256 struct StringTR { int squash; /* whether or not to squash duplicate characters */ short table[CHARSET]; /* the translation table */ Locker *locker; /* locking strategy for this structure */ }; typedef enum { TRCODE_NOMAP = -1, TRCODE_DELETE = -2 } TRCode; #define is_alpha(c) isalpha((int)(unsigned char)(c)) #define is_alnum(c) isalnum((int)(unsigned char)(c)) #define is_print(c) isprint((int)(unsigned char)(c)) #define is_space(c) isspace((int)(unsigned char)(c)) #define is_digit(c) isdigit((int)(unsigned char)(c)) #define is_xdigit(c) isxdigit((int)(unsigned char)(c)) #define to_lower(c) tolower((int)(unsigned char)(c)) #define to_upper(c) toupper((int)(unsigned char)(c)) #ifndef TEST /* Minimum string length: must be a power of 2 */ static const size_t MIN_STRING_SIZE = 32; /* Maximum bytes for an empty string: must be a power of 2 greater than MIN_STRING_SIZE */ static const size_t MIN_EMPTY_STRING_SIZE = 1024; void (flockfile)(FILE *stream); /* Missing from old glibc headers */ void (funlockfile)(FILE *stream); #ifndef HAVE_FLOCKFILE #define flockfile(stream) #define funlockfile(stream) #define getc_unlocked(stream) getc(stream) #endif /* C Allocates enough memory to add C extra bytes to C if necessary. On success, returns C<0>. On error, returns C<-1>. */ static int grow(String *str, size_t bytes) { int grown = 0; while (str->length + bytes > str->size) { if (str->size) str->size <<= 1; else str->size = MIN_STRING_SIZE; grown = 1; } if (grown) return mem_resize(&str->str, str->size) ? 0 : -1; return 0; } /* C Allocates less memory for removing C bytes from C if necessary. On success, returns C<0>. On error, returns C<-1>. */ static int shrink(String *str, size_t bytes) { int shrunk = 0; while (str->length - bytes < str->size >> 1) { if (str->size <= MIN_EMPTY_STRING_SIZE) break; str->size >>= 1; shrunk = 1; } if (shrunk) return mem_resize(&str->str, str->size) ? 0 : -1; return 0; } /* C Slides C's bytes, starting at C, C positions to the right to make room for more. On success, returns C<0>. On error, returns C<-1>. */ static int expand(String *str, ssize_t index, size_t range) { if (grow(str, range) == -1) return -1; memmove(str->str + index + range, str->str + index, (str->length - index) * sizeof(*str->str)); str->length += range; return 0; } /* C Slides C's bytes, starting at C + C, C positions to the left to close a gap starting at C. On success, returns C<0>. On error, returns C<-1>. */ static int contract(String *str, ssize_t index, size_t range) { memmove(str->str + index, str->str + index + range, (str->length - index - range) * sizeof(*str->str)); if (shrink(str, range) == -1) return -1; str->length -= range; return 0; } /* C Expands or contracts C as required so that C occupies C. On success, returns C<0>. On error, returns C<-1>. */ static int adjust(String *str, ssize_t index, size_t range, size_t length) { if (range < length) return expand(str, index + range, length - range); if (range > length) return contract(str, index + length, range - length); return 0; } /* =item C Creates a I specified by C and the following arguments as in I. On success, returns the new string. It is the caller's responsibility to deallocate the new string with I or I. It is strongly recommended to use I, because it also sets the pointer variable to C. On error, returns C with C set appropriately. B String *str = str_create(buf); // EVIL String *str = str_create("%s", buf); // GOOD =cut */ String *str_create(const char *format, ...) { String *str; va_list args; va_start(args, format); str = str_vcreate_with_locker_sized(NULL, MIN_STRING_SIZE, format, args); va_end(args); return str; } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronized by C. =cut */ String *str_create_with_locker(Locker *locker, const char *format, ...) { String *str; va_list args; va_start(args, format); str = str_vcreate_with_locker_sized(locker, MIN_STRING_SIZE, format, args); va_end(args); return str; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vcreate(const char *format, va_list args) { return str_vcreate_with_locker_sized(NULL, MIN_STRING_SIZE, format, args); } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronized by C. =cut */ String *str_vcreate_with_locker(Locker *locker, const char *format, va_list args) { return str_vcreate_with_locker_sized(locker, MIN_STRING_SIZE, format, args); } /* =item C Creates a I specified by C and the following arguments as in I. The initial allocation for the string data is at least C bytes. On success, returns the new string. It is the caller's responsibility to deallocate the new string with I or I. It is strongly recommended to use I, because it also sets the pointer variable to C. On error, returns C with C set appropriately. =cut */ String *str_create_sized(size_t size, const char *format, ...) { String *str; va_list args; va_start(args, format); str = str_vcreate_with_locker_sized(NULL, size, format, args); va_end(args); return str; } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ String *str_create_with_locker_sized(Locker *locker, size_t size, const char *format, ...) { String *str; va_list args; va_start(args, format); str = str_vcreate_with_locker_sized(locker, size, format, args); va_end(args); return str; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vcreate_sized(size_t size, const char *format, va_list args) { return str_vcreate_with_locker_sized(NULL, size, format, args); } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ #ifndef va_copy #define va_copy(dst, src) __va_copy((dst), (src)) #endif String *str_vcreate_with_locker_sized(Locker *locker, size_t size, const char *format, va_list args) { String *str; char *buf = NULL; ssize_t length; unsigned int bit; va_list args_copy; for (bit = 1; bit; bit <<= 1) { if (bit >= size) { size = bit; break; } } if (!bit) return set_errnull(EINVAL); if (!format) format = ""; for (;; size <<= 1) { if (!mem_resize(&buf, size)) { mem_release(buf); return NULL; } #ifdef va_copy va_copy(args_copy, args); length = vsnprintf(buf, size, format, args_copy); va_end(args_copy); #else length = vsnprintf(buf, size, format, args); #endif if (length != -1 && length < size) break; } if (!(str = mem_new(String))) /* XXX decouple */ { mem_release(buf); return NULL; } str->size = size; str->length = length + 1; str->str = buf; str->locker = locker; return str; } /* =item C Creates a copy of C. On success, returns the copy. It is the caller's responsibility to deallocate the new string with I or I. It is strongly recommended to use I, because it also sets the pointer variable to C. On error, returns C with C set appropriately. =cut */ String *str_copy(const String *str) { return str_copy_with_locker(NULL, str); } /* =item C Equivalent to I except that C is not read-locked. =cut */ String *str_copy_unlocked(const String *str) { return str_copy_with_locker_unlocked(NULL, str); } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ String *str_copy_with_locker(Locker *locker, const String *str) { return str_substr_with_locker(locker, str, 0, -1); } /* =item C Equivalent to I except that C is not read-locked. =cut */ String *str_copy_with_locker_unlocked(Locker *locker, const String *str) { return str_substr_with_locker_unlocked(locker, str, 0, -1); } /* =item C Similar to I except that it recognises UNIX (C<"\n">), DOS/Windows (C<"\r\n">) and old Macintosh (C<"\r">) line endings (even different line endings in the same file), and it can read a line of any size into the I that it returns. Reading stops after the C, or after the end of the line is reached. Line endings are always stored as a single C<"\n"> character. A C is placed after the last character in the buffer. On success, returns a new I. It is the caller's responsibility to deallocate the new string with I or I. It is strongly recommended to use I, because it also sets the pointer variable to C. On error, or when the end of file occurs while no characters have been read, returns C. Calls to this function can be mixed with calls to other input functions from the I library on the same input stream. =cut */ String *str_fgetline(FILE *stream) { return str_fgetline_with_locker(NULL, stream); } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ String *str_fgetline_with_locker(Locker *locker, FILE *stream) { String *ret = NULL; char buf[BUFSIZ]; flockfile(stream); while (fgetline_unlocked(buf, BUFSIZ, stream)) { if (!ret) { if (!(ret = str_create_with_locker(locker, "%s", buf))) break; } else if (!str_append(ret, "%s", buf)) { str_destroy(&ret); break; } if (cstr(ret)[ret->length - 2] == '\n') break; } funlockfile(stream); return ret; } /* =item C Releases (deallocates) C. =cut */ void str_release(String *str) { Locker *locker; if (!str) return; if (str_wrlock(str)) return; locker = str->locker; mem_release(str->str); mem_release(str); locker_unlock(locker); } /* =item C Destroys (deallocates and sets to C) C<*str>. Returns C. B strings shared by multiple threads must not be destroyed until after all threads have finished with it. =cut */ void *str_destroy(String **str) { if (str && *str) { str_release(*str); *str = NULL; } return NULL; } /* =item C Claims a read lock on C (if C was created with a I). Clients must call this before calling I (for the purpose of reading the raw string data) on a string that was created with a I. It is the client's responsibility to call I when finished with the raw string data. It is also needed when multiple read-only I module functions need to be called atomically. It is the caller's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are I and any read-only I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ #define str_rdlock(str) ((str) ? locker_rdlock((str)->locker) : EINVAL) #define str_wrlock(str) ((str) ? locker_wrlock((str)->locker) : EINVAL) #define str_unlock(str) ((str) ? locker_unlock((str)->locker) : EINVAL) int (str_rdlock)(const String *str) { return str_rdlock(str); } /* =item C Claims a write lock on C (if C was created with a I). Clients need to call this before calling I (for the purpose of modifying the raw string data) on a string that was created with a I. It is the client's responsibility to call I when finished with the raw string data. It is also needed when multiple read/write I module functions need to be called atomically. It is the caller's responsibility to call I after the atomic operation. The only functions that may be called on C between calls to I and I are I and any I module functions whose name ends with C<_unlocked>. On success, returns C<0>. On error, returns an error code. =cut */ int (str_wrlock)(const String *str) { return str_wrlock(str); } /* =item C Unlocks a read lock or a write lock on C obtained with I or I (if C was created with a I). On success, returns C<0>. On error, returns an error code. =cut */ int (str_unlock)(const String *str) { return str_unlock(str); } /* =item C Returns whether or not C is the empty string. On error, returns C<-1> with C set appropriately. =cut */ int str_empty(const String *str) { int empty; int err; if (!str) return set_errno(EINVAL); if ((err = str_rdlock(str))) return set_errno(err); empty = str_empty_unlocked(str); if ((err = str_unlock(str))) return set_errno(err); return empty; } /* =item C Equivalent to I except that C is not read-locked. =cut */ int str_empty_unlocked(const String *str) { if (!str) return set_errno(EINVAL); return (str->length == 1); } /* =item C Returns the length of C. On error, returns C<-1> with C set appropriately. =cut */ ssize_t str_length(const String *str) { size_t length; int err; if (!str) return set_errno(EINVAL); if ((err = str_rdlock(str))) return set_errno(err); length = str_length_unlocked(str); if ((err = str_unlock(str))) return set_errno(err); return length; } /* =item C Equivalent to I except that C is not read-locked. =cut */ ssize_t str_length_unlocked(const String *str) { if (!str) return set_errno(EINVAL); return str->length - 1; } /* =item C Returns the raw I string in C. Do not use this pointer to extend the length of the string. It's OK to use it to reduce the length of the string, provided that you call I or I immediately afterwards. When used on a string that is shared by multiple threads, I must appear between calls to I or I and I. =cut */ char *cstr(const String *str) { if (!str) return set_errnull(EINVAL); return str->str; } /* =item C Sets the length of C to C. Only needed after the raw I string returned by I has been used to shorten a string. On success, returns the length of C. On error, returns C<-1> with C set appropriately. =cut */ ssize_t str_set_length(String *str, size_t length) { ssize_t len; int err; if (!str) return set_errno(EINVAL); if ((err = str_wrlock(str))) return set_errno(err); len = str_set_length_unlocked(str, length); if ((err = str_unlock(str))) return set_errno(err); return len; } /* =item C Equivalent to I except that C is not write-locked. =cut */ ssize_t str_set_length_unlocked(String *str, size_t length) { if (!str || length >= str->length) return set_errno(EINVAL); str->length = length + 1; str->str[str->length - 1] = '\0'; return str->length - 1; } /* =item C Calculates and stores the length of C. Only needed after the raw I string returned by I has been used to shorten a string. Note: Treats C as a C-terminated string and should be avoided. Use I instead. On success, returns the length of C. On error, returns C<-1> with C set appropriately. =cut */ ssize_t str_recalc_length(String *str) { ssize_t len; int err; if (!str) return set_errno(EINVAL); if ((err = str_wrlock(str))) return set_errno(err); len = str_recalc_length_unlocked(str); if ((err = str_unlock(str))) return set_errno(err); return len; } /* =item C Equivalent to I except that C is not write-locked. =cut */ ssize_t str_recalc_length_unlocked(String *str) { if (!str) return set_errno(EINVAL); str->length = strlen(str->str) + 1; return str->length - 1; } /* =item C Makes C the empty string. On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_clear(String *str) { return str_remove_range(str, 0, -1); } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_clear_unlocked(String *str) { return str_remove_range_unlocked(str, 0, -1); } /* =item C Removes the C'th character from C. If C is negative, it refers to a character position relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_remove(String *str, ssize_t index) { return str_remove_range(str, index, 1); } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_remove_unlocked(String *str, ssize_t index) { return str_remove_range_unlocked(str, index, 1); } /* =item C Removes C characters from C starting at C. If C or C are negative, they refer to character positions relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_remove_range(String *str, ssize_t index, ssize_t range) { String *ret; int err; if (!str) return set_errnull(EINVAL); if ((err = str_wrlock(str))) return set_errnull(err); ret = str_remove_range_unlocked(str, index, range); if ((err = str_unlock(str))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_remove_range_unlocked(String *str, ssize_t index, ssize_t range) { if (!str) return set_errnull(EINVAL); if (index < 0) index = str->length + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = str->length + range - index; if (range < 0) return set_errnull(EINVAL); if (str->length - 1 < index + range) return set_errnull(EINVAL); contract(str, index, range); return str; } /* =item C Adds the string specified by C to C at position C. If C is negative, it refers to a character position relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_insert(String *str, ssize_t index, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vinsert(str, index, format, args); va_end(args); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_insert_unlocked(String *str, ssize_t index, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vinsert_unlocked(str, index, format, args); va_end(args); return ret; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vinsert(String *str, ssize_t index, const char *format, va_list args) { String *ret; int err; if (!str) return set_errnull(EINVAL); if ((err = str_wrlock(str))) return set_errnull(err); ret = str_vinsert_unlocked(str, index, format, args); if ((err = str_unlock(str))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_vinsert_unlocked(String *str, ssize_t index, const char *format, va_list args) { String *tmp, *ret; if (!str) return set_errnull(EINVAL); if (index < 0) index = str->length + index; if (index < 0) return set_errnull(EINVAL); if (str->length - 1 < index) return set_errnull(EINVAL); if (!(tmp = str_vcreate(format, args))) return NULL; ret = str_insert_str_unlocked(str, index, tmp); str_release(tmp); return ret; } /* =item C Inserts C into C, starting at position C. If C is negative, it refers to a character position relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_insert_str(String *str, ssize_t index, const String *src) { String *ret; int err; if (!str || !src) return set_errnull(EINVAL); if ((err = str_rdlock(src))) return set_errnull(err); if ((err = str_wrlock(str))) { str_unlock(src); return set_errnull(err); } ret = str_insert_str_unlocked(str, index, src); if ((err = str_unlock(str))) { str_unlock(src); return set_errnull(err); } if ((err = str_unlock(src))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write-locked and C is not read-locked. Note: If C needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with I and I. =cut */ String *str_insert_str_unlocked(String *str, ssize_t index, const String *src) { size_t length; if (!str || !src) return set_errnull(EINVAL); if (index < 0) index = str->length + index; if (index < 0) return set_errnull(EINVAL); if (str->length - 1 < index) return set_errnull(EINVAL); length = src->length - 1; if (expand(str, index, length) == -1) return NULL; memcpy(str->str + index, src->str, length); return str; } /* =item C Appends the string specified by C to C. On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_append(String *str, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vinsert(str, -1, format, args); va_end(args); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_append_unlocked(String *str, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vinsert_unlocked(str, -1, format, args); va_end(args); return ret; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vappend(String *str, const char *format, va_list args) { return str_vinsert(str, -1, format, args); } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_vappend_unlocked(String *str, const char *format, va_list args) { return str_vinsert_unlocked(str, -1, format, args); } /* =item C Appends C to C. On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_append_str(String *str, const String *src) { return str_insert_str(str, -1, src); } /* =item C Equivalent to I except that C is not write-locked and C is not read-locked. Note: If C needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with I and I. =cut */ String *str_append_str_unlocked(String *str, const String *src) { return str_insert_str_unlocked(str, -1, src); } /* =item C Prepends the string specified by C to C. On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_prepend(String *str, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vinsert(str, 0, format, args); va_end(args); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_prepend_unlocked(String *str, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vinsert_unlocked(str, 0, format, args); va_end(args); return ret; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vprepend(String *str, const char *format, va_list args) { return str_vinsert(str, 0, format, args); } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_vprepend_unlocked(String *str, const char *format, va_list args) { return str_vinsert_unlocked(str, 0, format, args); } /* =item C Prepends C to C. On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_prepend_str(String *str, const String *src) { return str_insert_str(str, 0, src); } /* =item C Equivalent to I except that C is not write-locked and C is not read-locked. Note: If C needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with I and I. =cut */ String *str_prepend_str_unlocked(String *str, const String *src) { return str_insert_str_unlocked(str, 0, src); } /* =item C Replaces C characters in C, starting at C, with the string specified by C. If C or C are negative, they refer to character positions relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns C. On error, returns C with C set appropriately. =cut */ String *str_replace(String *str, ssize_t index, ssize_t range, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vreplace(str, index, range, format, args); va_end(args); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_replace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vreplace_unlocked(str, index, range, format, args); va_end(args); return ret; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vreplace(String *str, ssize_t index, ssize_t range, const char *format, va_list args) { String *tmp, *ret; if (!str) return set_errnull(EINVAL); if (!(tmp = str_vcreate(format, args))) return NULL; ret = str_replace_str(str, index, range, tmp); str_release(tmp); return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_vreplace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, va_list args) { String *tmp, *ret; if (!str) return set_errnull(EINVAL); if (!(tmp = str_vcreate(format, args))) return NULL; ret = str_replace_str_unlocked(str, index, range, tmp); str_release(tmp); return ret; } /* =item C Replaces C characters in C, starting at C, with C. If C or C are negative, they refer to character positions relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, return C. On error, returns C with C set appropriately. =cut */ String *str_replace_str(String *str, ssize_t index, ssize_t range, const String *src) { String *ret; int err; if (!src || !str) return set_errnull(EINVAL); if ((err = str_rdlock(src))) return set_errnull(err); if ((err = str_wrlock(str))) { str_unlock(src); return set_errnull(err); } ret = str_replace_str_unlocked(str, index, range, src); if ((err = str_unlock(str))) { str_unlock(src); return set_errnull(err); } if ((err = str_unlock(src))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not write-locked and C is not read-locked. Note: If C needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with I and I. =cut */ String *str_replace_str_unlocked(String *str, ssize_t index, ssize_t range, const String *src) { size_t length; if (!src || !str) return set_errnull(EINVAL); if (index < 0) index = str->length + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = str->length + range - index; if (range < 0) return set_errnull(EINVAL); if (str->length - 1 < index + range) return set_errnull(EINVAL); length = src->length - 1; if (adjust(str, index, range, length) == -1) return NULL; memcpy(str->str + index, src->str, length); return str; } /* =item C Creates a new I object consisting of C characters from C, starting at C. If C or C are negative, they refer to character positions relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns the new string. It is the caller's responsibility to deallocate the new string with I or I. On error, returns C with C set appropriately. =cut */ String *str_substr(const String *str, ssize_t index, ssize_t range) { return str_substr_with_locker(NULL, str, index, range); } /* =item C Equivalent to I except that C is not read-locked. =cut */ String *str_substr_unlocked(const String *str, ssize_t index, ssize_t range) { return str_substr_with_locker_unlocked(NULL, str, index, range); } /* =item C Equivalent to I except that multiple threads accessing the new substring will be synchronised by C. =cut */ String *str_substr_with_locker(Locker *locker, const String *str, ssize_t index, ssize_t range) { String *ret; int err; if (!str) return set_errnull(EINVAL); if ((err = str_rdlock(str))) return set_errnull(err); ret = str_substr_with_locker_unlocked(locker, str, index, range); if ((err = str_unlock(str))) return set_errnull(err); return ret; } /* =item C Equivalent to I except that C is not read-locked. =cut */ String *str_substr_with_locker_unlocked(Locker *locker, const String *str, ssize_t index, ssize_t range) { String *ret; if (!str) return set_errnull(EINVAL); if (index < 0) index = str->length + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = str->length + range - index; if (range < 0) return set_errnull(EINVAL); if (str->length - 1 < index + range) return set_errnull(EINVAL); if (!(ret = str_create_with_locker_sized(locker, range + 1, NULL))) return NULL; memcpy(ret->str, str->str + index, range); ret->length = range + 1; ret->str[ret->length - 1] = '\0'; return ret; } /* =item C Equivalent to I but works on an ordinary I string. =cut */ String *substr(const char *str, ssize_t index, ssize_t range) { return substr_with_locker(NULL, str, index, range); } /* =item C Equivalent to I except that multiple threads accessing the new substring will be synchronised by C. Note that no locking is performed on C as it is a raw I string. =cut */ String *substr_with_locker(Locker *locker, const char *str, ssize_t index, ssize_t range) { String *ret; size_t len = 0; if (!str) return set_errnull(EINVAL); if (index < 0 || range < 0) len = strlen(str) + 1; if (index < 0) index = len + index; if (index < 0) return set_errnull(EINVAL); if (range < 0) range = len + range - index; if (range < 0) return set_errnull(EINVAL); if (!(ret = str_create_with_locker_sized(locker, range + 1, NULL))) return NULL; memcpy(ret->str, str + index, range); ret->length = range + 1; ret->str[ret->length - 1] = '\0'; return ret; } /* =item C Removes a substring from C starting at C of length C characters. If C or C are negative, they refer to character positions relative to the end of the string (C<-1> is the position after the last character, C<-2> is the position of the last character, and so on). On success, returns the substring. It is the caller's responsibility to deallocate the new substring with I or I. It is strongly recommended to use I, because it also sets the pointer variable to C. On error, returns C with C set appropriately. =cut */ String *str_splice(String *str, ssize_t index, ssize_t range) { return str_splice_with_locker(NULL, str, index, range); } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_splice_unlocked(String *str, ssize_t index, ssize_t range) { return str_splice_with_locker_unlocked(NULL, str, index, range); } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ String *str_splice_with_locker(Locker *locker, String *str, ssize_t index, ssize_t range) { String *ret; int err; if (!str) return set_errnull(EINVAL); if ((err = str_wrlock(str))) return set_errnull(err); ret = str_splice_with_locker_unlocked(locker, str, index, range); if ((err = str_unlock(str))) { str_release(ret); return set_errnull(err); } return ret; } /* =item C Equivalent to I except that C is not write-locked. =cut */ String *str_splice_with_locker_unlocked(Locker *locker, String *str, ssize_t index, ssize_t range) { String *ret; if (!str) return set_errnull(EINVAL); if (!(ret = str_substr_with_locker_unlocked(locker, str, index, range))) return NULL; if (!str_remove_range_unlocked(str, index, range)) { str_release(ret); return NULL; } return ret; } /* =item C Creates a new I containing the string determined by C repeated C times. On success, return the new string. It is the caller's responsibility to deallocate the new string with I or I. It is strongly recommended to use I, because it also sets the pointer variable to C. On error, returns C with C set appropriately. =cut */ String *str_repeat(size_t count, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vrepeat_with_locker(NULL, count, format, args); va_end(args); return ret; } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ String *str_repeat_with_locker(Locker *locker, size_t count, const char *format, ...) { String *ret; va_list args; va_start(args, format); ret = str_vrepeat_with_locker(locker, count, format, args); va_end(args); return ret; } /* =item C Equivalent to I with the variable argument list specified directly as for I. =cut */ String *str_vrepeat(size_t count, const char *format, va_list args) { return str_vrepeat_with_locker(NULL, count, format, args); } /* =item C Equivalent to I except that multiple threads accessing the new string will be synchronised by C. =cut */ String *str_vrepeat_with_locker(Locker *locker, size_t count, const char *format, va_list args) { String *tmp, *ret; ssize_t length; size_t i; if (!(tmp = str_vcreate(format, args))) return NULL; if ((length = str_length(tmp)) == -1) return NULL; if (!(ret = str_create_with_locker_sized(locker, length * count + 1, NULL))) { str_release(tmp); return NULL; } for (i = 0; i < count; ++i) { if (!str_append_str(ret, tmp)) { str_release(tmp); str_release(ret); return NULL; } } str_release(tmp); return ret; } /* =item C This is just like the I operator. The following documentation was taken from I. Transliterates all occurrences of the characters in C with the corresponding character in C. On success, returns the number of characters replaced or deleted. On error, returns C<-1> with C set appropriately. A character range can be specified with a hyphen, so C does the same replacement as C. Note also that the whole range idea is rather unportable between character sets - and even within character sets they might cause results you probably didn't expect. A sound principle is to use only ranges that begin from and end at either alphabets of equal case (a-e, A-E), or digits (0-4). Anything else is unsafe. If in doubt, spell out the character sets in full. Options: TR_COMPLEMENT Complement from. TR_DELETE Delete found but unreplaced characters. TR_SQUASH Squash duplicate replaced characters. If C is specified, C is complemented. If C is specified, any characters specified by C not found in C are deleted. (Note that this is slightly more flexible than the behavior of some tr programs, which delete anything they find in C.) If C is specified, sequences of characters that were transliterated to the same character are squashed down to a single instance of the character. If C is used, C is always interpreted exactly as specified. Otherwise, if C is shorter than C, the final character is replicated till it is long enough. If C is empty or C, C is replicated. This latter is useful for counting characters in a class or for squashing character sequences in a class. Examples: str_tr(s, "A-Z", "a-z", 0); // canonicalize to lower case str_tr(s, "a-z", "A-Z", 0); // canonicalize to upper case str_tr(s, "a-zA-Z", "A-Za-z", 0); // swap upper and lower case str_tr(s, "*", "*", 0); // count the stars in str str_tr(s, "0-9", "", 0); // count the digits in $_ str_tr(s, "a-zA-Z", "", TR_SQUASH); // bookkeeper -> bokeper str_tr(s, "a-zA-Z", " ", TR_COMPLEMENT | TR_SQUASH); // change non-alphas to single space str_tr(c, "a-zA-Z", "n-za-mN-ZA-M", 0); // Rot13 from = str_create("\200-\377"); to = str_create("%c-\177", '\000'); str_tr_str(s, from, to, 0); // clear 8th bit If multiple transliterations are given for a character, only the first one is used: str_tr(str, "AAA", "XYZ", 0); will transliterate any A to X. =cut */ static StringTR *tr_compile_table(StringTR *table, const char *from, const char *to, int option); int str_tr(String *str, const char *from, const char *to, int option) { StringTR table[1]; if (!str || !from) return set_errno(EINVAL); table->locker = NULL; if (!tr_compile_table(table, from, to, option)) return -1; return str_tr_compiled(str, table); } /* =item C Equivalent to I except that C is not write-locked. =cut */ int str_tr_unlocked(String *str, const char *from, const char *to, int option) { StringTR table[1]; if (!str || !from) return set_errno(EINVAL); table->locker = NULL; if (!tr_compile_table(table, from, to, option)) return -1; return str_tr_compiled_unlocked(str, table); } /* =item C Equivalent to I except that C and C are I objects. This is needed when C or C need to contain C characters. =cut */ static StringTR *str_tr_compile_table(StringTR *table, const String *from, const String *to, int option); int str_tr_str(String *str, const String *from, const String *to, int option) { StringTR table[1]; if (!str || !from) return set_errno(EINVAL); table->locker = NULL; if (!str_tr_compile_table(table, from, to, option)) return -1; return str_tr_compiled(str, table); } /* =item C Equivalent to I except that C is not write-locked and C and C are not read-locked. Note: If C and C need to be read-locked, it is the caller's responsibility to lock and unlock them explicitly with I and I. =cut */ static StringTR *str_tr_compile_table_unlocked(StringTR *table, const String *from, const String *to, int option); int str_tr_str_unlocked(String *str, const String *from, const String *to, int option) { StringTR table[1]; if (!str || !from) return set_errno(EINVAL); table->locker = NULL; if (!str_tr_compile_table_unlocked(table, from, to, option)) return -1; return str_tr_compiled_unlocked(str, table); } /* =item C Equivalent to I but works on an ordinary I string. =cut */ int tr(char *str, const char *from, const char *to, int option) { StringTR table[1]; if (!str || !from) return set_errno(EINVAL); table->locker = NULL; if (!tr_compile_table(table, from, to, option)) return -1; return tr_compiled(str, table); } /* =item C Compiles C, C and C