Compare commits
10 commits
49acb75498
...
dcbb488f7d
Author | SHA1 | Date | |
---|---|---|---|
dcbb488f7d | |||
8395a05807 | |||
f16a9f4c6d | |||
983fabcd2a | |||
7e21b2bfce | |||
ffc91b9114 | |||
|
9c4c3fc899 | ||
|
a19ebea78f | ||
d643cc876b | |||
8e8342528f |
15 changed files with 449 additions and 525 deletions
|
@ -1,3 +0,0 @@
|
|||
build
|
||||
out
|
||||
*-leaked.txt
|
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Cytoplasm .gitignore
|
||||
|
||||
build
|
||||
out
|
||||
*-leaked.txt
|
||||
*.orig
|
||||
|
||||
Makefile
|
78
README.md
Normal file
78
README.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Cytoplasm (libcytoplasm)
|
||||
|
||||
Cytoplasm is a general-purpose C library and runtime stub for creating high-level (particularly networked and multi-threaded) C applications. It allows applications to take advantage of the speed, flexibility, and simplicity of the C programming language, while providing helpful code to perform various complex tasks. Cytoplasm provides high-level data structures, a basic logging facility, an HTTP client and server, and more.
|
||||
|
||||
Cytoplasm aims not to only do one thing well, but to do many things good enough. The primary target of Cytoplasm is simple, yet higher level C applications that have to perform relatively complex tasks, but don't want to pull in a large number of dependencies.
|
||||
|
||||
Cytoplasm is extremely opinionated on the way programs using it are written. It strives to create a comprehensive and tightly-integrated programming environment, while also maintaining C programming correctness. It doesn't do any macro magic or make C look like anything other than C. It is written entirely in C89, and depends only on a POSIX environment. This differentiates it from other general-purpose libraries that often require modern compilers and non-standard language and environment features. Cytoplasm is intended to be extremely portable and simple, while still providing some of the functionality expected in higher-level programming languages in a platform-agnostic manner. In the case of TLS, Cytoplasm wraps low-level TLS libraries to offer a single, unified interface to TLS so that programs do not have to care about the underlying implementation.
|
||||
|
||||
Cytoplasm is probably not suitable for embedded programming. It makes liberal use of the heap, and while data structures are designed to conserve memory where possible and practical, minimal memory usage is not really a design goal for Cytoplasm, although Cytoplasm takes care not to use any more memory than it absolutely needs. Cytoplasm also wraps a few standard libraries with additional logic and checking. While this ensures better runtime safetly, this inevitably adds a little overhead.
|
||||
|
||||
Originally a part of Telodendria (https://telodendria.io), a Matrix homeserver written in C, Cytoplasm was split off into its own project due to the desire of some Telodendria developers to use Telodendria's code in other projects. Cytoplasm is still a Telodendria project, and is maintained along side of Telodendria itself, even living in the same CVS module, but it is designed specifically to be distributed and used totally independent of Telodendria.
|
||||
|
||||
The name "Cytoplasm" was chosen for a few reasons. It plays off the precedent set up by the Matrix organization in naming projects after the parts of a neuron. It also speaks to the function of Cytoplasm. The cytoplasm of a cell is the supporting material. It is what gives the cell its shape, and it facilitates the movement of materials to the other cell parts. Likewise, Cytoplasm aims to provide a support mechanism for C applications that have to perform complex tasks.
|
||||
|
||||
Cytoplasm also starts with a C, which I think is a nice touch for C libraries. It's also fun to say and unique enough that searching for "libcytoplasm" should bring you to this project and not some other one.
|
||||
|
||||
## Building
|
||||
|
||||
If your operating system or software distribution provides a pre-built package of Cytoplasm, you should prefer to use that instead of building it from source.
|
||||
|
||||
Cytoplasm aims to have zero dependencies beyond what is mandated by POSIX. You only need the standard math and pthread libraries to build it. TLS support can optionally be enabled by setting the TLS_IMPL environment variable. The supported TLS implementations are as follows:
|
||||
|
||||
- OpenSSL
|
||||
- LibreSSL
|
||||
|
||||
If TLS support is not enabled, all APIs that use it should fall back to non-TLS behavior in a sensible manner. For example, if TLS support is not enabled, then the HTTP client API will simply return an error if a TLS connection is requested. Cytoplasm uses a custom build script instead of Make, for the sake of portability. To build everything, just run the script:
|
||||
|
||||
$ sh make.sh
|
||||
|
||||
This will produce the following out/ directory:
|
||||
|
||||
```
|
||||
out/
|
||||
lib/
|
||||
libcytoplasm.so - The Cytoplasm shared library.
|
||||
libcytoplasm.a - The Cytoplasm static library.
|
||||
cytoplasm.o - The Cytoplasm runtime stub.
|
||||
bin/
|
||||
hdoc - The documentation generator tool.
|
||||
man/ - All Cytoplasm API documentation.
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Cytoplasm provides the typical .so and .a files, which can be used to link programs with it in the usual way. However, Cytoplasm also provides a minimal runtime environment that is intended to be used with the library. As such, it also provides a runtime stub, which is intended to be linked in with programs using Cytoplasm. This stub is responsible for setting up and tearing down some Cytoplasm APIs. While it isn't required by any means, it makes Cytoplasm a lot easier to use for programmers by abstracting out all of the common logic that most programs will want to use.
|
||||
|
||||
Here is the canonical Hello World written with Cytoplasm:
|
||||
|
||||
```c
|
||||
#include <Log.h>
|
||||
|
||||
int Main(void)
|
||||
{
|
||||
Log(LOG_INFO, "Hello World!");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
If this file is Hello.c, then you can compile it by doing something like this:
|
||||
|
||||
$ cc -o hello Hello.c cytoplasm.o -lcytoplasm
|
||||
|
||||
This command assumes that the runtime stub resides in the current working directory, and that libcytoplasm.so is in your library path. If you're using the version of Cytoplasm installed by your operating system or software distribution, consult the documentation for the location of the runtime stub. It may be located in /usr/local/libexec or some other similar location. If you've built Cytoplasm from source and wish to link to it from there, you may wish to do something like this:
|
||||
|
||||
$ export CYTOPLASM=/path/to/Cytoplasm/out/lib
|
||||
$ cc -o hello Hello.c "${CYTOPLASM}/cytoplasm.o" \
|
||||
"-L${CYTOPLASM}" -lcytoplasm
|
||||
|
||||
As you may have noticed, C programs using Cytoplasm's runtime stub don't write the main() function. Instead, they use Main(). The main() function is provided by the runtime stub. The full form of Main() expected by the stub is as follows:
|
||||
|
||||
```c
|
||||
int Main(Array *args, HashMap *env);
|
||||
```
|
||||
|
||||
The first argument is a Cytoplasm array of the command line arguments, and the second is a Cytoplasm hash map of environment variables. Most linkers will let programs omit the env argument, or both arguments if you don't need either. The return value of Main() is returned to the operating system.
|
||||
|
||||
Note that both arguments to Main may be treated like any other array or hash map. However, do not invoke ArrayFree() or HashMapFree() on the passed pointers, because memory is cleaned up after Main() returns.
|
||||
|
158
README.txt
158
README.txt
|
@ -1,158 +0,0 @@
|
|||
Cytoplasm (libcytoplasm)
|
||||
========================================================================
|
||||
|
||||
Cytoplasm is a general-purpose C library and runtime stub for
|
||||
creating high-level (particularly networked and multi-threaded) C
|
||||
applications. It allows applications to take advantage of the speed,
|
||||
flexibility, and simplicity of the C programming language, while
|
||||
providing helpful code to perform various complex tasks. Cytoplasm
|
||||
provides high-level data structures, a basic logging facility, an
|
||||
HTTP client and server, and more.
|
||||
|
||||
Cytoplasm aims not to only do one thing well, but to do many things
|
||||
good enough. The primary target of Cytoplasm is simple, yet higher
|
||||
level C applications that have to perform relatively complex tasks,
|
||||
but don't want to pull in a large number of dependencies.
|
||||
|
||||
Cytoplasm is extremely opinionated on the way programs using it are
|
||||
written. It strives to create a comprehensive and tightly-integrated
|
||||
programming environment, while also maintaining C programming
|
||||
correctness. It doesn't do any macro magic or make C look like
|
||||
anything other than C. It is written entirely in C89, and depends
|
||||
only on a POSIX environment. This differentiates it from other
|
||||
general-purpose libraries that often require modern compilers and
|
||||
non-standard language and environment features. Cytoplasm is intended
|
||||
to be extremely portable and simple, while still providing some of
|
||||
the functionality expected in higher-level programming languages
|
||||
in a platform-agnostic manner. In the case of TLS, Cytoplasm wraps
|
||||
low-level TLS libraries to offer a single, unified interface to TLS
|
||||
so that programs do not have to care about the underlying
|
||||
implementation.
|
||||
|
||||
Cytoplasm is probably not suitable for embedded programming. It makes
|
||||
liberal use of the heap, and while data structures are designed to
|
||||
conserve memory where possible and practical, minimal memory usage
|
||||
is not really a design goal for Cytoplasm, although Cytoplasm takes
|
||||
care not to use any more memory than it absolutely needs. Cytoplasm
|
||||
also wraps a few standard libraries with additional logic and
|
||||
checking. While this ensures better runtime safetly, this inevitably
|
||||
adds a little overhead.
|
||||
|
||||
Originally a part of Telodendria (https://telodendria.io), a Matrix
|
||||
homeserver written in C, Cytoplasm was split off into its own project
|
||||
due to the desire of some Telodendria developers to use Telodendria's
|
||||
code in other projects. Cytoplasm is still a Telodendria project,
|
||||
and is maintained along side of Telodendria itself, even living in
|
||||
the same CVS module, but it is designed specifically to be distributed
|
||||
and used totally independent of Telodendria.
|
||||
|
||||
The name "Cytoplasm" was chosen for a few reasons. It plays off the
|
||||
precedent set up by the Matrix organization in naming projects after
|
||||
the parts of a neuron. It also speaks to the function of Cytoplasm.
|
||||
The cytoplasm of a cell is the supporting material. It is what gives
|
||||
the cell its shape, and it facilitates the movement of materials
|
||||
to the other cell parts. Likewise, Cytoplasm aims to provide a
|
||||
support mechanism for C applications that have to perform complex
|
||||
tasks.
|
||||
|
||||
Cytoplasm also starts with a C, which I think is a nice touch for C
|
||||
libraries. It's also fun to say and unique enough that searching for
|
||||
"libcytoplasm" should bring you to this project and not some other
|
||||
one.
|
||||
|
||||
Building
|
||||
------------------------------------------------------------------------
|
||||
|
||||
If your operating system or software distribution provides a pre-built
|
||||
package of Cytoplasm, you should prefer to use that instead of
|
||||
building it from source.
|
||||
|
||||
Cytoplasm aims to have zero dependencies beyond what is mandated
|
||||
by POSIX. You only need the standard math and pthread libraries to
|
||||
build it. TLS support can optionally be enabled by setting the
|
||||
TLS_IMPL environment variable. The supported TLS implementations
|
||||
are as follows:
|
||||
|
||||
* OpenSSL
|
||||
* LibreSSL
|
||||
|
||||
If TLS support is not enabled, all APIs that use it should fall
|
||||
back to non-TLS behavior in a sensible manner. For example, if TLS
|
||||
support is not enabled, then the HTTP client API will simply return
|
||||
an error if a TLS connection is requested.
|
||||
|
||||
Cytoplasm uses a custom build script instead of Make, for the sake
|
||||
of portability. To build everything, just run the script:
|
||||
|
||||
$ sh make.sh
|
||||
|
||||
This will produce the following out/ directory:
|
||||
|
||||
out/
|
||||
lib/
|
||||
libcytoplasm.so - The Cytoplasm shared library.
|
||||
libcytoplasm.a - The Cytoplasm static library.
|
||||
cytoplasm.o - The Cytoplasm runtime stub.
|
||||
bin/
|
||||
hdoc - The documentation generator tool.
|
||||
man/ - All Cytoplasm API documentation.
|
||||
|
||||
Usage
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Cytoplasm provides the typical .so and .a files, which can be used
|
||||
to link programs with it in the usual way. However, Cytoplasm also
|
||||
provides a minimal runtime environment that is intended to be used
|
||||
with the library. As such, it also provides a runtime stub, which
|
||||
is intended to be linked in with programs using Cytoplasm. This
|
||||
stub is responsible for setting up and tearing down some Cytoplasm
|
||||
APIs. While it isn't required by any means, it makes Cytoplasm a
|
||||
lot easier to use for programmers by abstracting out all of the
|
||||
common logic that most programs will want to use.
|
||||
|
||||
Here is the canonical Hello World written with Cytoplasm:
|
||||
|
||||
#include <Log.h>
|
||||
|
||||
int Main(void)
|
||||
{
|
||||
Log(LOG_INFO, "Hello World!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
If this file is Hello.c, then you can compile it by doing something
|
||||
like this:
|
||||
|
||||
$ cc -o hello Hello.c cytoplasm.o -lcytoplasm
|
||||
|
||||
This command assumes that the runtime stub resides in the current
|
||||
working directory, and that libcytoplasm.so is in your library path.
|
||||
If you're using the version of Cytoplasm installed by your operating
|
||||
system or software distribution, consult the documentation for the
|
||||
location of the runtime stub. It may be located in
|
||||
/usr/local/libexec or some other similar location. If you've built
|
||||
Cytoplasm from source and wish to link to it from there, you may wish
|
||||
to do something like this:
|
||||
|
||||
$ export CYTOPLASM=/path/to/Cytoplasm/out/lib
|
||||
$ cc -o hello Hello.c "${CYTOPLASM}/cytoplasm.o" \
|
||||
"-L${CYTOPLASM}" -lcytoplasm
|
||||
|
||||
As you may have noticed, C programs using Cytoplasm's runtime stub
|
||||
don't write the main() function. Instead, they use Main(). The main()
|
||||
function is provided by the runtime stub. The full form of Main()
|
||||
expected by the stub is as follows:
|
||||
|
||||
int Main(Array *args, HashMap *env);
|
||||
|
||||
The first argument is a Cytoplasm array of the command line
|
||||
arguments, and the second is a Cytoplasm hash map of environment
|
||||
variables. Most linkers will let programs omit the env argument,
|
||||
or both arguments if you don't need either. The return value of
|
||||
Main() is returned to the operating system.
|
||||
|
||||
Note that both arguments to Main may be treated like any other
|
||||
array or hash map. However, do not invoke ArrayFree() or HashMapFree()
|
||||
on the passed pointers, because memory is cleaned up after Main()
|
||||
returns.
|
||||
|
251
configure
vendored
Executable file
251
configure
vendored
Executable file
|
@ -0,0 +1,251 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Argument Parsing
|
||||
#
|
||||
|
||||
echo "Build Configuration"
|
||||
echo "-------------------"
|
||||
|
||||
BUILD="build"
|
||||
OUT="out"
|
||||
SRC="src"
|
||||
INCLUDE="src/include"
|
||||
TOOLS="tools"
|
||||
|
||||
CFLAGS="-Wall -Wextra -pedantic -std=c89 -O3 -pipe -D_DEFAULT_SOURCE -I${INCLUDE}"
|
||||
LIBS="-lm -pthread"
|
||||
|
||||
|
||||
# Set default args for all platforms
|
||||
SCRIPT_ARGS="--lib-rtstub=RtStub --prefix=/usr/local --enable-ld-extra --lib-name=Cytoplasm --lib-version=0.4.0 --static $@"
|
||||
|
||||
# Set platform specific args
|
||||
case "$(uname)" in
|
||||
OpenBSD)
|
||||
SCRIPT_ARGS="--with-libressl $SCRIPT_ARGS"
|
||||
;;
|
||||
*)
|
||||
SCRIPT_ARGS="--with-openssl $SCRIPT_ARGS"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Processing options..."
|
||||
echo "Ran with arguments: $SCRIPT_ARGS"
|
||||
|
||||
# Process all arguments
|
||||
for arg in $SCRIPT_ARGS; do
|
||||
case "$arg" in
|
||||
--with-openssl)
|
||||
TLS_IMPL="OPENSSL"
|
||||
TLS_LIBS="-lcrypto -lssl"
|
||||
;;
|
||||
--with-libressl)
|
||||
TLS_IMPL="LIBRESSL"
|
||||
TLS_LIBS="-ltls -lcrypto -lssl"
|
||||
;;
|
||||
--disable-tls)
|
||||
TLS_LIBS=""
|
||||
;;
|
||||
--prefix=*)
|
||||
PREFIX=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--enable-ld-extra)
|
||||
LD_EXTRA="-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections"
|
||||
;;
|
||||
--disable-ld-extra)
|
||||
LD_EXTRA=""
|
||||
;;
|
||||
--lib-name=*)
|
||||
LIB_NAME=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--lib-version=*)
|
||||
LIB_VERSION=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--enable-debug)
|
||||
DEBUG="-O0 -g"
|
||||
echo "Notice: --enable-debug implies --disable-ld-extra and --no-static."
|
||||
echo "You must explicitly provide --enable-ld-extra and/or --static after"
|
||||
echo "specifying --enable-debug if you wish to enable these features in debug mode."
|
||||
LD_EXTRA=""
|
||||
STATIC=""
|
||||
;;
|
||||
--disable-debug)
|
||||
DEBUG=""
|
||||
;;
|
||||
--lib-rtstub=*)
|
||||
STUB=$(echo "$arg" | cut -d '=' -f 2-)
|
||||
;;
|
||||
--static)
|
||||
STATIC="-static -Wl,-static"
|
||||
;;
|
||||
--no-static)
|
||||
STATIC=""
|
||||
;;
|
||||
*)
|
||||
echo "Invalid argument: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$TLS_IMPL" ]; then
|
||||
CFLAGS="${CFLAGS} -DTLS_IMPL=${TLS_IMPL}"
|
||||
LIBS="${LIBS} ${TLS_LIBS}"
|
||||
fi
|
||||
|
||||
CFLAGS="${CFLAGS} '-DLIB_NAME=\"${LIB_NAME}\"' '-DLIB_VERSION=\"${LIB_VERSION}\"' ${DEBUG}"
|
||||
LDFLAGS="${LIBS} ${LD_EXTRA}"
|
||||
|
||||
#
|
||||
# Makefile generation
|
||||
#
|
||||
|
||||
collect() {
|
||||
from="$1"
|
||||
orig_ext="$2"
|
||||
new_ext="$3"
|
||||
prefix="$4"
|
||||
exec="$5"
|
||||
|
||||
find "${from}" -name "*${orig_ext}" -type f | while IFS= read -r src; do
|
||||
src=$(echo "$src" | sed -e "s|^${from}||g")
|
||||
obj=$(echo "$src" | sed -e "s|${orig_ext}\$|${new_ext}|g")
|
||||
|
||||
obj="${prefix}${obj}"
|
||||
src="${from}${src}"
|
||||
|
||||
"${exec}" "${src}" "${obj}"
|
||||
done
|
||||
}
|
||||
|
||||
print_src() {
|
||||
printf '%s ' "$1"
|
||||
}
|
||||
|
||||
print_obj() {
|
||||
printf '%s ' "$2"
|
||||
}
|
||||
|
||||
compile_obj() {
|
||||
src="$1"
|
||||
obj="$2"
|
||||
|
||||
cc -I${INCLUDE} -MM -MT "${obj}" "${src}"
|
||||
echo "${TAB}@mkdir -p $(dirname ${obj})"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
|
||||
}
|
||||
|
||||
compile_bin() {
|
||||
src="$1"
|
||||
out="$2"
|
||||
|
||||
echo "${out}: ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so ${OUT}/lib/${LIB_NAME}.o ${src}"
|
||||
echo "${TAB}@mkdir -p ${OUT}/bin"
|
||||
echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" ${OUT}/lib/${LIB_NAME}.o -L${OUT}/lib \$(LDFLAGS) -l${LIB_NAME} ${STATIC}"
|
||||
}
|
||||
|
||||
compile_doc() {
|
||||
src="$1"
|
||||
out="$2"
|
||||
|
||||
echo "${out}: ${OUT}/bin/hdoc ${src}"
|
||||
echo "${TAB}@mkdir -p ${OUT}/man/man3"
|
||||
echo "${TAB}${OUT}/bin/hdoc -D \"Os=${LIB_NAME}\" -i \"${src}\" -o \"${out}\""
|
||||
}
|
||||
|
||||
install_out() {
|
||||
src="$1"
|
||||
out="$2"
|
||||
|
||||
echo "${TAB}install -D \"$src\" \"$out\""
|
||||
}
|
||||
|
||||
install_man() {
|
||||
src="${OUT}/man/man3/${LIB_NAME}-$(basename $1 .h).3"
|
||||
out="$2"
|
||||
|
||||
echo "${TAB}install -D \"$src\" \"$out\""
|
||||
}
|
||||
|
||||
uninstall_out() {
|
||||
src="$1"
|
||||
out="$2"
|
||||
|
||||
echo "${TAB}rm \"$out\""
|
||||
}
|
||||
|
||||
echo "Generating Makefile..."
|
||||
|
||||
OBJS=$(collect ${SRC}/ .c .o ${BUILD}/ print_obj)
|
||||
TAB=$(printf '\t')
|
||||
|
||||
cat << EOF > Makefile
|
||||
.POSIX:
|
||||
|
||||
# Generated by '$0' on $(date).
|
||||
# This file should generally not be manually edited.
|
||||
|
||||
PREFIX = ${PREFIX}
|
||||
CFLAGS = ${CFLAGS}
|
||||
LDFLAGS = ${LDFLAGS}
|
||||
|
||||
all: ${LIB_NAME} docs tools
|
||||
docs: $(collect ${INCLUDE}/ .h .3 ${OUT}/man/man3/${LIB_NAME}- print_obj)
|
||||
tools: $(collect ${TOOLS}/ .c '' ${OUT}/bin/ print_obj)
|
||||
|
||||
print-libs:
|
||||
${TAB}@echo ${LIBS}
|
||||
|
||||
format:
|
||||
${TAB}find . -name '*.c' | while IFS= read -r src; do \\
|
||||
${TAB} if indent "\$\$src"; then \\
|
||||
${TAB} rm \$\$(basename "\$\$src").BAK; \\
|
||||
${TAB} fi \\
|
||||
${TAB}done
|
||||
|
||||
license:
|
||||
${TAB}find . -name '*.[ch]' | while IFS= read -r src; do \\
|
||||
${TAB} srcHeader=\$\$(grep -n -m 1 '^ \*/' "\$\$src" | cut -d ':' -f 1); \\
|
||||
${TAB} head -n\$\$srcHeader \$\$src | \\
|
||||
${TAB} diff -u -p - "LICENSE.txt" | \\
|
||||
${TAB} patch "\$\$src" | grep -v "^Hmm"; \\
|
||||
${TAB}done
|
||||
|
||||
${LIB_NAME}: ${OUT}/lib/${LIB_NAME}.o ${OUT}/lib/lib${LIB_NAME}.a ${OUT}/lib/lib${LIB_NAME}.so
|
||||
|
||||
install: ${LIB_NAME}
|
||||
${TAB}install -D ${OUT}/lib/${LIB_NAME}.o \$(PREFIX)/lib/${LIB_NAME}.o
|
||||
${TAB}install -D ${OUT}/lib/lib${LIB_NAME}.a \$(PREFIX)/lib/lib${LIB_NAME}.a
|
||||
${TAB}install -D ${OUT}/lib/lib${LIB_NAME}.so \$(PREFIX)/lib/lib${LIB_NAME}.so
|
||||
$(collect ${INCLUDE}/ '' '' \$\(PREFIX\)/include/${LIB_NAME}/ install_out)
|
||||
$(collect ${INCLUDE}/ .h .3 \$\(PREFIX\)/man/man3/${LIB_NAME}- install_man)
|
||||
|
||||
uninstall:
|
||||
${TAB}rm -r \$(PREFIX)/lib/${LIB_NAME}.*
|
||||
${TAB}rm -r \$(PREFIX)/lib/lib${LIB_NAME}.*
|
||||
${TAB}rm -r \$(PREFIX)/include/${LIB_NAME}
|
||||
${TAB}rm -r \$(PREFIX)/man/man3/${LIB_NAME}-*
|
||||
|
||||
clean:
|
||||
${TAB}rm -r "${BUILD}" "${OUT}"
|
||||
|
||||
${OUT}/lib/${LIB_NAME}.o: ${BUILD}/${STUB}.o
|
||||
${TAB}@mkdir -p ${OUT}/lib
|
||||
${TAB}cp ${BUILD}/${STUB}.o ${OUT}/lib/${LIB_NAME}.o
|
||||
|
||||
${OUT}/lib/lib${LIB_NAME}.a: ${OBJS}
|
||||
${TAB}@mkdir -p ${OUT}/lib
|
||||
${TAB}\$(AR) rcs ${OUT}/lib/lib${LIB_NAME}.a ${OBJS}
|
||||
|
||||
${OUT}/lib/lib${LIB_NAME}.so: ${OBJS}
|
||||
${TAB}@mkdir -p ${OUT}/lib
|
||||
${TAB}\$(CC) -shared -o ${OUT}/lib/lib${LIB_NAME}.so ${OBJS} ${LDFLAGS}
|
||||
|
||||
$(collect ${SRC}/ .c .o ${BUILD}/ compile_obj)
|
||||
$(collect ${TOOLS}/ .c '' ${OUT}/bin/ compile_bin)
|
||||
$(collect ${INCLUDE}/ .h .3 ${OUT}/man/man3/${LIB_NAME}- compile_doc)
|
||||
|
||||
EOF
|
||||
|
||||
echo "Done. Run 'make' to build ${LIB_NAME}."
|
|
@ -1,3 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <Array.h>
|
||||
#include <HashMap.h>
|
||||
|
||||
|
|
328
make.sh
328
make.sh
|
@ -1,328 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
addprefix() {
|
||||
prefix="$1"
|
||||
shift
|
||||
for thing in "$@"; do
|
||||
echo "${prefix}${thing}"
|
||||
done
|
||||
|
||||
unset prefix
|
||||
unset thing
|
||||
}
|
||||
|
||||
: "${NAME:=Cytoplasm}"
|
||||
: "${LIB_NAME:=$(echo ${NAME} | tr '[A-Z]' '[a-z]')}"
|
||||
: "${VERSION:=0.4.0}"
|
||||
|
||||
: "${CVS_TAG:=${NAME}-$(echo ${VERSION} | sed 's/\./_/g')}"
|
||||
|
||||
|
||||
: "${SRC:=src}"
|
||||
: "${TOOLS:=tools}"
|
||||
: "${EXAMPLES:=examples}"
|
||||
: "${BUILD:=build}"
|
||||
: "${OUT:=out}"
|
||||
: "${STUB:=RtStub}"
|
||||
: "${LICENSE:=LICENSE.txt}"
|
||||
|
||||
: "${CC:=cc}"
|
||||
: "${AR:=ar}"
|
||||
|
||||
: "${DEFINES:=-D_DEFAULT_SOURCE -DLIB_NAME=\"${NAME}\" -DLIB_VERSION=\"${VERSION}\"}"
|
||||
: "${INCLUDE:=${SRC}/include}"
|
||||
|
||||
: "${CFLAGS:=-Wall -Wextra -pedantic -std=c89 -O3 -pipe}"
|
||||
: "${LD_EXTRA:=-flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections}"
|
||||
: "${LDFLAGS:=-lm -pthread}"
|
||||
|
||||
if [ "${DEBUG}" = "1" ]; then
|
||||
CFLAGS="${CFLAGS} -O0 -g"
|
||||
LD_EXTRA=""
|
||||
fi
|
||||
|
||||
if [ -n "${TLS_IMPL}" ]; then
|
||||
case "${TLS_IMPL}" in
|
||||
"LIBRESSL")
|
||||
TLS_LIBS="-ltls -lcrypto -lssl"
|
||||
;;
|
||||
"OPENSSL")
|
||||
TLS_LIBS="-lcrypto -lssl"
|
||||
;;
|
||||
*)
|
||||
echo "Unrecognized TLS implementation: ${TLS_IMPL}"
|
||||
echo "Consult Tls.h for supported implementations."
|
||||
echo "Note that the TLS_ prefix is omitted in TLS_IMPL."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
DEFINES="${DEFINES} -DTLS_IMPL=TLS_${TLS_IMPL}"
|
||||
LDFLAGS="${LDFLAGS} ${TLS_LIBS}"
|
||||
fi
|
||||
|
||||
CFLAGS="${CFLAGS} ${DEFINES} $(addprefix -I$(pwd)/ ${INCLUDE})"
|
||||
LDFLAGS="${LDFLAGS} ${LD_EXTRA}"
|
||||
|
||||
# Check the modificiation time of a file. This is used to do
|
||||
# incremental builds; we only want to rebuild files that have
|
||||
# have changed.
|
||||
mod_time() {
|
||||
if [ -n "$1" ] && [ -f "$1" ]; then
|
||||
case "$(uname)" in
|
||||
Linux | CYGWIN_NT* | Haiku)
|
||||
stat -c %Y "$1"
|
||||
;;
|
||||
*BSD | DragonFly | Minix)
|
||||
stat -f %m "$1"
|
||||
;;
|
||||
*)
|
||||
# Platform unknown, force rebuilding the whole
|
||||
# project every time.
|
||||
echo "0"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
# Substitute shell variables in a stream with their actual value
|
||||
# in this shell.
|
||||
setsubst() {
|
||||
SED="/tmp/sed-$RANDOM.txt"
|
||||
|
||||
(
|
||||
set | while IFS='=' read -r var val; do
|
||||
val=$(echo "$val" | cut -d "'" -f 2- | rev | cut -d "'" -f 2- | rev)
|
||||
echo "s|\\\${$var}|$val|g"
|
||||
done
|
||||
|
||||
echo "s|\\\${[a-zA-Z_]*}||g"
|
||||
echo "s|'''|'|g"
|
||||
) >"$SED"
|
||||
|
||||
sed -f "$SED" $@
|
||||
rm "$SED"
|
||||
}
|
||||
|
||||
recipe_docs() {
|
||||
export LD_LIBRARY_PATH="${OUT}/lib"
|
||||
mkdir -p "${OUT}/man/man3"
|
||||
|
||||
for header in $(find ${INCLUDE} -name '*.h'); do
|
||||
basename=$(basename "$header")
|
||||
man=$(echo "${OUT}/man/man3/$basename" | sed -e 's/\.h$/\.3/')
|
||||
|
||||
if [ $(mod_time "$header") -ge $(mod_time "$man") ]; then
|
||||
echo "DOC $basename"
|
||||
if ! "${OUT}/bin/hdoc" -D "Os=${NAME}" -i "$header" -o "$man"; then
|
||||
rm "$man"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if which makewhatis 2>&1 > /dev/null; then
|
||||
makewhatis "${OUT}/man"
|
||||
fi
|
||||
}
|
||||
|
||||
recipe_libs() {
|
||||
echo "-lm -pthread ${TLS_LIBS}"
|
||||
}
|
||||
|
||||
recipe_build() {
|
||||
mkdir -p "${BUILD}" "${OUT}/bin" "${OUT}/lib"
|
||||
cd "${SRC}"
|
||||
|
||||
|
||||
echo "CC = ${CC}"
|
||||
echo "CFLAGS = ${CFLAGS}"
|
||||
echo "LDFLAGS = ${LDFLAGS}"
|
||||
echo
|
||||
|
||||
do_rebuild=0
|
||||
objs=""
|
||||
for src in $(find . -name '*.c' | cut -d '/' -f 2-); do
|
||||
obj=$(echo "${BUILD}/$src" | sed -e 's/\.c$/\.o/')
|
||||
|
||||
if [ $(basename "$obj" .o) != "${STUB}" ]; then
|
||||
objs="$objs $obj"
|
||||
fi
|
||||
|
||||
if [ $(mod_time "$src") -ge $(mod_time "../$obj") ]; then
|
||||
echo "CC $(basename $obj)"
|
||||
obj_dir=$(dirname "../$obj")
|
||||
mkdir -p "$obj_dir"
|
||||
if ! $CC $CFLAGS -fPIC -c -o "../$obj" "$src"; then
|
||||
exit 1
|
||||
fi
|
||||
do_rebuild=1
|
||||
fi
|
||||
done
|
||||
|
||||
cd ..
|
||||
|
||||
if [ $do_rebuild -eq 1 ] || [ ! -f "${OUT}/lib/lib${LIB_NAME}.a" ]; then
|
||||
echo "AR lib${LIB_NAME}.a"
|
||||
if ! $AR rcs "${OUT}/lib/lib${LIB_NAME}.a" $objs; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $do_rebuild -eq 1 ] || [ ! -f "${OUT}/lib/lib${LIB_NAME}.so" ]; then
|
||||
echo "LD lib${LIB_NAME}.so"
|
||||
if ! $CC -shared -o "${OUT}/lib/lib${LIB_NAME}.so" $objs ${LDFLAGS}; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cp "${BUILD}/${STUB}.o" "${OUT}/lib/${LIB_NAME}.o"
|
||||
|
||||
for src in $(find "${TOOLS}" -name '*.c'); do
|
||||
out=$(basename "$src" .c)
|
||||
out="${OUT}/bin/$out"
|
||||
|
||||
if [ $(mod_time "$src") -ge $(mod_time "$out") ] || [ $do_rebuild -eq 1 ]; then
|
||||
echo "CC $(basename $out)"
|
||||
mkdir -p "$(dirname $out)"
|
||||
if ! $CC $CFLAGS -o "$out" "$src" "${OUT}/lib/${LIB_NAME}.o" "-L${OUT}/lib" "-l${LIB_NAME}" ${LDFLAGS}; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
recipe_docs
|
||||
}
|
||||
|
||||
recipe_examples() {
|
||||
for src in $(find "${EXAMPLES}" -name '*.c'); do
|
||||
out=$(basename "$src" .c)
|
||||
out="${OUT}/bin/$out"
|
||||
|
||||
if [ $(mod_time "$src") -ge $(mod_time "$out") ]; then
|
||||
echo "CC $(basename $out)"
|
||||
mkdir -p "$(dirname $out)"
|
||||
if ! $CC $CFLAGS -o "$out" "$src" "${OUT}/lib/${LIB_NAME}.o" "-L${OUT}/lib" "-l${LIB_NAME}" ${LDFLAGS}; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
recipe_clean() {
|
||||
rm -r "${BUILD}" "${OUT}"
|
||||
}
|
||||
|
||||
# Update copyright comments in sources and header files.
|
||||
recipe_license() {
|
||||
find . -name '*.[ch]' | while IFS= read -r src; do
|
||||
if [ -t 1 ]; then
|
||||
printf "LICENSE %s%*c\r" $(basename "$src") "16" " "
|
||||
fi
|
||||
srcHeader=$(grep -n -m 1 '^ \*/' "$src" | cut -d ':' -f 1)
|
||||
head "-n$srcHeader" "$src" |
|
||||
diff -u -p - "${LICENSE}" |
|
||||
patch "$src" | grep -v "^Hmm"
|
||||
done
|
||||
if [ -t 1 ]; then
|
||||
printf "%*c\n" "50" " "
|
||||
fi
|
||||
}
|
||||
|
||||
# Format source code files by running indent(1) on them.
|
||||
recipe_format() {
|
||||
find . -name '*.c' | while IFS= read -r src; do
|
||||
if [ -t 1 ]; then
|
||||
printf "FMT %s%*c\r" $(basename "$src") "16" " "
|
||||
fi
|
||||
if indent "$src"; then
|
||||
rm $(basename "$src").BAK
|
||||
fi
|
||||
done
|
||||
if [ -t 1 ]; then
|
||||
printf "%*c\n" "50" " "
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate a release tarball, checksum and sign it, and push it to
|
||||
# the web root.
|
||||
recipe_release() {
|
||||
# Tag the release at this point in time.
|
||||
cvs tag "$CVS_TAG"
|
||||
|
||||
mkdir -p "${OUT}/release"
|
||||
cd "${OUT}/release"
|
||||
|
||||
# Generate the release tarball.
|
||||
cvs export "-r$CVS_TAG" "${NAME}"
|
||||
mv "${NAME}" "${NAME}-v${VERSION}"
|
||||
tar -czf "${NAME}-v${VERSION}.tar.gz" "${NAME}-v${VERSION}"
|
||||
rm -r "${NAME}-v${VERSION}"
|
||||
|
||||
# Checksum the release tarball.
|
||||
sha256 "${NAME}-v${VERSION}.tar.gz" > "${NAME}-v${VERSION}.tar.gz.sha256"
|
||||
|
||||
# Sign the release tarball.
|
||||
if [ ! -z "${SIGNIFY_SECRET}" ]; then
|
||||
signify -S -s "${SIGNIFY_SECRET}" \
|
||||
-m "${NAME}-v${VERSION}.tar.gz" \
|
||||
-x "${NAME}-v${VERSION}.tar.gz.sig"
|
||||
else
|
||||
echo "Warning: SIGNIFY_SECRET not net."
|
||||
echo "The built tarball will not be signed."
|
||||
fi
|
||||
}
|
||||
|
||||
recipe_patch() {
|
||||
# If the user has not set their MXID, try to deduce one from
|
||||
# their system.
|
||||
if [ -z "$MXID" ]; then
|
||||
MXID="@${USER}:$(hostname)"
|
||||
fi
|
||||
|
||||
# If the user has not set their EDITOR, use a safe default.
|
||||
# (vi should be available on any POSIX system)
|
||||
if [ -z "$EDITOR" ]; then
|
||||
EDITOR=vi
|
||||
fi
|
||||
|
||||
NORMALIZED_MXID=$(echo "$MXID" | sed -e 's/@//g' -e 's/:/-/g')
|
||||
PATCH_FILE="${NORMALIZED_MXID}_$(date +%s).patch"
|
||||
|
||||
# Generate the actual patch file
|
||||
# Here we write some nice headers, and then do a cvs diff.
|
||||
(
|
||||
printf 'From: "%s" <%s>\n' "$DISPLAY_NAME" "$MXID"
|
||||
echo "Date: $(date)"
|
||||
echo "Subject: "
|
||||
echo
|
||||
cvs -q diff -uNp $PATCHSET | grep -v '^\? '
|
||||
) >"$PATCH_FILE"
|
||||
|
||||
"$EDITOR" "$PATCH_FILE"
|
||||
echo "$PATCH_FILE"
|
||||
}
|
||||
|
||||
recipe_diff() {
|
||||
tmp_patch="/tmp/${NAME}-$(date +%s).patch"
|
||||
cvs -q diff -uNp $PATCHSET >"$tmp_patch"
|
||||
if [ -z "$PAGER" ]; then
|
||||
PAGER="less -F"
|
||||
fi
|
||||
|
||||
$PAGER "$tmp_patch"
|
||||
rm "$tmp_patch"
|
||||
}
|
||||
|
||||
# Execute the user-specified recipes.
|
||||
for recipe in $@; do
|
||||
recipe_$recipe
|
||||
done
|
||||
|
||||
# If no recipe was provided, run a build.
|
||||
if [ -z "$1" ]; then
|
||||
recipe_build
|
||||
fi
|
41
src/Array.c
41
src/Array.c
|
@ -317,12 +317,53 @@ ArrayUnique(Array * array, int (*compare) (void *, void *))
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Even though the following operations could be done using only the
|
||||
* public Array API defined above, I opted for low-level struct
|
||||
* manipulation because it allows much more efficient copying; we only
|
||||
* allocate what we for sure need upfront, and don't have to
|
||||
* re-allocate during the operation. */
|
||||
|
||||
Array *
|
||||
ArrayReverse(Array * array)
|
||||
{
|
||||
Array *ret;
|
||||
|
||||
size_t i;
|
||||
size_t size;
|
||||
|
||||
if (!array)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!array->size)
|
||||
{
|
||||
return ArrayCreate();
|
||||
}
|
||||
|
||||
ret = Malloc(sizeof(Array));
|
||||
|
||||
size = array->size;
|
||||
|
||||
ret->size = size;
|
||||
ret->allocated = size;
|
||||
ret->entries = Malloc(sizeof(void *) * size);
|
||||
|
||||
if (!ret->entries)
|
||||
{
|
||||
Free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
ret->entries[i] = array->entries[size - i - 1];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Array *
|
||||
ArrayFromVarArgs(size_t n, va_list ap)
|
||||
{
|
||||
|
|
|
@ -196,7 +196,7 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
char *pathPart;
|
||||
char *tmp;
|
||||
HttpRouteFunc *exec = NULL;
|
||||
Array *matches;
|
||||
Array *matches = NULL;
|
||||
size_t i;
|
||||
int retval;
|
||||
|
||||
|
@ -254,14 +254,19 @@ HttpRouterRoute(HttpRouter * router, char *path, void *args, void **ret)
|
|||
{
|
||||
/* pmatch[0] is the whole string, not the first
|
||||
* subexpression */
|
||||
char *substr;
|
||||
regmatch_t cpmatch;
|
||||
|
||||
for (i = 1; i < REG_MAX_SUB; i++)
|
||||
{
|
||||
cpmatch = pmatch[i];
|
||||
substr = StrSubstr(pathPart, cpmatch.rm_so, cpmatch.rm_eo);
|
||||
if (pmatch[i].rm_so == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ArrayAdd(matches, StrSubstr(pathPart, pmatch[i].rm_so, pmatch[i].rm_eo));
|
||||
ArrayAdd(matches, substr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,6 @@ StrSubstr(const char *inStr, size_t start, size_t end)
|
|||
}
|
||||
|
||||
len = end - start;
|
||||
|
||||
outStr = Malloc(len + 1);
|
||||
if (!outStr)
|
||||
{
|
||||
|
|
|
@ -151,6 +151,14 @@ extern void ArraySort(Array *, int (*) (void *, void *));
|
|||
*/
|
||||
extern Array *ArrayUnique(Array *, int (*) (void *, void *));
|
||||
|
||||
|
||||
/**
|
||||
* Reverses the order of all elements in the array into a new array on
|
||||
* the heap, to be freed using
|
||||
* .Fn ArrayFree .
|
||||
*/
|
||||
extern Array *ArrayReverse(Array *);
|
||||
|
||||
/**
|
||||
* If possible, reduce the amount of memory allocated to this array
|
||||
* by calling
|
||||
|
|
|
@ -527,7 +527,7 @@ end_loop:
|
|||
|
||||
size_t size;
|
||||
size_t len;
|
||||
char c;
|
||||
int c;
|
||||
|
||||
Free(fileName);
|
||||
if (!file)
|
||||
|
|
Loading…
Reference in a new issue