about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile20
-rw-r--r--README60
-rw-r--r--example.c44
-rw-r--r--libste.3115
-rw-r--r--ste.h4
-rw-r--r--stechr.c8
-rw-r--r--stecpe.c21
-rw-r--r--stecpy.c17
-rw-r--r--steprn.c23
-rw-r--r--tests.c125
10 files changed, 437 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0be968b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+all: libste.a example
+
+libste.a: stechr.o stecpe.o stecpy.o steprn.o
+	$(AR) $(ARFLAGS) $@ $^
+
+example: example.o libste.a
+
+tests: tests.o libste.a
+
+check: tests FRC
+	prove -v ./tests
+
+README: libste.3
+	mandoc -Tutf8 $^ | col -bx >$@
+
+
+clean: FRC
+	rm -f *.o *.a example tests
+
+FRC:
diff --git a/README b/README
new file mode 100644
index 0000000..26bf455
--- /dev/null
+++ b/README
@@ -0,0 +1,60 @@
+LIBSTE(3)                  Library Functions Manual                  LIBSTE(3)
+
+NAME
+     stecpy, stecpe, stechr, steprn – string library based on string ends
+
+SYNOPSIS
+     #include <ste.h>
+
+     char *
+     stecpy(char *dst, char *end, const char *src);
+
+     char *
+     stecpe(char *dst, char *dstend, const char *src, const char *srcend);
+
+     char *
+     stechr(const char *src, const char *end, int c);
+
+     char *
+     steprn(char *dst, char *end, const char *fmt, ...);
+
+DESCRIPTION
+     libste provides four useful functions for dealing with strings.
+
+     stecpy copies the NUL-terminated string src to dst, but writes no
+     characters beyond end.  If any characters are copied, dst will be NUL-
+     terminated and the return value is a pointer to the NUL byte.  On
+     truncation, end is returned.
+
+     stecpe copies the string between src and srcend to dst, but writes no
+     characters beyond dstend.  If any characters are copied, dst will be NUL-
+     terminated and the return value is a pointer to the NUL byte.  On
+     truncation, end is returned.
+
+     stechr returns a pointer to the first occurence of c (converted to a
+     char) in the NUL-terminated string pointed to by src, but reads no
+     characters beyond end.  If c is not found, stecpy returns a pointer to
+     the first NUL byte in src, or end if none was found.
+
+     steprn uses vsnprintf(3) to write formatted output to dst, but writes no
+     characters beyond end.  If any characters are written, dst will be NUL-
+     terminated and the return value is a pointer to the NUL byte.  On
+     truncation, end is returned.
+
+     Note that it is safe to pass the return value of all functions listed
+     above as argument for dst when the same end is reused.  In this case, the
+     function call does nothing but return dst again.  At any point,
+     truncation can be checked by comparing the return value to end.
+
+AUTHORS
+     Leah Neukirchen <leah@vuxu.org>
+
+LICENSE
+     libste is in the public domain.
+
+     To the extent possible under law, the creator of this work has waived all
+     copyright and related or neighboring rights to this work.
+
+     http://creativecommons.org/publicdomain/zero/1.0/
+
+Void Linux                     November 5, 2021                     Void Linux
diff --git a/example.c b/example.c
new file mode 100644
index 0000000..e9f57d5
--- /dev/null
+++ b/example.c
@@ -0,0 +1,44 @@
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ste.h"
+
+/* example of libste usage: iterate over $PATH and append argv[1],
+   print the entries that fit into PATH_MAX. */
+
+int
+main(int argc, char *argv[])
+{
+	char *path = getenv("PATH");
+	if (!path)
+	    path = "";
+
+	char *program = argc > 1 ? argv[1] : "xyzzy";
+
+	char *pathend = path + strlen(path);
+
+	char buf[PATH_MAX];
+	char *bufend = buf + sizeof buf;
+
+	while (1) {
+		char *pos = buf;
+
+		char *colon = stechr(path, pathend, ':');
+		if (path == colon)  /* empty entry */
+			pos = stecpy(buf, bufend, ".");
+		else
+			pos = stecpe(buf, bufend, path, colon);
+
+		pos = steprn(pos, bufend, "/%s", program);
+
+		if (pos < bufend) {  /* no trunaction */
+			printf("%s\n", buf);
+		}
+
+		if (colon == pathend)
+			break;
+		path = colon + 1;
+	}
+}
diff --git a/libste.3 b/libste.3
new file mode 100644
index 0000000..24b3d4a
--- /dev/null
+++ b/libste.3
@@ -0,0 +1,115 @@
+.Dd November 5, 2021
+.Dt LIBSTE 3
+.Os
+.Sh NAME
+.Nm stecpy ,
+.Nm stecpe ,
+.Nm stechr ,
+.Nm steprn
+.Nd string library based on string ends
+.Sh SYNOPSIS
+.In ste.h
+.Ft "char *"
+.Fn stecpy "char *dst" "char *end" "const char *src"
+.Ft "char *"
+.Fn stecpe "char *dst" "char *dstend" "const char *src" "const char *srcend"
+.Ft "char *"
+.Fn stechr "const char *src" "const char *end" "int c"
+.Ft "char *"
+.Fn steprn "char *dst" "char *end" "const char *fmt" "..."
+.Sh DESCRIPTION
+.Nm libste
+provides four useful functions for dealing with strings.
+.Pp
+.Nm stecpy
+copies the NUL-terminated string
+.Fa src
+to
+.Fa dst ,
+but writes no characters beyond
+.Fa end .
+If any characters are copied,
+.Fa dst
+will be NUL-terminated
+and the return value is a pointer to the NUL byte.
+On truncation,
+.Fa end
+is returned.
+.Pp
+.Nm stecpe
+copies the string between
+.Fa src
+and
+.Fa srcend
+to
+.Fa dst ,
+but writes no characters beyond
+.Fa dstend .
+If any characters are copied,
+.Fa dst
+will be NUL-terminated
+and the return value is a pointer to the NUL byte.
+On truncation,
+.Fa end
+is returned.
+.Pp
+.Nm stechr
+returns a pointer to the first occurence of
+.Fa c
+.Pq converted to a Vt char
+in the NUL-terminated string pointed to by
+.Fa src ,
+but reads no characters beyond
+.Fa end .
+If
+.Fa c
+is not found,
+.Nm
+returns a pointer to the first NUL byte in
+.Fa src ,
+or
+.Fa end
+if none was found.
+.Pp
+.Nm steprn
+uses
+.Xr vsnprintf 3
+to write formatted output to
+.Fa dst ,
+but writes no characters beyond
+.Fa end .
+If any characters are written,
+.Fa dst
+will be NUL-terminated
+and the return value is a pointer to the NUL byte.
+On truncation,
+.Fa end
+is returned.
+.Pp
+Note that it is safe to pass the return value of all functions listed above
+as argument for
+.Fa dst
+when the same
+.Fa end
+is reused.
+In this case, the function call does nothing but return
+.Fa dst
+again.
+At any point, truncation can be checked by comparing the return value to
+.Fa end .
+.\" .Sh RETURN VALUES
+.\" .Sh SEE ALSO
+.\" .Sh STANDARDS
+.\" .Sh HISTORY
+.Sh AUTHORS
+.An Leah Neukirchen Aq Mt leah@vuxu.org
+.Sh LICENSE
+.Nm libste
+is in the public domain.
+.Pp
+To the extent possible under law,
+the creator of this work
+has waived all copyright and related or
+neighboring rights to this work.
+.Pp
+.Lk http://creativecommons.org/publicdomain/zero/1.0/
diff --git a/ste.h b/ste.h
new file mode 100644
index 0000000..b2ff64e
--- /dev/null
+++ b/ste.h
@@ -0,0 +1,4 @@
+char *stecpy(char *dst, char *end, const char *src);
+char *stecpe(char *dst, char *end, const char *src, const char *srcend);
+char *stechr(const char *src, const char *end, int c);
+char *steprn(char *dst, char *end, const char *fmt, ...);
diff --git a/stechr.c b/stechr.c
new file mode 100644
index 0000000..d5bce2f
--- /dev/null
+++ b/stechr.c
@@ -0,0 +1,8 @@
+char *
+stechr(const char *src, const char *end, int c)
+{
+	while (src < end && *src && *(unsigned char *)src != (unsigned char)c)
+		src++;
+
+	return (char *)src;
+}
diff --git a/stecpe.c b/stecpe.c
new file mode 100644
index 0000000..0cfbaad
--- /dev/null
+++ b/stecpe.c
@@ -0,0 +1,21 @@
+#include <stddef.h>
+#include <string.h>
+
+char *
+stecpe(char *dst, const char *end, const char *src, const char *srcend)
+{
+        if (dst >= end)
+                return dst;
+
+	size_t l = end - dst - 1;
+	size_t t = 1;
+	if (srcend - src < l) {
+		l = srcend - src;
+		t = 0;
+	}
+
+	memcpy(dst, src, l);
+	dst[l] = 0;
+
+	return dst + l + t;
+}
diff --git a/stecpy.c b/stecpy.c
new file mode 100644
index 0000000..02b69c6
--- /dev/null
+++ b/stecpy.c
@@ -0,0 +1,17 @@
+#include <stddef.h>
+
+char *
+stecpy(char *dst, const char *end, const char *src)
+{
+        if (dst >= end)
+                return dst;
+
+	size_t n = end - dst;
+	while (n && (*dst = *src))
+		n--, src++, dst++;
+
+	if (dst == end)
+		dst[-1] = 0;
+
+	return dst;
+}
diff --git a/steprn.c b/steprn.c
new file mode 100644
index 0000000..2ca0a27
--- /dev/null
+++ b/steprn.c
@@ -0,0 +1,23 @@
+#include <stdarg.h>
+#include <stdio.h>
+
+char *
+steprn(char *dst, char *end, const char *fmt, ...)
+{
+	if (dst >= end)
+		return end;
+
+	va_list ap;
+	va_start(ap, fmt);
+	int r = vsnprintf(dst, end - dst, fmt, ap);
+	va_end(ap);
+
+	if (r < 0) {
+		/* snprintf only fails for silly reasons:
+		   truncate what was written, behave as noop.  */
+		*dst = 0;
+		return dst;
+	}
+
+	return r > end - dst ? end : dst + r;
+}
diff --git a/tests.c b/tests.c
new file mode 100644
index 0000000..9255a70
--- /dev/null
+++ b/tests.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "ste.h"
+
+static int status;
+
+void
+is(char *desc, int ok)
+{
+	if (!ok)
+		status = 1;
+	printf("%s - %s\n", ok ? "ok" : "not ok", desc);
+}
+
+int
+main()
+{
+	printf("1..39\n");
+
+	printf("# stecpy\n");
+
+	char buf[16];
+	char *end = buf + sizeof buf;
+	char buf2[32] = "stringxyzxyzxyzxyzxyz";
+	char *pos, *prevpos;
+
+	pos = buf;
+	pos = stecpy(pos, end, "abc");
+	is("1x3 = 3", strlen(buf) == 3);
+	pos = stecpy(pos, end, "def");
+	is("2x3 = 6", strlen(buf) == 6);
+	pos = stecpy(pos, end, "ghi");
+	is("3x3 = 9", strlen(buf) == 9);
+	pos = stecpy(pos, end, "jkl");
+	is("4x3 = 12", strlen(buf) == 12);
+	pos = stecpy(pos, end, "mno");
+	is("5x3 = 15", strlen(buf) == 15);
+	pos = stecpy(pos, end, "full");
+	is("buffer is full", strlen(buf) == 15);
+	is("return value is end", pos == end);
+	pos = stecpy(pos, end, "fuller");
+	is("buffer doesn't get fuller", strlen(buf) == 15);
+	is("return value is end", pos == end);
+
+	pos = buf;
+	pos = stecpy(pos, end, "abcdefghijklmnopq");
+	is("truncation", strlen(buf) == 15);
+	is("return value is end", pos == end);
+
+	pos = buf;
+	pos = stecpy(pos, end, "xyz");
+	pos = stecpy(prevpos=pos, end, "");
+	is("empty append works", strlen(buf) == 3);
+	is("return value is unchanged", pos == prevpos);
+
+
+	printf("# steprn\n");
+
+	pos = buf;
+	pos = steprn(pos, end, "%d", 123);
+	is("1x3 = 3", strlen(buf) == 3);
+	pos = steprn(pos, end, "%d", 456);
+	is("2x3 = 6", strlen(buf) == 6);
+	pos = steprn(pos, end, "%d", 789);
+	is("3x3 = 9", strlen(buf) == 9);
+	pos = steprn(pos, end, "%03d", 007);
+	is("4x3 = 12", strlen(buf) == 12);
+	pos = steprn(pos, end, "%d", -42);
+	is("5x3 = 15", strlen(buf) == 15);
+	pos = steprn(pos, end, "%d", 7890);
+	is("buffer is full", strlen(buf) == 15);
+	is("return value is end", pos == end);
+	pos = steprn(pos, end, "%d", 67890);
+	is("buffer doesn't get fuller", strlen(buf) == 15);
+	is("return value is end", pos == end);
+
+	pos = buf;
+	pos = steprn(pos, end, "%s", "abcdefghijklmnopq");
+	is("truncation", strlen(buf) == 15);
+	is("return value is end", pos == end);
+
+	pos = buf;
+	pos = steprn(pos, end, "%s%s", "x", "yz");
+	pos = steprn(prevpos=pos, end, "");
+	is("empty append works", strlen(buf) == 3);
+	is("return value is unchanged", pos == prevpos);
+
+
+	printf("# stecpe\n");
+
+	pos = buf;
+	pos = stecpe(pos, end, buf2, buf2 + 6);
+	is("1x6 = 6", strlen(buf) == 6);
+	pos = stecpe(pos, end, buf2, buf2 + 6);
+	is("2x6 = 12", strlen(buf) == 12);
+	pos = stecpe(pos, end, buf2, buf2 + 6);
+	is("buffer is full", strlen(buf) == 15);
+	is("return value is end", pos == end);
+
+	pos = buf;
+	pos = steprn(pos, end, buf2, buf2 + sizeof buf2);
+	is("truncation", strlen(buf) == 15);
+	is("return value is end", pos == end);
+
+	pos = buf;
+	pos = stecpe(pos, end, buf2, buf2 + 3);
+	pos = stecpe(prevpos=pos, end, buf2, buf2);
+	is("empty append works", strlen(buf) == 3);
+	is("return value is unchanged", pos == prevpos);
+
+
+	printf("# stechr\n");
+	char *x = stechr(buf2, buf2 + sizeof buf2, 'x');
+	is("x found", *x == 'x');
+	is("x is at buf2[6]", x == buf2 + 6);
+	char *w = stechr(buf2, buf2 + sizeof buf2, 'w');
+	is("w not found", *w == 0);
+	is("returned end of string instead", w == buf2 + strlen(buf2));
+	char *y = stechr(buf2, buf2 + 6, 'y');
+	is("y not found in first 6 chars", y == buf2 + 6);
+
+
+	return status;
+}