aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2024-01-30 20:51:38 +0000
committerLaurent Bercot <ska@appnovation.com>2024-01-30 20:51:38 +0000
commit21d51f7e0a639a3224ffc45dc3c06854decf1d45 (patch)
tree87c4a36f66510e79d7ec8315361bb8bbbb5767f5
downloadapaste-21d51f7e0a639a3224ffc45dc3c06854decf1d45.tar.gz
Initial commit
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--.gitignore10
-rw-r--r--AUTHORS2
-rw-r--r--CONTRIBUTING5
-rw-r--r--COPYING13
-rw-r--r--DCO37
-rw-r--r--INSTALL167
-rw-r--r--Makefile148
-rw-r--r--NEWS6
-rw-r--r--README23
-rwxr-xr-xconfigure514
-rw-r--r--doc/apaste.html114
-rw-r--r--doc/apastec.html88
-rw-r--r--doc/apasted.html137
-rw-r--r--doc/index.html110
-rw-r--r--doc/upgrade.html28
-rw-r--r--package/deps-build2
-rw-r--r--package/deps.mak16
-rw-r--r--package/info4
-rw-r--r--package/modes3
-rw-r--r--package/targets.mak7
-rwxr-xr-xpatch-for-solaris21
-rw-r--r--src/client/PROTOCOL.txt19
-rw-r--r--src/client/apaste.c101
-rw-r--r--src/client/apastec.c155
-rw-r--r--src/client/apastec.h17
-rw-r--r--src/client/deps-exe/apaste1
-rw-r--r--src/client/deps-exe/apastec4
-rw-r--r--src/client/send_file.c93
-rw-r--r--src/include-local/apaste-common.h9
-rw-r--r--src/server/apasted.c339
-rw-r--r--src/server/deps-exe/apasted3
-rwxr-xr-xtools/gen-deps.sh100
-rwxr-xr-xtools/install.sh69
33 files changed, 2365 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5c3c565
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.o
+*.a
+*.lo
+*.so
+*.so.*
+/config.mak
+/src/include/apaste/config.h
+/apaste
+/apastec
+/apasted
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..7a708a1
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Main author:
+ Laurent Bercot <ska-skaware@skarnet.org>
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 0000000..6279422
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,5 @@
+ Please add a Signed-Off-By: line at the end of your commit,
+which certifies that you have the right and authority to pass
+it on as an open-source patch, as explicited in the Developer's
+Certificate of Origin available in this project's DCO file,
+or at https://developercertificate.org/
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..b747199
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,13 @@
+Copyright (c) 2024 Laurent Bercot <ska-skaware@skarnet.org>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/DCO b/DCO
new file mode 100644
index 0000000..8201f99
--- /dev/null
+++ b/DCO
@@ -0,0 +1,37 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+1 Letterman Drive
+Suite D4700
+San Francisco, CA, 94129
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..4765a67
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,167 @@
+Build Instructions
+------------------
+
+* Requirements
+ ------------
+
+ - A POSIX-compliant C development environment
+ - GNU make version 3.81 or later
+ - skalibs version 2.14.1.1 or later: https://skarnet.org/software/skalibs/
+ - s6-networking version 2.7.0.2 or later: https://skarnet.org/software/s6-networking/
+
+ This software will run on any operating system that implements
+POSIX.1-2008, available at:
+ https://pubs.opengroup.org/onlinepubs/9699919799/
+
+
+* Standard usage
+ --------------
+
+ ./configure && make && sudo make install
+
+ will work for most users.
+ It will install the binaries in /usr/bin.
+
+ You can strip the binaries and libraries of their extra symbols via
+"make strip" before the "make install" phase. It will shave a few bytes
+off them.
+
+
+* Customization
+ -------------
+
+ You can customize paths via flags given to configure.
+ See ./configure --help for a list of all available configure options.
+
+
+* Environment variables
+ ---------------------
+
+ Controlling a build process via environment variables is a big and
+dangerous hammer. You should try and pass flags to configure instead;
+nevertheless, a few standard environment variables are recognized.
+
+ If the CC environment variable is set, its value will override compiler
+detection by configure. The --host=HOST option will still add a HOST-
+prefix to the value of CC.
+
+ The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags
+auto-detected by configure. To entirely override the flags set by
+configure instead, use make variables.
+
+
+* Make variables
+ --------------
+
+ You can invoke make with a few variables for more configuration.
+
+ CC, CFLAGS, CPPFLAGS, LDFLAGS, LDLIBS, AR, RANLIB, STRIP, INSTALL and
+CROSS_COMPILE can all be overridden on the make command line. This is
+an even bigger hammer than running ./configure with environment
+variables, so it is advised to only do this when it is the only way of
+obtaining the behaviour you want.
+
+ DESTDIR can be given on the "make install" command line in order to
+install to a staging directory.
+
+
+* Shared libraries
+ ----------------
+
+ Software from skarnet.org is small enough that shared libraries are
+generally not worth using. Static linking is simpler and incurs less
+runtime overhead and less points of failure: so by default, shared
+libraries are not built and binaries are linked against the static
+versions of the skarnet.org libraries. Nevertheless, you can:
+ * build shared libraries: --enable-shared
+ * link binaries against shared libraries: --disable-allstatic
+
+
+* Static binaries
+ ---------------
+
+ By default, binaries are linked against static versions of all the
+libraries they depend on, except for the libc. You can enforce
+linking against the static libc with --enable-static-libc.
+
+ Be aware that the GNU libc behaves badly with static linking and
+produces huge executables, which is why it is not the default.
+Other libcs are better suited to static linking, for instance
+musl: https://musl-libc.org/
+
+
+* Cross-compilation
+ -----------------
+
+ skarnet.org packages centralize all the difficulty of
+cross-compilation in one place: skalibs. Once you have
+cross-compiled skalibs, the rest is easy.
+
+ * Use the --host=HOST option to configure, HOST being the triplet
+for your target.
+ * Make sure your cross-toolchain binaries (i.e. prefixed with HOST-)
+are accessible via your PATH environment variable.
+ * Make sure to use the correct version of skalibs for your target,
+and the correct sysdeps directory, making use of the
+--with-include, --with-lib, --with-dynlib and --with-sysdeps
+options as necessary.
+
+
+* The slashpackage convention
+ ---------------------------
+
+ The slashpackage convention (http://cr.yp.to/slashpackage.html)
+is a package installation scheme that provides a few guarantees
+over other conventions such as the FHS, for instance fixed
+absolute pathnames. skarnet.org packages support it: use the
+--enable-slashpackage option to configure, or
+--enable-slashpackage=DIR for a prefixed DIR/package tree.
+This option will activate slashpackage support during the build
+and set slashpackage-compatible installation directories.
+If $package_home is the home of the package, defined as
+DIR/package/$category/$package-$version with the variables
+read from the package/info file, then:
+
+ --dynlibdir is set to $package_home/library.so
+ --bindir is set to $package_home/command
+ --sbindir is also set to $package_home/command (slashpackage
+differentiates root-only binaries by their Unix rights, not their
+location in the filesystem)
+ --libexecdir is also set to $package_home/command (slashpackage
+does not need a specific directory for internal binaries)
+ --libdir is set to $package_home/library
+ --includedir is set to $package_home/include
+
+ --prefix is pretty much ignored when you use --enable-slashpackage.
+You should probably not use both --enable-slashpackage and --prefix.
+
+ When using slashpackage, two additional Makefile targets are
+available after "make install":
+ - "make update" changes the default version of the software to the
+freshly installed one. (This is useful when you have several installed
+versions of the same software, which slashpackage supports.)
+ - "make -L global-links" adds links from /command and /library.so to the
+default version of the binaries and shared libraries. The "-L" option to
+make is necessary because targets are symbolic links, and the default make
+behaviour is to check the pointed file's timestamp and not the symlink's
+timestamp.
+
+
+* Absolute pathnames
+ ------------------
+
+ You may want to use fixed absolute pathnames even if you're not
+following the slashpackage convention: for instance, the Nix packaging
+system prefers calling binaries with immutable paths rather than rely on
+PATH resolution. If you are in that case, use the --enable-absolute-paths
+option to configure. This will ensure that programs calling binaries from
+this package will call them with their full installation path (in bindir)
+without relying on a PATH search.
+
+
+* Out-of-tree builds
+ ------------------
+
+ skarnet.org packages do not support out-of-tree builds. They
+are small, so it does not cost much to duplicate the entire
+source tree if parallel builds are needed.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b82361b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,148 @@
+#
+# This Makefile requires GNU make.
+#
+# Do not make changes here.
+# Use the included .mak files.
+#
+
+it: all
+
+make_need := 3.81
+ifeq "" "$(strip $(filter $(make_need), $(firstword $(sort $(make_need) $(MAKE_VERSION)))))"
+fail := $(error Your make ($(MAKE_VERSION)) is too old. You need $(make_need) or newer)
+endif
+
+CC = $(error Please use ./configure first)
+
+STATIC_LIBS :=
+SHARED_LIBS :=
+INTERNAL_LIBS :=
+EXTRA_TARGETS :=
+LIB_DEFS :=
+
+define library_definition
+LIB$(firstword $(subst =, ,$(1))) := lib$(lastword $(subst =, ,$(1))).$(if $(DO_ALLSTATIC),a,so).xyzzy
+ifdef DO_SHARED
+SHARED_LIBS += lib$(lastword $(subst =, ,$(1))).so.xyzzy
+endif
+ifdef DO_STATIC
+STATIC_LIBS += lib$(lastword $(subst =, ,$(1))).a.xyzzy
+endif
+endef
+
+-include config.mak
+include package/targets.mak
+
+$(foreach var,$(LIB_DEFS),$(eval $(call library_definition,$(var))))
+
+include package/deps.mak
+
+version_m := $(basename $(version))
+version_M := $(basename $(version_m))
+version_l := $(basename $(version_M))
+CPPFLAGS_ALL := $(CPPFLAGS_AUTO) $(CPPFLAGS)
+CFLAGS_ALL := $(CFLAGS_AUTO) $(CFLAGS)
+ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)
+CFLAGS_SHARED := -fPIC
+else
+CFLAGS_SHARED :=
+endif
+LDFLAGS_ALL := $(LDFLAGS_AUTO) $(LDFLAGS)
+AR := $(CROSS_COMPILE)ar
+RANLIB := $(CROSS_COMPILE)ranlib
+STRIP := $(CROSS_COMPILE)strip
+INSTALL := ./tools/install.sh
+
+ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS)
+ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) $(INTERNAL_LIBS)
+ALL_INCLUDES := $(wildcard src/include/$(package)/*.h)
+
+all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES) $(EXTRA_INCLUDES)
+
+clean:
+ @exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo) $(EXTRA_TARGETS)
+
+distclean: clean
+ @exec rm -f config.mak src/include/$(package)/config.h
+
+tgz: distclean
+ @. package/info && \
+ rm -rf /tmp/$$package-$$version && \
+ cp -a . /tmp/$$package-$$version && \
+ cd /tmp && \
+ tar -zpcv --owner=0 --group=0 --numeric-owner --exclude=.git* -f /tmp/$$package-$$version.tar.gz $$package-$$version && \
+ exec rm -rf /tmp/$$package-$$version
+
+strip: $(ALL_LIBS) $(ALL_BINS)
+ifneq ($(strip $(STATIC_LIBS)),)
+ exec $(STRIP) -x -R .note -R .comment $(STATIC_LIBS)
+endif
+ifneq ($(strip $(ALL_BINS)$(SHARED_LIBS)),)
+ exec $(STRIP) -R .note -R .comment $(ALL_BINS) $(SHARED_LIBS)
+endif
+
+install: install-dynlib install-libexec install-bin install-lib install-include
+install-dynlib: $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(dynlibdir)/lib%.so)
+install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%)
+install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%)
+install-lib: $(STATIC_LIBS:lib%.a.xyzzy=$(DESTDIR)$(libdir)/lib%.a)
+install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) $(EXTRA_INCLUDES:src/include/%.h=$(DESTDIR)$(includedir)/%.h)
+
+ifneq ($(exthome),)
+
+$(DESTDIR)$(exthome): $(DESTDIR)$(home)
+ exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome)
+
+update: $(DESTDIR)$(exthome)
+
+global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M)) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%)
+
+$(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/%
+ exec $(INSTALL) -D -l ..$(subst $(sproot),,$(exthome))/command/$(<F) $@
+
+$(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M): $(DESTDIR)$(dynlibdir)/lib%.so.$(version_M)
+ exec $(INSTALL) -D -l ..$(subst $(sproot),,$(exthome))/library.so/$(<F) $@
+
+.PHONY: update global-links
+
+endif
+
+$(DESTDIR)$(dynlibdir)/lib%.so $(DESTDIR)$(dynlibdir)/lib%.so.$(version_M): lib%.so.xyzzy
+ $(INSTALL) -D -m 755 $< $@.$(version) && \
+ $(INSTALL) -l $(@F).$(version) $@.$(version_M) && \
+ exec $(INSTALL) -l $(@F).$(version_M) $@
+
+$(DESTDIR)$(libexecdir)/% $(DESTDIR)$(bindir)/%: % package/modes
+ exec $(INSTALL) -D -m 600 $< $@
+ grep -- ^$(@F) < package/modes | { read name mode owner && \
+ if [ x$$owner != x ] ; then chown -- $$owner $@ ; fi && \
+ chmod $$mode $@ ; }
+
+$(DESTDIR)$(libdir)/lib%.a: lib%.a.xyzzy
+ exec $(INSTALL) -D -m 644 $< $@
+
+$(DESTDIR)$(includedir)/$(package)/%.h: src/include/$(package)/%.h
+ exec $(INSTALL) -D -m 644 $< $@
+
+$(DESTDIR)$(includedir)/%.h: src/include/%.h
+ exec $(INSTALL) -D -m 644 $< $@
+
+%.o: %.c
+ exec $(CC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) -c -o $@ $<
+
+%.lo: %.c
+ exec $(CC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) $(CFLAGS_SHARED) -c -o $@ $<
+
+$(ALL_BINS):
+ exec $(CC) -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL) $(LDFLAGS_NOSHARED) $^ $(EXTRA_LIBS) $(LDLIBS)
+
+lib%.a.xyzzy:
+ exec $(AR) rc $@ $^
+ exec $(RANLIB) $@
+
+lib%.so.xyzzy:
+ exec $(CC) -o $@ $(CFLAGS_ALL) $(CFLAGS_SHARED) $(LDFLAGS_ALL) $(LDFLAGS_SHARED) -Wl,-soname,$(patsubst lib%.so.xyzzy,lib%.so.$(version_M),$@) $^ $(EXTRA_LIBS) $(LDLIBS)
+
+.PHONY: it all clean distclean tgz strip install install-dynlib install-bin install-lib install-include
+
+.DELETE_ON_ERROR:
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..5ef0e8e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,6 @@
+Changelog for apaste.
+
+In 0.0.1.0
+----------
+
+ - Initial release.
diff --git a/README b/README
new file mode 100644
index 0000000..61d0f93
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+apaste - a command-line pastebin
+--------------------------------
+
+ apaste is a simple implementation of a pastebin, to publish data
+to a server via a short command line.
+ It was originally made for the Adélie Linux distribution, but
+can be used anywhere.
+ See https://skarnet.org/software/apaste/ for details.
+
+
+* Installation
+ ------------
+
+ See the INSTALL file.
+
+
+* Contact information
+ -------------------
+
+ Laurent Bercot <ska-skaware at skarnet.org>
+
+ Please use the <skaware at list.skarnet.org> mailing-list for
+questions about apaste.
diff --git a/configure b/configure
new file mode 100755
index 0000000..6fa974d
--- /dev/null
+++ b/configure
@@ -0,0 +1,514 @@
+#!/bin/sh
+
+cd `dirname "$0"`
+. package/info
+
+usage () {
+cat <<EOF
+Usage: $0 [OPTION]... [TARGET]
+
+Defaults for the options are specified in brackets.
+
+System types:
+ --target=TARGET configure to run on target TARGET [detected]
+ --host=TARGET same as --target
+
+Installation directories:
+ --prefix=PREFIX main installation prefix [/]
+ --exec-prefix=EPREFIX installation prefix for executable files [PREFIX]
+
+Fine tuning of the installation directories:
+ --dynlibdir=DIR shared library files [PREFIX/lib]
+ --bindir=BINDIR user executables [EPREFIX/bin]
+ --libexecdir=DIR package-scoped executables [EPREFIX/libexec]
+ --libdir=DIR static library files [PREFIX/lib/$package]
+ --includedir=DIR C header files [PREFIX/include]
+
+ If no --prefix option is given, by default libdir (but not dynlibdir) will be
+ /usr/lib/$package, and includedir will be /usr/include.
+
+Dependencies:
+ --with-sysdeps=DIR use sysdeps in DIR [PREFIX/lib/skalibs/sysdeps]
+ --with-include=DIR add DIR to the list of searched directories for headers
+ --with-lib=DIR add DIR to the list of searched directories for static libraries
+ --with-dynlib=DIR add DIR to the list of searched directories for shared libraries
+
+ If no --prefix option is given, by default sysdeps will be fetched from
+ /usr/lib/skalibs/sysdeps.
+
+Optional features:
+ --enable-shared build shared libraries [disabled]
+ --disable-static do not build static libraries [enabled]
+ --disable-allstatic do not prefer linking against static libraries [enabled]
+ --enable-static-libc make entirely static binaries [disabled]
+ --disable-all-pic do not build executables or static libs as PIC [enabled]
+ --enable-slashpackage[=ROOT] assume /package installation at ROOT [disabled]
+ --enable-absolute-paths do not rely on PATH to access this package's binaries,
+ hardcode absolute BINDIR/foobar paths instead [disabled]
+ --enable-nsss use the nsss library for user information [disabled]
+
+ --with-default-server=SERVER use SERVER as default server for apaste [adelielinux.xyz]
+ --with-default-port=PORT use PORT as default clear port for apaste [9999]
+ --with-default-tlsport=PORT use PORT as default TLS-enabled port for apaste [9997]
+ --with-default-cadir=CADIR use CADIR as trust anchor certificate location [/etc/ssl/certs]
+ --with-default-tls use secure connections by default [no]
+ --with-tmpdir=TMPDIR make temporary files in TMPDIR [/tmp]
+
+EOF
+exit 0
+}
+
+# Helper functions
+
+# If your system does not have printf, you can comment this, but it is
+# generally not a good idea to use echo.
+# See http://etalabs.net/sh_tricks.html
+echo () {
+ IFS=" "
+ printf %s\\n "$*"
+}
+
+quote () {
+ tr '\n' ' ' <<EOF | grep '^[-[:alnum:]_=,./:]* $' >/dev/null 2>&1 && { echo "$1" ; return 0 ; }
+$1
+EOF
+ echo "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#" -e "s|\*/|* /|g"
+}
+
+fail () {
+ echo "$*"
+ exit 1
+}
+
+fnmatch () {
+ eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac"
+}
+
+cmdexists () {
+ type "$1" >/dev/null 2>&1
+}
+
+trycc () {
+ test -z "$CC_AUTO" && cmdexists "$1" && CC_AUTO="$*"
+}
+
+stripdir () {
+ while eval "fnmatch '*/' \"\${$1}\"" ; do
+ eval "$1=\${$1%/}"
+ done
+}
+
+tryflag () {
+ echo "Checking whether compiler accepts $2 ..."
+ echo "typedef int x;" > "$tmpc"
+ if $CC_AUTO $CPPFLAGS_AUTO $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST "$2" -c -o "$tmpo" "$tmpc" >/dev/null 2>&1 ; then
+ echo " ... yes"
+ eval "$1=\"\${$1} \$2\""
+ eval "$1=\${$1# }"
+ return 0
+ else
+ echo " ... no"
+ return 1
+ fi
+}
+
+tryldflag () {
+ echo "Checking whether linker accepts $2 ..."
+ echo "typedef int x;" > "$tmpc"
+ if $CC_AUTO $CFLAGS_AUTO $CFLAGS $CFLAGS_POST $LDFLAGS_AUTO $LDFLAGS $LDFLAGS_POST -nostdlib "$2" -o "$tmpe" "$tmpc" >/dev/null 2>&1 ; then
+ echo " ... yes"
+ eval "$1=\"\${$1} \$2\""
+ eval "$1=\${$1# }"
+ return 0
+ else
+ echo " ... no"
+ return 1
+ fi
+}
+
+
+# Actual script
+
+CC_AUTO=
+CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -iquote src/include-local -Isrc/include"
+CPPFLAGS_POST="$CPPFLAGS"
+CPPFLAGS=
+CFLAGS_AUTO="-pipe -Wall"
+CFLAGS_POST="$CFLAGS"
+CFLAGS=-O2
+LDFLAGS_AUTO=
+LDFLAGS_POST="$LDFLAGS"
+LDFLAGS=
+LDFLAGS_NOSHARED=
+LDFLAGS_SHARED=-shared
+prefix=
+exec_prefix='$prefix'
+dynlibdir='$prefix/lib'
+libexecdir='$exec_prefix/libexec'
+bindir='$exec_prefix/bin'
+libdir='$prefix/lib/$package'
+includedir='$prefix/include'
+sysdeps='$prefix/lib/skalibs/sysdeps'
+manualsysdeps=false
+shared=false
+static=true
+allpic=true
+slashpackage=false
+abspath=false
+usensss=false
+sproot=
+home=
+exthome=
+allstatic=true
+evenmorestatic=false
+addincpath=''
+addlibspath=''
+addlibdpath=''
+vpaths=''
+vpathd=''
+build=
+
+server=adelielinux.xyz
+port=9999
+tlsport=9997
+cadir=/etc/ssl/certs
+deftls=false
+tmpdir=/tmp
+
+for arg ; do
+ case "$arg" in
+ --help) usage ;;
+ --prefix=*) prefix=${arg#*=} ;;
+ --exec-prefix=*) exec_prefix=${arg#*=} ;;
+ --dynlibdir=*) dynlibdir=${arg#*=} ;;
+ --libexecdir=*) libexecdir=${arg#*=} ;;
+ --bindir=*) bindir=${arg#*=} ;;
+ --libdir=*) libdir=${arg#*=} ;;
+ --includedir=*) includedir=${arg#*=} ;;
+ --with-sysdeps=*) sysdeps=${arg#*=} manualsysdeps=true ;;
+ --with-include=*) var=${arg#*=} ; stripdir var ; addincpath="$addincpath -I$var" ;;
+ --with-lib=*) var=${arg#*=} ; stripdir var ; addlibspath="$addlibspath -L$var" ; vpaths="$vpaths $var" ;;
+ --with-dynlib=*) var=${arg#*=} ; stripdir var ; addlibdpath="$addlibdpath -L$var" ; vpathd="$vpathd $var" ;;
+ --enable-shared|--enable-shared=yes) shared=true ;;
+ --disable-shared|--enable-shared=no) shared=false ;;
+ --enable-static|--enable-static=yes) static=true ;;
+ --disable-static|--enable-static=no) static=false ;;
+ --enable-allstatic|--enable-allstatic=yes) allstatic=true ;;
+ --disable-allstatic|--enable-allstatic=no) allstatic=false ; evenmorestatic=false ;;
+ --enable-static-libc|--enable-static-libc=yes) allstatic=true ; evenmorestatic=true ;;
+ --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;;
+ --enable-all-pic|--enable-all-pic=yes) allpic=true ;;
+ --disable-all-pic|--enable-all-pic=no) allpic=false ;;
+ --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;;
+ --enable-slashpackage) sproot= ; slashpackage=true ;;
+ --disable-slashpackage) sproot= ; slashpackage=false ;;
+ --enable-absolute-paths|--enable-absolute-paths=yes) abspath=true ;;
+ --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;;
+ --enable-nsss|--enable-nsss=yes) usensss=true ;;
+ --disable-nsss|--enable-nsss=no) usensss=false ;;
+ --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
+ --host=*|--target=*) target=${arg#*=} ;;
+ --build=*) build=${arg#*=} ;;
+ --with-default-server=*) server=${arg#*=} ;;
+ --with-default-port=*) port=${arg#*=} ;;
+ --with-default-tlsport=*) tlsport=${arg#*=} ;;
+ --with-default-cadir=*) cadir=${arg#*=} ;;
+ --with-default-tls|--with-default-tls=yes) deftls=true ;;
+ --without-default-tls|--with-default-tls=no) deftls=false ;;
+ --with-tmpdir=*) tmpdir=${arg#*=} ;;
+ -* ) echo "$0: unknown option $arg" ;;
+ *=*) eval "${arg%%=*}=\${arg#*=}" ;;
+ *) target=$arg ;;
+ esac
+done
+
+# Add /usr in the default default case
+if test -z "$prefix" ; then
+ if test "$libdir" = '$prefix/lib/$package' ; then
+ libdir=/usr/lib/$package
+ fi
+ if test "$includedir" = '$prefix/include' ; then
+ includedir=/usr/include
+ fi
+ if test "$sysdeps" = '$prefix/lib/skalibs/sysdeps' ; then
+ sysdeps=/usr/lib/skalibs/sysdeps
+ fi
+fi
+
+# Expand installation directories
+stripdir prefix
+for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysdeps sproot ; do
+ eval tmp=\${$i}
+ eval $i=$tmp
+ stripdir $i
+done
+
+# Get usable temp filenames
+i=0
+set -C
+while : ; do
+ i=$(($i+1))
+ tmpc="./tmp-configure-$$-$PPID-$i.c"
+ tmpo="./tmp-configure-$$-$PPID-$i.o"
+ tmpe="./tmp-configure-$$-$PPID-$i.tmp"
+ 2>|/dev/null > "$tmpc" && break
+ 2>|/dev/null > "$tmpo" && break
+ 2>|/dev/null > "$tmpe" && break
+ test "$i" -gt 50 && fail "$0: cannot create temporary files"
+done
+set +C
+trap 'rm -f "$tmpc" "$tmpo" "$tmpe"' EXIT ABRT INT QUIT TERM HUP
+
+# Set slashpackage values
+if $slashpackage ; then
+ home=${sproot}/package/${category}/${package}-${version}
+ exthome=${sproot}/package/${category}/${package}
+ if $manualsysdeps ; then
+ :
+ else
+ sysdeps=${DESTDIR}${sproot}/package/prog/skalibs/sysdeps
+ fi
+ extbinprefix=${exthome}/command
+ dynlibdir=${home}/library.so
+ bindir=${home}/command
+ libdir=${home}/library
+ libexecdir=$bindir
+ includedir=${home}/include
+ while read dep condvar ; do
+ if test -n "$condvar" ; then
+ eval "cond=$condvar"
+ else
+ cond=true
+ fi
+ if $cond ; then
+ addincpath="$addincpath -I${DESTDIR}${sproot}${dep}/include"
+ vpaths="$vpaths ${DESTDIR}${sproot}${dep}/library"
+ addlibspath="$addlibspath -L${DESTDIR}${sproot}${dep}/library"
+ vpathd="$vpathd ${DESTDIR}${sproot}${dep}/library.so"
+ addlibdpath="$addlibdpath -L${DESTDIR}${sproot}${dep}/library.so"
+ fi
+ done < package/deps-build
+fi
+
+# Find a C compiler to use
+if test -n "$target" && test x${build} != x${target} ; then
+ cross=${target}-
+else
+ cross=
+fi
+echo "Checking for C compiler..."
+trycc ${CC}
+if test -n "$CC_AUTO" ; then
+ b=`basename "$CC"`
+ adjust_cross=false
+ if test "$b" != "$CC" ; then
+ adjust_cross=true
+ echo "$0: warning: compiler $CC is declared with its own path. If it's not accessible via PATH, you will need to pass AR, RANLIB and STRIP make variables to the make invocation." 1>&2
+ fi
+ if test -n "$cross" ; then
+ if test "$b" = "${b##$cross}" ; then
+ echo "$0: warning: compiler $CC is declared as a cross-compiler for target $target but does not start with prefix ${cross}" 1>&2
+ elif $adjust_cross ; then
+ cross=`dirname "$CC"`/"$cross"
+ fi
+ fi
+fi
+trycc ${cross}gcc
+trycc ${cross}clang
+trycc ${cross}cc
+test -n "$CC_AUTO" || { echo "$0: cannot find a C compiler" ; exit 1 ; }
+echo " ... $CC_AUTO"
+echo "Checking whether C compiler works... "
+echo "typedef int x;" > "$tmpc"
+if $CC_AUTO $CPPFLAGS_AUTO $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST -c -o "$tmpo" "$tmpc" 2>"$tmpe" ; then
+ echo " ... yes"
+else
+ echo " ... no. Compiler output follows:"
+ cat < "$tmpe"
+ exit 1
+fi
+
+echo "Checking target system type..."
+if test -z "$target" ; then
+ if test -n "$build" ; then
+ target=$build ;
+ else
+ target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown
+ fi
+fi
+echo " ... $target"
+if test ! -d $sysdeps || test ! -f $sysdeps/target ; then
+ echo "$0: error: $sysdeps is not a valid sysdeps directory"
+ exit 1
+fi
+if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then
+ echo "$0: error: target $target does not match the contents of $sysdeps/target"
+ exit 1
+fi
+
+spawn_lib=$(cat $sysdeps/spawn.lib)
+socket_lib=$(cat $sysdeps/socket.lib)
+sysclock_lib=$(cat $sysdeps/sysclock.lib)
+timer_lib=$(cat $sysdeps/timer.lib)
+util_lib=$(cat $sysdeps/util.lib)
+
+if $allpic ; then
+ tryflag CPPFLAGS_AUTO -fPIC
+fi
+tryflag CFLAGS_AUTO -std=c99
+tryflag CFLAGS -fomit-frame-pointer
+tryflag CFLAGS_AUTO -fno-exceptions
+tryflag CFLAGS_AUTO -fno-unwind-tables
+tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables
+tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration
+tryflag CPPFLAGS_AUTO -Werror=implicit-int
+tryflag CPPFLAGS_AUTO -Werror=pointer-sign
+tryflag CPPFLAGS_AUTO -Werror=pointer-arith
+tryflag CFLAGS_AUTO -ffunction-sections
+tryflag CFLAGS_AUTO -fdata-sections
+
+tryldflag LDFLAGS_AUTO -Wl,--sort-section=alignment
+tryldflag LDFLAGS_AUTO -Wl,--sort-common
+
+CPPFLAGS_AUTO="${CPPFLAGS_AUTO}${addincpath}"
+
+if $evenmorestatic ; then
+ LDFLAGS_NOSHARED=-static
+fi
+
+if $shared ; then
+ tryldflag LDFLAGS -Wl,--hash-style=both
+fi
+
+LDFLAGS_SHARED="${LDFLAGS_SHARED}${addlibdpath}"
+
+if test -z "$vpaths" ; then
+ while read dep ; do
+ base=$(basename $dep) ;
+ vpaths="$vpaths /usr/lib/$base"
+ addlibspath="$addlibspath -L/usr/lib/$base"
+ done < package/deps-build
+fi
+
+if $allstatic ; then
+ LDFLAGS_NOSHARED="${LDFLAGS_NOSHARED}${addlibspath}"
+ tryldflag LDFLAGS_NOSHARED -Wl,--gc-sections
+else
+ LDFLAGS_NOSHARED="${LDFLAGS_NOSHARED}${addlibdpath}"
+fi
+
+echo "Creating config.mak..."
+cmdline=$(quote "$0")
+for i ; do cmdline="$cmdline $(quote "$i")" ; done
+exec 3>&1 1>config.mak
+cat << EOF
+# This file was generated by:
+# $cmdline
+# Any changes made here will be lost if configure is re-run.
+
+target := $target
+package := $package
+prefix := $prefix
+exec_prefix := $exec_prefix
+dynlibdir := $dynlibdir
+libexecdir := $libexecdir
+bindir := $bindir
+libdir := $libdir
+includedir := $includedir
+sysdeps := $sysdeps
+slashpackage := $slashpackage
+sproot := $sproot
+version := $version
+home := $home
+exthome := $exthome
+SPAWN_LIB := ${spawn_lib}
+SOCKET_LIB := ${socket_lib}
+SYSCLOCK_LIB := ${sysclock_lib}
+TIMER_LIB := ${timer_lib}
+UTIL_LIB := ${util_lib}
+
+CC := $CC_AUTO
+CPPFLAGS_AUTO := $CPPFLAGS_AUTO
+CPPFLAGS := $CPPFLAGS $CPPFLAGS_POST
+CFLAGS_AUTO := $CFLAGS_AUTO
+CFLAGS := $CFLAGS $CFLAGS_POST
+LDFLAGS_AUTO := $LDFLAGS_AUTO
+LDFLAGS := $LDFLAGS $LDFLAGS_POST
+LDFLAGS_SHARED := $LDFLAGS_SHARED
+LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED
+CROSS_COMPILE := $cross
+
+vpath lib%.a$vpaths
+vpath lib%.so$vpathd
+EOF
+if $allstatic ; then
+ echo ".LIBPATTERNS := lib%.a"
+ echo "DO_ALLSTATIC := 1"
+else
+ echo ".LIBPATTERNS := lib%.so"
+fi
+if $static ; then
+ echo "DO_STATIC := 1"
+else
+ echo "DO_STATIC :="
+fi
+if $shared ; then
+ echo "DO_SHARED := 1"
+else
+ echo "DO_SHARED :="
+fi
+if $allpic ; then
+ echo "STATIC_LIBS_ARE_PIC := 1"
+else
+ echo "STATIC_LIBS_ARE_PIC :="
+fi
+if $usensss ; then
+ echo "LIBNSSS := -lnsss"
+ echo "MAYBEPTHREAD_LIB := -lpthread"
+else
+ echo "LIBNSSS :="
+ echo "MAYBEPTHREAD_LIB :="
+fi
+
+exec 1>&3 3>&-
+echo " ... done."
+
+echo "Creating src/include/${package}/config.h..."
+mkdir -p -m 0755 src/include/${package}
+exec 3>&1 1> src/include/${package}/config.h
+cat <<EOF
+/* ISC license. */
+
+/* Generated by: $cmdline */
+
+#ifndef ${package_macro_name}_CONFIG_H
+#define ${package_macro_name}_CONFIG_H
+
+#define ${package_macro_name}_VERSION "$version"
+EOF
+if $slashpackage ; then
+ echo "#define ${package_macro_name}_BINPREFIX \"$bindir/\""
+ echo "#define ${package_macro_name}_EXTBINPREFIX \"$extbinprefix/\""
+elif $abspath ; then
+ echo "#define ${package_macro_name}_BINPREFIX \"$bindir/\""
+ echo "#define ${package_macro_name}_EXTBINPREFIX \"$bindir/\""
+else
+ echo "#define ${package_macro_name}_BINPREFIX \"\""
+ echo "#define ${package_macro_name}_EXTBINPREFIX \"\""
+fi
+echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\""
+echo
+echo "#define ${package_macro_name}_DEFAULT_SERVER \"$server\""
+echo "#define ${package_macro_name}_DEFAULT_PORT $port"
+echo "#define ${package_macro_name}_DEFAULT_TLSPORT $tlsport"
+echo "#define ${package_macro_name}_DEFAULT_CADIR \"$cadir\""
+if $deftls ; then
+echo "#define ${package_macro_name}_DEFAULT_TLS 1"
+else
+echo "#define ${package_macro_name}_DEFAULT_TLS 0"
+fi
+echo "#define ${package_macro_name}_TMPDIR \"$tmpdir\""
+echo
+echo "#endif"
+exec 1>&3 3>&-
+echo " ... done."
diff --git a/doc/apaste.html b/doc/apaste.html
new file mode 100644
index 0000000..34f66d3
--- /dev/null
+++ b/doc/apaste.html
@@ -0,0 +1,114 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>apaste: the apaste program</title>
+ <meta name="Description" content="apaste: the apaste program" />
+ <meta name="Keywords" content="apaste client interface pastebin command-line tpaste sprunge fiche s6-networking tcpclient tlsclient tls ssl" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">apaste</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>apaste</tt> program </h1>
+
+<p>
+ <tt>apaste</tt> is a program that reads one or more files, or its
+standard input, and sends the data to an <a href="apasted.html">apasted</a>
+server.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ apaste [ -S | -s ] [ -C <em>cadir</em> ] [ -d <em>server</em>[<em>:port</em>] ] [ -r <em>rtimeout</em> ] [ -w <em>wtimeout</em> ] <em>file...</em>
+</pre>
+
+<ul>
+ <li> apaste connects to a remote server via a plaintext or TLS-secured
+connection, expecting to find an <a href="apasted.html">apasted</a> server at the
+other end. </li>
+ <li> For every <em>file</em> given as argument, it sends the contents of <em>file</em>
+over the network. If <em>file</em> is <tt>-</tt> (dash), then stdout is transmitted
+until EOF. </li>
+ <li> The server answers with a blob of six printable characters, named a <em>slug</em>.
+Depending on the server configuration, it may embed the slug in a complete URL, for
+easy copy-paste into a browser; or it may embed it in another way. </li>
+ <li> apaste prints the slug to its stdout, then exits 0. </li>
+</ul>
+
+<h2> Exit codes </h2>
+
+<dl>
+ <dt> 0 </dt> <dd> Success. The data has been recorded by the server and available in
+some way as indicated by the slug. </dd>
+ <dt> 100 </dt> <dd> Bad usage. apaste was run in an incorrect way. </dd>
+ <dt> 111 </dt> <dd> System call failed. This usually signals an issue with the
+underlying operating system, or with the network in some way. </dt>
+</dl>
+
+<h2> Options </h2>
+
+<dl>
+ <dt> -S </dt>
+ <dd> Normal, plain text connection to the server, even if the built-in default
+is TLS. The built-in default can be changed via the <tt>--with-default-tls</tt>
+or <tt>--without-default-tls</tt> options to configure. </dd>
+
+ <dt> -s </dt>
+ <dd> TLS connection to the server, even if the built-in default
+is plain text. The built-in default can be changed via the <tt>--with-default-tls</tt>
+or <tt>--without-default-tls</tt> options to configure. </dd>
+
+ <dt> -C <em>cadir</em> </dt>
+ <dd> When using a TLS connection, use <em>cadir</em> as the directory
+containing the hashed names of the trust anchor certificates
+(used for verifying the server's certificate chain). The built-in default
+can be changed via the <tt>--with-default-cadir</tt> option to configure. </dd>
+
+ <dt> -d <em>server</em>[<em>:port</em>] </dt>
+ <dd> Connect to server <em>server</em>, port <em>port</em>. The built-in defaults
+can be changed via the <tt>--with-default-server</tt>, <tt>--with-default-port</tt>,
+and <tt>--with-default-tlsport</tt> options to configure. </dd>
+
+ <dt> -r <em>rtimeout</em> </dt>
+ <dd> If the server isn't answering with a slug within <em>rtimeout</em>
+milliseconds, give up. The default is 0, meaning infinite: apaste will
+wait forever for a server reply if necessary. </dd>
+
+ <dt> -w <em>wtimeout</em> </dt>
+ <dd> If the server hasn't accepted all the data within <em>wtimeout</em>
+milliseconds, give up. The default is 0, meaning infinite: apaste will
+take as much time as it needs to send its data. </dd>
+</dl>
+
+<h2> Example usage </h2>
+
+<ul>
+ <li> <code>$ echo Blah blah. | apaste -</code> </li>
+ <li> <code># apaste /etc/shadow </code> <small>(Just kidding. Don't do that.)</small> </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> apaste is only a wrapper around
+<a href="//skarnet.org/software/s6-networking/s6-tcpclient.html">s6-tcpclient</a>
+(for plain connections) or
+<a href="//skarnet.org/software/s6-networking/s6-tlsclient.html">s6-tlsclient</a>
+(for TLS-tunneled connections), and
+<a href="apastec.html">apastec</a> that is the real client. The point of apaste
+is to provide a short command line with good compiled-in defaults; make sure
+your defaults are correct at configure time when building the apaste package. </li>
+ <li> <tt>-</tt>, i.e. stdin, cannot be mentioned several times as an argument. It
+can be transmitted with other files, but cannot be duplicated. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/apastec.html b/doc/apastec.html
new file mode 100644
index 0000000..6711f3f
--- /dev/null
+++ b/doc/apastec.html
@@ -0,0 +1,88 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>apaste: the apastec program</title>
+ <meta name="Description" content="apaste: the apastec program" />
+ <meta name="Keywords" content="apastec apaste client interface pastebin command-line tpaste sprunge fiche" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">apaste</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>apastec</tt> program </h1>
+
+<p>
+ <tt>apastec</tt> is a program that reads one or more files, or its
+standard input, and sends them to a preopened file descriptor.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ apastec [ -r <em>rtimeout</em> ] [ -w <em>wtimeout</em> ] <em>file...</em>
+</pre>
+
+<ul>
+ <li> apastec expects to have, in addition to standard file descriptors 0, 1 and 2,
+its file descriptors 6 and 7 open and connected to a remote
+<a href="apasted.html">apasted</a> server. </li>
+ <li> For every <em>file</em> given as argument, it sends the contents of <em>file</em>
+over the network. If <em>file</em> is <tt>-</tt> (dash), then stdout is transmitted
+until EOF. </li>
+ <li> The server answers with a blob of six printable characters, named a <em>slug</em>.
+Depending on the server configuration, it may embed the slug in a complete URL, for
+easy copy-paste into a browser; or it may embed it in another way. </li>
+ <li> apastec prints the slug to its stdout, then exits 0. </li>
+</ul>
+
+<h2> Exit codes </h2>
+
+<dl>
+ <dt> 0 </dt> <dd> Success. The data has been recorded by the server and available in
+some way as indicated by the slug. </dd>
+ <dt> 100 </dt> <dd> Bad usage. apaste was run in an incorrect way. </dd>
+ <dt> 111 </dt> <dd> System call failed. This usually signals an issue with the
+underlying operating system, or with the network in some way. </dt>
+</dl>
+
+<h2> Options </h2>
+
+<dl>
+ <dt> -r <em>rtimeout</em> </dt>
+ <dd> If the server isn't answering with a slug within <em>rtimeout</em>
+milliseconds, give up. The default is 0, meaning infinite: apastec will
+wait forever for a server reply if necessary. </dd>
+
+ <dt> -w <em>wtimeout</em> </dt>
+ <dd> If the server hasn't accepted all the data within <em>wtimeout</em>
+milliseconds, give up. The default is 0, meaning infinite: apastec will
+take as much time as it needs to send its data. </dd>
+</dl>
+
+<h2> Typical usage </h2>
+
+<p>
+ apastec isn't meant to be used directly. It is meant to be invoked as
+part of a command line crafted by the <a href="apaste.html">apaste</a>
+command, where programs from the
+<a href="//skarnet.org/software/s6-networking/">s6-networking</a> package
+establish the connection to the server, then exec into apastec to read
+the user's data and transmit them with the apaste protocol.
+</p>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> <tt>-</tt>, i.e. stdin, cannot be mentioned several times as an argument. It
+can be transmitted with other files, but cannot be duplicated. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/apasted.html b/doc/apasted.html
new file mode 100644
index 0000000..98ec51d
--- /dev/null
+++ b/doc/apasted.html
@@ -0,0 +1,137 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>apaste: the apasted program</title>
+ <meta name="Description" content="apaste: the apasted program" />
+ <meta name="Keywords" content="apasted apaste server pastebin command-line tpaste sprunge fiche" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">apaste</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>apasted</tt> program </h1>
+
+<p>
+ <tt>apasted</tt> is the server-side program for the apaste package. It
+expects its stdin and stdout to be connected to the network, having being
+contacted by an <a href="apastec.html">apastec</a> client. It reads a
+series of files transmitted by the client, stores it on the server, and
+sends back a <em>slug</em> to the client, i.e. an identifier for the
+stored files.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ apasted [ -r <em>rtimeout</em> ] [ -w <em>wtimeout</em> ] [ -d <em>rootdir</em> ] [ -p <em>prefix</em> ] [ -m <em>maxfiles</em> ]
+</pre>
+
+<ul>
+ <li> apasted reads data on its stdin, expecting the protocol spoken by the
+<a href="apastec.html">apastec</a> client, containing one or more files. </li>
+ <li> It creates a subdirectory <em>subdir</em> in its working directory,
+and stores the files it receives under it.
+ <ul>
+ <li> If it only receives one file, it stores it in <tt><em>subdir</em>/index.txt</tt>. </li>
+ <li> If it receives two or more files, it stores each file <em>file</em> as
+<tt><em>subdir</em>/<em>file</em>.txt</tt>, and creates a
+<tt><em>subdir</em>/index.html</tt> file with hyperlinks to every file in <em>subdir</em>. </li>
+ </ul> </li>
+ <li> It sends the <em>subdir</em> name back to the client, as a <em>slug</em>. </li>
+ <li> It exits 0 </li>
+</ul>
+
+<h2> Exit codes </h2>
+
+<dl>
+ <dt> 0 </dt> <dd> Success. </dd>
+ <dt> 1 </dt> <dd> Protocol error. The client sent incorrectly formatted data. </dd>
+ <dt> 100 </dt> <dd> Bad usage. apasted was run in an incorrect way. </dd>
+ <dt> 111 </dt> <dd> System call failed. This usually signals an issue with the
+underlying operating system, or with the network in some way. </dt>
+</dl>
+
+<h2> Options </h2>
+
+<dl>
+ <dt> -r <em>rtimeout</em> </dt>
+ <dd> If the client hasn't transmitted all its data within <em>rtimeout</em>
+milliseconds, give up. The default is 0, meaning infinite: apasted will
+wait forever for client data if necessary. </dd>
+
+ <dt> -w <em>wtimeout</em> </dt>
+ <dd> If apasted fails to send the slug to the client within <em>wtimeout</em>
+milliseconds, give up. The default is 0, meaning infinite: apasted will
+take as much time as it needs to send its answer. </dd>
+
+ <dt> -d <em>rootdir</em> </dt>
+ <dd> Switch to <em>rootdir</em> and store files there. The default is
+apasted's working directory. </dd>
+
+ <dt> -p <em>prefix</em> </dt>
+ <dd> When sending a slug to the client, prefix <em>subdir</em> with <em>prefix</em>,
+and append a slash at the end. This is useful when apasted writes its files to
+a web server's document hierarchy, which is the intended case. If <em>prefix</em>
+is the URL of apasted's base directory, then the slug can directly be used as a
+URL to access the client's files. </dd>
+
+ <dt> -m <em>maxfiles</em> </dt>
+ <dd> Accept a maximum of <em>maxfiles</em> files at a time from the client.
+The default is 0, meaning unlimited: the client can send as many files as it
+wants and apasted will still store them if it is possible. </dd>
+</dl>
+
+<h2> Typical usage </h2>
+
+<ul>
+ <li> apasted is meant to be run under a super-server accepting client connections.
+Run it under inetd or
+<a href="//skarnet.org/software/s6-networking/s6-tcpserver.html">s6-tcpserver</a>
+for plaintext connections, and under
+<a href="//skarnet.org/software/s6-networking/s6-tlsserver.html">s6-tlsserver</a>
+for TLS-tunneled connections. </li>
+ <li> To publish the data received and stored by apasted, the simplest way is
+to have a web server running and serving the hierarchy of apasted files. Use the
+<tt>-p</tt> option on the apasted command line to make sure the client receives
+a full URL to its files. </li>
+</ul>
+
+<h2> Caveats </h2>
+
+<ul>
+ <li> apaste is a <em>push</em> protocol: the client sends data to be stored on
+the server. As such, it is very easy to abuse, and caution should be taken when
+running an apasted server:
+ <ul>
+ <li> If possible, have quotas on the filesystem hosting the apasted storage area </li>
+ <li> Use the <tt>-m</tt> option to avoid trivial inode exhaustion attacks </li>
+ <li> Use your super-server's options to mitigate client patterns of abuse, log and
+block the IPs of problematic clients </li>
+ <li> Run scripts that regularly delete old <em>subdir</em>s (and their contents)
+in the apasted storage area. </li>
+ <li> Be aware, and warn your users, that a pastebin is a service to the community,
+and that abusing the service is the best and quickest way to make it go away. </li>
+ </ul> </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> apasted will store a file named <em>file</em> as <em>file.txt</em> so that a
+web server can always serve it as plain text. apaste is not meant to send binary
+files of an arbitrary MIME type. </li>
+ <li> A unique file is stored as <tt><em>slug</em>/index.txt</tt>; if apasted is
+run with <tt>-p http://example.com/</tt> then the apaste client, on success,
+will print <tt>http://example.com/<em>slug</em>/</tt>. Make sure that your web server can
+automatically interpret that URL as <tt>http://example.com/<em>slug</em>/index.txt</tt>. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..6697333
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,110 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>apaste - a small command-line pastebin</title>
+ <meta name="Description" content="apaste - a small command-line pastebin" />
+ <meta name="Keywords" content="apaste pastebin tpaste fiche command line laurent bercot ska skarnet" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> apaste </h1>
+
+<h2> What is it&nbsp;? </h2>
+
+<p>
+ apaste is a small command-line pastebin, to share terminal output
+by pushing it to a server, so it can be made public via the Web or some
+other protocol.
+</p>
+
+<hr />
+
+<h2> Installation </h2>
+
+<h3> Requirements </h3>
+
+<ul>
+ <li> A POSIX-compliant system with a standard C development environment </li>
+ <li> GNU make, version 3.81 or later </li>
+ <li> <a href="//skarnet.org/software/skalibs/">skalibs</a> version
+2.14.1.1 or later. It's a build-time requirement. It's also a run-time
+requirement if you link against the shared version of the skalibs
+library. </li>
+ <li> <a href="//skarnet.org/software/s6-networking/">s6-networking</a> version
+2.7.0.2 or later. It's a build-time and run-time requirement: it's the layer
+that provides the network connection. </li>
+</ul>
+
+<h3> Licensing </h3>
+
+<p>
+ apaste is free software. It is available under the
+<a href="https://opensource.org/licenses/ISC">ISC license</a>.
+</p>
+
+<h3> Download </h3>
+
+<ul>
+ <li> The current released version of apaste is
+<a href="apaste-0.0.1.0.tar.gz">0.0.1.0</a>. </li>
+ <li> You can checkout a copy of the
+<a href="//git.skarnet.org/cgi-bin/cgit.cgi/apaste/">apaste
+git repository</a>:
+<pre> git clone git://git.skarnet.org/apaste </pre> </li>
+ <li> There's also a
+<a href="https://github.com/skarnet/apaste">GitHub mirror</a>
+of the apaste git repository. </li>
+</ul>
+
+<h3> Compilation </h3>
+
+<ul>
+ <li> See the enclosed INSTALL file for installation details. </li>
+</ul>
+
+<h3> Upgrade notes </h3>
+
+<ul>
+ <li> <a href="upgrade.html">This page</a> lists the differences to be aware of between
+the previous versions of apaste and the current one. </li>
+</ul>
+
+<hr />
+
+<h2> Reference </h2>
+
+<h3> Commands </h3>
+
+<ul>
+ <li> <a href="apaste.html">The <tt>apaste</tt> program</a> </li>
+ <li> <a href="apastec.html">The <tt>apastec</tt> program</a> </li>
+ <li> <a href="apasted.html">The <tt>apasted</tt> program</a> </li>
+</ul>
+
+<h2> Related resources </h2>
+
+<ul>
+ <li> <tt>apaste</tt> is discussed on the
+<a href="//skarnet.org/lists/#skaware">skaware</a> mailing-list. </li>
+</ul>
+
+<h2> Similar work </h2>
+
+<ul>
+ <li> <a href="https://github.com/solusipse/fiche">fiche</a> is also
+a small command-line pastebin utility. </li>
+ <li> <a href="https://tpaste.us/">tpaste</a> and
+<a href="https://sprunge.us/">sprunge </a> are similar utilities,
+one written in lua, the other in Python. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/upgrade.html b/doc/upgrade.html
new file mode 100644
index 0000000..c8650e5
--- /dev/null
+++ b/doc/upgrade.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>apaste: how to upgrade</title>
+ <meta name="Description" content="apaste: how to upgrade" />
+ <meta name="Keywords" content="apaste installation upgrade" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">apaste</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> What has changed in apaste </h1>
+
+<h2> in 0.0.1.0 </h2>
+
+<ul>
+ <li> Initial release. </li>
+</ul>
+
+</body>
+</html>
diff --git a/package/deps-build b/package/deps-build
new file mode 100644
index 0000000..c4bd95c
--- /dev/null
+++ b/package/deps-build
@@ -0,0 +1,2 @@
+/package/prog/skalibs
+/package/net/s6-networking
diff --git a/package/deps.mak b/package/deps.mak
new file mode 100644
index 0000000..3f6192a
--- /dev/null
+++ b/package/deps.mak
@@ -0,0 +1,16 @@
+#
+# This file has been generated by tools/gen-deps.sh
+#
+
+src/client/apaste.o src/client/apaste.lo: src/client/apaste.c src/include/apaste/config.h
+src/client/apastec.o src/client/apastec.lo: src/client/apastec.c src/include-local/apaste-common.h src/include/apaste/config.h src/client/apastec.h
+src/client/send_file.o src/client/send_file.lo: src/client/send_file.c
+src/server/apasted.o src/server/apasted.lo: src/server/apasted.c src/include-local/apaste-common.h src/include/apaste/config.h
+
+apaste: EXTRA_LIBS := -lskarnet
+apaste: src/client/apaste.o
+apastec: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
+apastec: src/client/apastec.o src/client/send_file.o
+apasted: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
+apasted: src/server/apasted.o
+INTERNAL_LIBS :=
diff --git a/package/info b/package/info
new file mode 100644
index 0000000..333079b
--- /dev/null
+++ b/package/info
@@ -0,0 +1,4 @@
+package=apaste
+version=0.0.1.0
+category=web
+package_macro_name=APASTE
diff --git a/package/modes b/package/modes
new file mode 100644
index 0000000..4e5f807
--- /dev/null
+++ b/package/modes
@@ -0,0 +1,3 @@
+apaste 0755
+apastec 0755
+apasted 0755
diff --git a/package/targets.mak b/package/targets.mak
new file mode 100644
index 0000000..1c4f30e
--- /dev/null
+++ b/package/targets.mak
@@ -0,0 +1,7 @@
+BIN_TARGETS := \
+apaste \
+apastec \
+apasted
+
+LIBEXEC_TARGETS :=
+
diff --git a/patch-for-solaris b/patch-for-solaris
new file mode 100755
index 0000000..2d1296b
--- /dev/null
+++ b/patch-for-solaris
@@ -0,0 +1,21 @@
+#!/usr/xpg4/bin/sh
+
+patchit () {
+ echo '#!/usr/xpg4/bin/sh' > $1.tmp
+ tail -n +2 $1 >> $1.tmp
+ mv -f $1.tmp $1
+ chmod 755 $1
+}
+
+# Solaris doesn't understand POSIX.1-2008 either.
+sed -e 's/XOPEN_SOURCE=700/XOPEN_SOURCE=600/' < configure > configure.tmp
+mv -f configure.tmp configure
+
+patchit ./configure
+patchit ./tools/install.sh
+patchit ./tools/gen-deps.sh
+
+echo 'SHELL := /usr/xpg4/bin/sh' > Makefile.tmp
+echo >> Makefile.tmp
+cat Makefile >> Makefile.tmp
+mv -f Makefile.tmp Makefile
diff --git a/src/client/PROTOCOL.txt b/src/client/PROTOCOL.txt
new file mode 100644
index 0000000..6e828ca
--- /dev/null
+++ b/src/client/PROTOCOL.txt
@@ -0,0 +1,19 @@
+
+apastec:
+
+banner \n
+nfiles (uint32) \n
+{
+ namelength (uint16) \n
+ name (namelength) \n
+ filelength (uint64) \n
+ file (filelength) \n
+}
+banner \n
+
+
+apasted:
+
+banner \n
+slug \n
+banner \n
diff --git a/src/client/apaste.c b/src/client/apaste.c
new file mode 100644
index 0000000..1d2a739
--- /dev/null
+++ b/src/client/apaste.c
@@ -0,0 +1,101 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/exec.h>
+
+#include <s6-networking/config.h>
+
+#include <apaste/config.h>
+
+#define USAGE "apaste [ -S | -s ] [ -C cadir ] [ -d server[:port] ] [ -r rtimeout ] [ -w wtimeout ] file..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char *const *argv)
+{
+ char const *cadir = APASTE_DEFAULT_CADIR ;
+ char const *server = APASTE_DEFAULT_SERVER ;
+ int dotls = APASTE_DEFAULT_TLS ;
+ uint32_t rt = 0 ;
+ uint32_t wt = 0 ;
+ uint16_t port = 0 ;
+ PROG = "apaste" ;
+
+ {
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, (char const *const *)argv, "SsC:d:r:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'S' : dotls = 0 ; break ;
+ case 's' : dotls = 1 ; break ;
+ case 'C' : cadir = l.arg ; break ;
+ case 'd' :
+ {
+ char *colon = strchr(l.arg, ':') ;
+ server = l.arg ;
+ if (colon)
+ {
+ *colon = 0 ;
+ if (!uint160_scan(colon + 1, &port)) dieusage() ;
+ }
+ break ;
+ }
+ case 'r' : if (!uint320_scan(l.arg, &rt)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &wt)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+ if (!port) port = dotls ? APASTE_DEFAULT_TLSPORT : APASTE_DEFAULT_PORT ;
+ {
+ char const *newargv[12 + argc] ;
+ unsigned int m = 0 ;
+ char fmtr[UINT32_FMT] ;
+ char fmtw[UINT32_FMT] ;
+ char fmtp[UINT16_FMT] ;
+
+ fmtp[uint16_fmt(fmtp, port)] = 0 ;
+
+ newargv[m++] = dotls ? S6_NETWORKING_EXTBINPREFIX "s6-tlsclient" : S6_NETWORKING_EXTBINPREFIX "s6-tcpclient" ;
+ newargv[m++] = "-N" ;
+ newargv[m++] = "--" ;
+ newargv[m++] = server ;
+ newargv[m++] = fmtp ;
+ newargv[m++] = APASTE_BINPREFIX "apastec" ;
+ if (rt)
+ {
+ fmtr[uint32_fmt(fmtr, rt)] = 0 ;
+ newargv[m++] = "-r" ;
+ newargv[m++] = fmtr ;
+ }
+ if (rt)
+ {
+ fmtw[uint32_fmt(fmtw, wt)] = 0 ;
+ newargv[m++] = "-w" ;
+ newargv[m++] = fmtw ;
+ }
+ newargv[m++] = "--" ;
+ while (argc--) newargv[m++] = *argv++ ;
+ newargv[m++] = 0 ;
+
+ if (dotls)
+ {
+ size_t len = strlen(cadir) ;
+ char modif[7 + len] ;
+ memcpy(modif, "CADIR=", 6) ;
+ memcpy(modif + 6, cadir, len + 1) ;
+ xmexec_n(newargv, modif, len + 7, 1) ;
+ }
+ else xexec(newargv) ;
+ }
+}
diff --git a/src/client/apastec.c b/src/client/apastec.c
new file mode 100644
index 0000000..3237ceb
--- /dev/null
+++ b/src/client/apastec.c
@@ -0,0 +1,155 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/buffer.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+
+#include <apaste/config.h>
+#include "apaste-common.h"
+#include "apastec.h"
+
+#define USAGE "apastec [ -r rtimeout ] [ -w wtimeout ] file..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static void apaste_send (buffer *b, char const *file, tain const *deadline)
+{
+ int fd ;
+ struct stat st ;
+ size_t len = strlen(file) ;
+ if (!len) strerr_dief2x(100, "empty file", " names are invalid") ;
+ if (len > UINT16_MAX) strerr_dief2x(100, "file name too long: ", file) ;
+ if (len == 1 && file[0] == '-')
+ {
+ char tmp[sizeof(APASTE_TMPDIR) + 14] = APASTE_TMPDIR "/apaste:XXXXXX" ;
+ fd = mkstemp(tmp) ;
+ if (fd == -1) strerr_diefu2sys(111, "mkstemp ", tmp) ;
+ unlink_void(tmp) ;
+ if (fd_cat(0, fd) == -1) strerr_diefu2sys(111, "copy stdin to ", tmp) ;
+ if (lseek(fd, 0, SEEK_SET) == -1) strerr_diefu1sys(111, "lseek") ;
+ ndelay_on(fd) ;
+ }
+ else
+ {
+ if (file[len-1] == '/') strerr_dief2x(100, "directory", " names are invalid") ;
+ fd = open_read(file) ;
+ if (fd == -1) strerr_diefu2sys(111, "open ", file) ;
+ }
+
+ if (fstat(fd, &st) == -1) strerr_diefu2sys(111, "stat ", file) ;
+ if (!S_ISREG(st.st_mode)) strerr_dief3x(100, "file ", file, " is not a regular file") ;
+ {
+ char fmt[UINT64_FMT] ;
+ size_t m = uint16_fmt(fmt, len) ;
+ fmt[m++] = '\n' ;
+ if (buffer_timed_put_g(b, fmt, m, deadline) < m
+ || buffer_timed_put_g(b, file, len, deadline) < len
+ || buffer_timed_put_g(b, "\n", 1, deadline) < 1)
+ strerr_diefu1sys(111, "write to server") ;
+ m = uint64_fmt(fmt, st.st_size) ;
+ fmt[m++] = '\n' ;
+ if (buffer_timed_put_g(b, fmt, m, deadline) < m)
+ strerr_diefu1sys(111, "write to server") ;
+ }
+ send_file_g(b, fd, st.st_size, file, deadline) ;
+ fd_close(fd) ;
+ if (buffer_timed_put_g(b, "\n", 1, deadline) < 1)
+ strerr_diefu1sys(111, "write to server") ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ tain rtto = TAIN_INFINITE_RELATIVE, wtto = TAIN_INFINITE_RELATIVE ;
+ tain deadline ;
+ PROG = "apastec" ;
+ {
+ uint32_t rt = 0, wt = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "r:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'r' : if (!uint320_scan(l.arg, &rt)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &wt)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (rt) tain_from_millisecs(&rtto, rt) ;
+ if (wt) tain_from_millisecs(&rtto, wt) ;
+ }
+ if (!argc) dieusage() ;
+ if (argc > UINT32_MAX) strerr_dief1x(100, "too many arguments") ;
+
+ if (!sig_altignore(SIGPIPE))
+ strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_set_stopwatch_g() ;
+ tain_add_g(&deadline, &wtto) ;
+
+ {
+ char buf[BUFFER_OUTSIZE] ;
+ buffer b = BUFFER_INIT(&buffer_write, 7, buf, BUFFER_OUTSIZE) ;
+ buffer_putnoflush(&b, APASTEC_BANNER "\n", sizeof(APASTEC_BANNER)) ;
+ {
+ char fmt[UINT32_FMT] ;
+ size_t m = uint32_fmt(fmt, argc) ;
+ fmt[m++] = '\n' ;
+ buffer_putnoflush(&b, fmt, m) ;
+ }
+ {
+ int gotstdin = 0 ;
+ for (unsigned int i = 0 ; i < argc ; i++) if (argv[i][0] == '-' && !argv[i][1])
+ {
+ if (gotstdin) strerr_dief1x(100, "stdin specified more than once") ;
+ gotstdin = 1 ;
+ }
+ }
+ for (unsigned int i = 0 ; i < argc ; i++) apaste_send(&b, argv[i], &deadline) ;
+ if (buffer_timed_put_g(&b, APASTEC_BANNER "\n", sizeof(APASTEC_BANNER), &deadline) < sizeof(APASTEC_BANNER)
+ || !buffer_timed_flush_g(&b, &deadline))
+ strerr_diefu1sys(111, "write to apaste server") ;
+ }
+
+ fd_shutdown(7, 1) ;
+ fd_close(7) ;
+ tain_add_g(&deadline, &rtto) ;
+
+ {
+ char buf[BUFFER_INSIZE] ;
+ char banner[256] ;
+ char slug[256] ;
+ buffer b = BUFFER_INIT(&buffer_read, 6, buf, BUFFER_INSIZE) ;
+ size_t w = 0, sluglen = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ if (w != sizeof(APASTED_BANNER) || memcmp(banner, APASTED_BANNER, w-1))
+ strerr_dief1x(1, "server returned invalid data") ;
+ if (sanitize_read(timed_getlnmax_g(&b, slug, 256, &sluglen, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ w = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ if (w != sizeof(APASTED_BANNER) || memcmp(banner, APASTED_BANNER, w-1))
+ strerr_dief1x(1, "server returned invalid data") ;
+ if (buffer_putflush(buffer_1small, slug, sluglen) == -1)
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+
+ return 0 ;
+}
diff --git a/src/client/apastec.h b/src/client/apastec.h
new file mode 100644
index 0000000..84ab242
--- /dev/null
+++ b/src/client/apastec.h
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#ifndef APASTEC_H
+#define APASTEC_H
+
+#include <sys/types.h>
+
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+
+
+/* send_file.c */
+
+extern void send_file (buffer *, int, off_t, char const *, tain const *, tain *) ;
+#define send_file_g(b, fd, size, file, deadline) send_file(b, fd, size, file, (deadline), &STAMP)
+
+#endif
diff --git a/src/client/deps-exe/apaste b/src/client/deps-exe/apaste
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/client/deps-exe/apaste
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/client/deps-exe/apastec b/src/client/deps-exe/apastec
new file mode 100644
index 0000000..1e0ab64
--- /dev/null
+++ b/src/client/deps-exe/apastec
@@ -0,0 +1,4 @@
+send_file.o
+-lskarnet
+${SOCKET_LIB}
+${SYSCLOCK_LIB}
diff --git a/src/client/send_file.c b/src/client/send_file.c
new file mode 100644
index 0000000..4da8412
--- /dev/null
+++ b/src/client/send_file.c
@@ -0,0 +1,93 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifdef SKALIBS_HASSENDFILE
+
+#include <sys/types.h>
+#include <sys/sendfile.h>
+#include <limits.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+struct sendfile_s
+{
+ int fd ;
+ buffer *out ;
+ off_t pos ;
+ uint64_t n ;
+} ;
+
+static int s_getfd (void *p)
+{
+ struct sendfile_s *sf = p ;
+ return buffer_fd(sf->out) ;
+}
+
+static int s_isnonempty (void *p)
+{
+ struct sendfile_s *sf = p ;
+ return !!sf->n ;
+}
+
+static int s_flush (void *p)
+{
+ struct sendfile_s *sf = p ;
+ while (sf->n)
+ {
+ ssize_t r = sendfile(buffer_fd(sf->out), sf->fd, &sf->pos, sf->n > SSIZE_MAX ? SSIZE_MAX : sf->n) ;
+ if (r == -1) return 0 ;
+ sf->n -= r ;
+ }
+ return 1 ;
+}
+
+void send_file (buffer *b, int fd, off_t size, char const *fn, tain const *deadline, tain *stamp)
+{
+ struct sendfile_s sf = { .fd = fd, .out = b, .pos = 0, .n = size } ;
+ if (!buffer_timed_flush(b, deadline, stamp))
+ strerr_diefu1sys(111, "write to network") ;
+ if (!timed_flush(&sf, &s_getfd, &s_isnonempty, &s_flush, deadline, stamp))
+ strerr_diefu3sys(111, "sendfile ", fn, " to network") ;
+}
+
+#else
+
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+void send_file (buffer *b, int fd, uint64_t n, char const *fn, tain const *deadline, tain *stamp)
+{
+ struct iovec v[2] ;
+ ssize_t r ;
+ if (!n) goto flushit ;
+ fillit:
+ buffer_wpeek(b, v) ;
+ r = allreadv(fd, v, 2) ;
+ if (r == -1) strerr_diefu2sys(111, "read from ", fn) ;
+ if (!r) strerr_diefu3x(111, "send ", fn, ": file was truncated") ;
+ if (r > n)
+ {
+ r = n ;
+ strerr_warnw2x("sending elongated file: ", fn) ;
+ }
+ buffer_wseek(b, r) ;
+ n -= r ;
+ flushit:
+ if (!buffer_timed_flush(b, deadline, stamp))
+ strerr_diefu1sys(111, "write to network") ;
+ if (n) goto fillit ;
+}
+
+#endif
diff --git a/src/include-local/apaste-common.h b/src/include-local/apaste-common.h
new file mode 100644
index 0000000..8a680d9
--- /dev/null
+++ b/src/include-local/apaste-common.h
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#ifndef APASTE_COMMON_H
+#define APASTE_COMMON_H
+
+#define APASTEC_BANNER "apastec protocol v1"
+#define APASTED_BANNER "apasted protocol v1"
+
+#endif
diff --git a/src/server/apasted.c b/src/server/apasted.c
new file mode 100644
index 0000000..b338154
--- /dev/null
+++ b/src/server/apasted.c
@@ -0,0 +1,339 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/siovec.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+
+#include <apaste/config.h>
+#include "apaste-common.h"
+
+#define USAGE "apasted [ -r rtimeout ] [ -w wtimeout ] [ -d rootdir ] [ -p prefix ] [ -m maxfiles ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define FORBIDDEN " \t\r\n\"<>&;"
+
+#define INDEXSTART "\
+<html>\n\
+ <head>\n\
+ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\
+ <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n\
+ <meta http-equiv=\"Content-Language\" content=\"en\" />\n\
+ <title>apaste results</title>\n\
+ <meta name=\"Description\" content=\"apaste results\" />\n\
+ </head>\n\
+<body>\n\
+<h3> apaste results </h3>\n\
+<ul>\n"
+
+static void cleanup (char const *dir)
+{
+ int e = errno ;
+ rm_rf(dir) ;
+ errno = e ;
+}
+
+static void prepare_index (buffer *ib, char const *dir, char *buf, size_t buflen)
+{
+ int fd ;
+ size_t dirlen = strlen(dir) ;
+ char fn[dirlen + 12] ;
+ memcpy(fn, dir, dirlen) ;
+ memcpy(fn + dirlen, "/index.html", 12) ;
+ fd = open_create(fn) ;
+ if (fd == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu3sys(111, "open ", dir, "/index.html for writing") ;
+ }
+ buffer_init(ib, &buffer_write, fd, buf, buflen) ;
+ if (buffer_puts(ib, INDEXSTART) == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "prepare index for ", dir) ;
+ }
+}
+
+static inline void add_index_entry (char const *dir, buffer *ib, char const *name, uint64_t filelen)
+{
+ char fmt[UINT64_FMT] ;
+ size_t m = uint64_fmt(fmt, filelen) ;
+ if (buffer_puts(ib, " <li> <a href=\"") == -1
+ || buffer_puts(ib, name) == -1
+ || buffer_puts(ib, ".txt\">") == -1
+ || buffer_puts(ib, name) == -1
+ || buffer_puts(ib, "</a> (") == -1
+ || buffer_put(ib, fmt, m) == -1
+ || buffer_puts(ib, " bytes) </li>\n") == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu5sys(111, "add ", name, " to ", dir, "/index.html") ;
+ }
+}
+
+static inline void finish_index (buffer *ib, char const *dir)
+{
+ if (buffer_putsflush(ib, "</ul>\n</body>\n</html>\n") == -1
+ || fsync(buffer_fd(ib)) == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "finish index for ", dir) ;
+ }
+ fd_close(buffer_fd(ib)) ;
+}
+
+static inline void recv_one_file (char const *dir, char const *fn, buffer *b, int fd, uint64_t n, tain const *deadline)
+{
+ struct iovec v[2] ;
+ size_t len ;
+ while (n)
+ {
+ unsigned int j = 2 ;
+ if (buffer_len(b) < n)
+ {
+ if (sanitize_read(buffer_timed_fill_g(b, deadline)) <= 0)
+ {
+ cleanup(dir) ;
+ _exit(1) ;
+ }
+ }
+ buffer_rpeek(b, v) ;
+ len = siovec_len(v, 2) ;
+ if (len > n)
+ {
+ j = siovec_trunc(v, 2, n) ;
+ len = n ;
+ }
+ if (allwritev(fd, v, j) < len)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "write to ", fn) ;
+ }
+ buffer_rseek(b, len) ;
+ n -= len ;
+ }
+}
+
+static inline size_t add_unique (stralloc *sa, size_t *indices, uint32_t n, char const *bname)
+{
+ size_t blen = strlen(bname) ;
+ size_t pos = sa->len ;
+ static uint32_t suffix = 0 ;
+ if (!stralloc_readyplus(sa, blen + UINT32_FMT + 2)) return 0 ;
+ memcpy(sa->s + pos, bname, blen + 1) ;
+ for (;;)
+ {
+ uint32_t i = 0 ;
+ for (; i < n ; i++) if (!strcmp(sa->s + indices[i], sa->s + pos)) break ;
+ if (i == n) break ;
+ sa->s[pos + blen] = '.' ;
+ sa->s[pos + blen + 1 + uint32_fmt(sa->s + pos + blen + 1, suffix++)] = 0 ;
+ }
+
+ blen = strlen(sa->s + pos) ;
+ indices[n] = sa->len ;
+ sa->len += blen + 1 ;
+ return blen ;
+}
+
+static void read_one_file (char const *dir, buffer *b, buffer *ib, stralloc *sa, size_t *indices, uint32_t n, tain const *deadline)
+{
+ uint64_t filelen ;
+ size_t bnamelen ;
+ uint16_t namelen ;
+
+ {
+ size_t w = 0 ;
+ size_t m ;
+ char fmt[UINT16_FMT] ;
+ if (sanitize_read(timed_getlnmax_g(b, fmt, UINT16_FMT, &w, '\n', deadline)) <= 0) goto err ;
+ m = uint16_scan(fmt, &namelen) ;
+ if (!m || m+1 != w || fmt[m] != '\n') goto err ;
+ }
+
+ {
+ size_t w = 0 ;
+ char *bname ;
+ char name[namelen + 1] ;
+ if (sanitize_read(timed_getlnmax_g(b, name, namelen + 1, &w, '\n', deadline)) <= 0) goto err ;
+ if (!w || w != namelen + 1 || name[namelen] != '\n') goto err ;
+ name[namelen] = 0 ;
+ bname = strrchr(name, '/') ;
+ if (bname) bname++ ; else bname = name ;
+ if (!*bname) goto err ;
+
+ if (ib) /* sanitize the filename for inclusion in index.html */
+ {
+ size_t blen = strlen(bname) ;
+ if (byte_in(bname, blen, FORBIDDEN, sizeof(FORBIDDEN)) != blen) goto err ;
+ bnamelen = add_unique(sa, indices, n, bname) ;
+ if (!bnamelen)
+ {
+ cleanup(dir) ;
+ strerr_diefu3sys(111, "make ", bname, " unique") ;
+ }
+ }
+ else bnamelen = 5 ;
+ }
+
+ {
+ size_t w = 0 ;
+ size_t m ;
+ char fmt[UINT64_FMT] ;
+ if (sanitize_read(timed_getlnmax_g(b, fmt, UINT64_FMT, &w, '\n', deadline)) <= 0) goto err ;
+ m = uint64_scan(fmt, &filelen) ;
+ if (!m || m+1 != w || fmt[m] != '\n') goto err ;
+ }
+
+ {
+ int fd ;
+ size_t dirlen = strlen(dir) ;
+ char fn[dirlen + bnamelen + 6] ;
+ memcpy(fn, dir, dirlen) ;
+ fn[dirlen] = '/' ;
+ memcpy(fn + dirlen + 1, ib ? sa->s + indices[n] : "index", bnamelen) ;
+ memcpy(fn + dirlen + 1 + bnamelen, ".txt", 5) ;
+ fd = open_create(fn) ;
+ if (fd == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu3sys(111, "open ", fn, " for writing") ;
+ }
+ recv_one_file(dir, fn, b, fd, filelen, deadline) ;
+ if (fsync(fd) == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "fsync ", fn) ;
+ }
+ fd_close(fd) ;
+ }
+
+ {
+ char c ;
+ if (sanitize_read(buffer_timed_get_g(b, &c, 1, deadline)) != 1) goto err ;
+ if (c != '\n') goto err ;
+ }
+
+ if (ib) add_index_entry(dir, ib, sa->s + indices[n], filelen) ;
+ return ;
+
+ err:
+ cleanup(dir) ;
+ _exit(1) ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ char const *prefix = "" ;
+ tain rtto = TAIN_INFINITE_RELATIVE, wtto = TAIN_INFINITE_RELATIVE ;
+ tain deadline ;
+ uint32_t maxfiles = 0 ;
+ uint32_t n ;
+ char buf[4097] ;
+ char dir[7] = "XXXXXX" ;
+ buffer b = BUFFER_INIT(&buffer_read, 0, buf, 4097) ;
+ PROG = "apasted" ;
+ {
+ char const *rootdir = 0 ;
+ uint32_t rt = 0, wt = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "r:w:d:p:m:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'r' : if (!uint320_scan(l.arg, &rt)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &wt)) dieusage() ; break ;
+ case 'd' : rootdir = l.arg ; break ;
+ case 'p' : prefix = l.arg ; break ;
+ case 'm' : if (!uint320_scan(l.arg, &maxfiles)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (rt) tain_from_millisecs(&rtto, rt) ;
+ if (wt) tain_from_millisecs(&rtto, wt) ;
+ if (rootdir && chdir(rootdir) == -1)
+ strerr_diefu2sys(111, "chdir to ", rootdir) ;
+ if (strlen(prefix) > 4000) strerr_dief1x(100, "prefix too long") ;
+ }
+
+ if (!sig_altignore(SIGPIPE))
+ strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_set_stopwatch_g() ;
+ tain_add_g(&deadline, &rtto) ;
+
+ {
+ char banner[256] ;
+ size_t w = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0) _exit(1) ;
+ if (w != sizeof(APASTEC_BANNER) || memcmp(banner, APASTEC_BANNER, w-1)) _exit(1) ;
+ }
+
+ {
+ char fmt[UINT32_FMT] ;
+ size_t w = 0 ;
+ size_t m ;
+ if (sanitize_read(timed_getlnmax_g(&b, fmt, UINT32_FMT, &w, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ m = uint32_scan(fmt, &n) ;
+ if (!m || m+1 != w) _exit(1) ;
+ if (!n || (maxfiles && n > maxfiles)) _exit(1) ;
+ }
+
+ if (!mkdtemp(dir)) strerr_diefu1sys(111, "mkdtemp") ;
+ if (n == 1) read_one_file(dir, &b, 0, 0, 0, 0, &deadline) ;
+ else
+ {
+ stralloc sa = STRALLOC_ZERO ;
+ size_t indices[n] ;
+ buffer ib ;
+ char ibuf[4096] ;
+ prepare_index(&ib, dir, ibuf, 4096) ;
+ for (uint32_t i = 0 ; i < n ; i++) read_one_file(dir, &b, &ib, &sa, indices, i, &deadline) ;
+ finish_index(&ib, dir) ;
+ }
+
+ {
+ char banner[256] ;
+ size_t w = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0
+ || w != sizeof(APASTEC_BANNER)
+ || memcmp(banner, APASTEC_BANNER, w-1)) goto err ;
+ }
+
+ buffer_init(&b, &buffer_write, 1, buf, 4097) ;
+ buffer_putsnoflush(&b, APASTED_BANNER "\n") ;
+ if (prefix[0]) buffer_putsnoflush(&b, prefix) ;
+ buffer_putnoflush(&b, dir, 6) ;
+ if (!strncmp(prefix, "http", 4)) buffer_putnoflush(&b, "/", 1) ;
+ buffer_putsnoflush(&b, "\n" APASTED_BANNER "\n") ;
+ tain_add_g(&deadline, &wtto) ;
+ if (!buffer_timed_flush_g(&b, &deadline)) goto err ;
+
+ return 0 ;
+
+ err:
+ cleanup(dir) ;
+ return 1 ;
+}
diff --git a/src/server/deps-exe/apasted b/src/server/deps-exe/apasted
new file mode 100644
index 0000000..720fe7d
--- /dev/null
+++ b/src/server/deps-exe/apasted
@@ -0,0 +1,3 @@
+-lskarnet
+${SOCKET_LIB}
+${SYSCLOCK_LIB}
diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh
new file mode 100755
index 0000000..befe021
--- /dev/null
+++ b/tools/gen-deps.sh
@@ -0,0 +1,100 @@
+#!/bin/sh -e
+
+. package/info
+
+echo '#'
+echo '# This file has been generated by tools/gen-deps.sh'
+echo '#'
+echo
+
+internal_libs=
+
+for dir in src/include/${package} src/* ; do
+ for file in $(ls -1 $dir | grep -- \\.h$) ; do
+ {
+ grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ;
+ grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2
+ } | sort -u | {
+ deps=
+ while read dep ; do
+ if echo $dep | grep -q "^${package}/" ; then
+ deps="$deps src/include/$dep"
+ elif test -f "${dir}/$dep" ; then
+ deps="$deps ${dir}/$dep"
+ else
+ deps="$deps src/include-local/$dep"
+ fi
+ done
+ if test -n "$deps" ; then
+ echo "${dir}/${file}:${deps}"
+ fi
+ }
+ done
+done
+
+for dir in src/* ; do
+ for file in $(ls -1 $dir | grep -- \\.c$) ; do
+ {
+ grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ;
+ grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2
+ } | sort -u | {
+ deps=" ${dir}/$file"
+ while read dep ; do
+ if echo $dep | grep -q "^${package}/" ; then
+ deps="$deps src/include/$dep"
+ elif test -f "${dir}/$dep" ; then
+ deps="$deps ${dir}/$dep"
+ else
+ deps="$deps src/include-local/$dep"
+ fi
+ done
+ o=$(echo $file | sed s/\\.c$/.o/)
+ lo=$(echo $file | sed s/\\.c$/.lo/)
+ echo "${dir}/${o} ${dir}/${lo}:${deps}"
+ }
+ done
+done
+echo
+
+for dir in $(ls -1 src | grep -v ^include) ; do
+ for file in $(ls -1 src/$dir/deps-lib) ; do
+ deps=
+ libs=
+ while read dep ; do
+ if echo $dep | grep -q -e ^-l -e '^\${.*_LIB}' ; then
+ libs="$libs $dep"
+ else
+ deps="$deps src/$dir/$dep"
+ fi
+ done < src/$dir/deps-lib/$file
+ echo 'ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)'
+ echo "lib${file}.a.xyzzy:$deps"
+ echo else
+ echo "lib${file}.a.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')"
+ echo endif
+ if grep -E "^LIB_DEFS [+:]=" package/targets.mak | grep -qF "$file" ; then
+ echo "lib${file}.so.xyzzy: EXTRA_LIBS :=$libs"
+ echo "lib${file}.so.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')"
+ else
+ internal_libs="$internal_libs lib${file}.a.xyzzy"
+ fi
+ done
+
+ for file in $(ls -1 src/$dir/deps-exe) ; do
+ deps=
+ libs=
+ while read dep ; do
+ if echo $dep | grep -q -- \\.o$ ; then
+ dep="src/$dir/$dep"
+ fi
+ if echo $dep | grep -q -e ^-l -e '^\${.*_LIB}' ; then
+ libs="$libs $dep"
+ else
+ deps="$deps $dep"
+ fi
+ done < src/$dir/deps-exe/$file
+ echo "$file: EXTRA_LIBS :=$libs"
+ echo "$file: src/$dir/$file.o$deps"
+ done
+done
+echo "INTERNAL_LIBS :=$internal_libs"
diff --git a/tools/install.sh b/tools/install.sh
new file mode 100755
index 0000000..e96dd7b
--- /dev/null
+++ b/tools/install.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+usage() {
+ echo "usage: $0 [ -D ] [ -l ] [ -m mode ] [ -O owner:group ] src dst" 1>&2
+ exit 1
+}
+
+mkdirp=false
+symlink=false
+mode=0755
+og=
+
+while getopts Dlm:O: name ; do
+ case "$name" in
+ D) mkdirp=true ;;
+ l) symlink=true ;;
+ m) mode=$OPTARG ;;
+ O) og=$OPTARG ;;
+ ?) usage ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+test "$#" -eq 2 || usage
+src=$1
+dst=$2
+tmp="$dst.tmp.$$"
+
+case "$dst" in
+ */) echo "$0: $dst ends in /" 1>&2 ; exit 1 ;;
+esac
+
+set -C
+set -e
+
+if $mkdirp ; then
+ umask 022
+ case "$2" in
+ */*) mkdir -p "${dst%/*}" ;;
+ esac
+fi
+
+trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP
+
+umask 077
+
+if $symlink ; then
+ ln -s "$src" "$tmp"
+else
+ cat < "$1" > "$tmp"
+ if test -n "$og" ; then
+ chown -- "$og" "$tmp"
+ fi
+ chmod -- "$mode" "$tmp"
+fi
+
+mv -f "$tmp" "$dst"
+if test -d "$dst" ; then
+ rm -f "$dst/$(basename $tmp)"
+ if $symlink ; then
+ mkdir "$tmp"
+ ln -s "$src" "$tmp/$(basename $dst)"
+ mv -f "$tmp/$(basename $dst)" "${dst%/*}"
+ rmdir "$tmp"
+ else
+ echo "$0: $dst is a directory" 1>&2
+ exit 1
+ fi
+fi