commit 275821f11c4e8bcd80d20683727c2138af96d850
parent f581da0f2f23fc1cd47308630ddacb2f64c66c12
Author: Friedel Schön <[email protected]>
Date: Mon, 6 May 2024 11:54:20 +0200
refactoring
Diffstat:
206 files changed, 11433 insertions(+), 1920 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -1,48 +1,46 @@
-{
- "AlignAfterOpenBracket": "Align",
- "AlignConsecutiveAssignments": true,
- "AlignConsecutiveDeclarations": true,
- "AlignConsecutiveMacros": true,
- "AlignEscapedNewlines": true,
- "AlignOperands": "AlignAfterOperator",
- "AllowShortBlocksOnASingleLine": true,
- "AllowShortIfStatementsOnASingleLine": true,
- "AllowShortLoopsOnASingleLine": true,
- "BreakBeforeBraces": "Attach",
- "BreakBeforeTernaryOperators": true,
- "BreakConstructorInitializers": "BeforeComma",
- "BreakInheritanceList": "BeforeComma",
- "BreakStringLiterals": false,
- "ColumnLimit": 0,
- "Cpp11BracedListStyle": false,
- "FixNamespaceComments": true,
- "IncludeBlocks": "Regroup",
- "IndentCaseLabels": true,
- "IndentPPDirectives": "AfterHash",
- "IndentWidth": 4,
- "MaxEmptyLinesToKeep": 2,
- "NamespaceIndentation": "All",
- "PointerAlignment": "Left",
- "SortIncludes": true,
- "SortUsingDeclarations": true,
- "SpaceAfterCStyleCast": true,
- "SpaceAfterLogicalNot": false,
- "SpaceAfterTemplateKeyword": false,
- "SpaceBeforeAssignmentOperators": true,
- "SpaceBeforeCpp11BracedList": false,
- "SpaceBeforeCtorInitializerColon": true,
- "SpaceBeforeInheritanceColon": true,
- "SpaceBeforeRangeBasedForLoopColon": true,
- "SpaceBeforeSquareBrackets": false,
- "SpaceInEmptyBlock": false,
- "SpaceInEmptyParentheses": false,
- "SpacesBeforeTrailingComments": 4,
- "SpacesInAngles": false,
- "SpacesInCStyleCastParentheses": false,
- "SpacesInConditionalStatement": false,
- "SpacesInContainerLiterals": false,
- "SpacesInParentheses": false,
- "SpacesInSquareBrackets": false,
- "TabWidth": 4,
- "UseTab": "ForIndentation"
-}
-\ No newline at end of file
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: true
+AlignConsecutiveMacros: true
+AlignEscapedNewlines: true
+AlignOperands: AlignAfterOperator
+AllowShortBlocksOnASingleLine: true
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+BreakStringLiterals: false
+ColumnLimit: 0
+Cpp11BracedListStyle: false
+FixNamespaceComments: true
+IncludeBlocks: Regroup
+IndentCaseLabels: true
+IndentPPDirectives: AfterHash
+IndentWidth: 4
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
+PointerAlignment: Left
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 4
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+TabWidth: 4
+UseTab: ForIndentation
diff --git a/Makefile b/Makefile
@@ -1,12 +1,14 @@
TOPDIR = .
-include $(TOPDIR)/config.mk
-SUBDIRS += src docs
+SUBDIRS += src docs lib
include $(TOPDIR)/mk/subdir.mk
install: install-static
+install-pages: install-assets
+
install-static:
$(SILENT)install -d $(EPREFIX)/fiss
$(SILENT)for file in resume start stop suspend; do \
@@ -14,6 +16,13 @@ install-static:
install -m 755 etc/$$file $(EPREFIX)/fiss; \
done
+install-assets:
+ $(SILENT)install -d $(PREFIX)/share/doc/fiss;
+ $(SILENT)for file in github-mark-white.svg github-mark.svg style.css; do \
+ echo "[INST] $(PREFIX)/share/doc/fiss/$$file"; \
+ install -m 755 assets/$$file $(PREFIX)/share/doc/fiss/; \
+ done
+
uninstall: uninstall-static
uninstall-static:
diff --git a/assets/template.html b/assets/template.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html lang=en>
+
+<head>
+ <title>Friedel's Initialization and Service Supervision</title>
+ <meta charset=utf-8 />
+ <meta name=viewport content='width=device-width,initial-scale=1' />
+ <link rel=stylesheet href=style.css />
+ <script type=text/javascript>
+ function toggle_dark() {
+ var githubImage = document.getElementById('github');
+ var toggleButton = document.getElementById('toggle_dark');
+ if (document.body.classList.toggle('dark')) {
+ // is dark
+ githubImage.src = 'github-mark-white.svg';
+ toggleButton.innerHTML = ' turn the lights on ';
+ } else {
+ // is light
+ githubImage.src = 'github-mark.svg';
+ toggleButton.innerHTML = ' turn the lights off ';
+ }
+ }
+ </script>
+</head>
+
+<body>
+ <div id=wrapper>%%%</div>
+</body>
+
+</html>
+\ No newline at end of file
diff --git a/assets/toggle-dark.js b/assets/toggle-dark.js
@@ -1,20 +0,0 @@
-function toggle_dark() {
- var githubImage = document.getElementById('github');
- var toggleButton = document.getElementById('toggle_dark');
- if (document.body.classList.toggle('dark')) {
- // is dark
- githubImage.src = 'assets/github-mark-white.svg';
- toggleButton.innerHTML = ' turn the lights on ';
- } else {
- // is light
- githubImage.src = 'assets/github-mark.svg';
- toggleButton.innerHTML = ' turn the lights off ';
- }
-}
-
-/*document.addEventListener('readystatechange', function (state) {
- if (document.readyState == 'complete')
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- toggle_dark();
- }
-});*/
diff --git a/config.mk b/config.mk
@@ -4,10 +4,16 @@ VERSION = v0.4.0
IN_REPLACE := 's/%VERSION%/$(VERSION)/g'
SILENT = @
-CC := gcc
-CFLAGS := -g -Wall -Wextra -Wpedantic -Wno-gnu-zero-variadic-macro-arguments -I$(TOPDIR) -DSV_VERSION=\"$(VERSION)\" -g -std=gnu99
-LDFLAGS := -fPIE
+CC = gcc
+LIBDIR = $(TOPDIR)/lib
+CPPFLAGS = -I$(TOPDIR) -I$(LIBDIR)/libbio -I$(LIBDIR)/libfmt -I$(LIBDIR)/libutf -I$(LIBDIR)/libutil -DSV_VERSION=\"$(VERSION)\"
+CFLAGS = -Wall -Wextra -Wpedantic -Wunused-result -O2 -Werror -g
+LDFLAGS = -fPIE
+ARFLAGS = -rcs
-SED := sed
-PYTHON := python3
-AWK := awk
+SED = sed
+PYTHON = python3
+AWK = awk
+
+PREFIX=/home/friedel/projects/fiss/test/
+EPREFIX=$(PREFIX)/etc
+\ No newline at end of file
diff --git a/contrib/command.txt b/contrib/command.txt
@@ -3,20 +3,21 @@ FISS COMMANDS
+ new in fiss, - same as in runit, ~ different behaviour
-- up (u): starts the services, pin as started
-- down (d): stops the service, pin as stopped
-- once (o): starts the service, pin as started once
-+ xup (U): stops the service, don't pin as stopped
-+ xdown (D): stops the service, don't pin as stopped
-- term (t): same as down
-- kill (k): sends kill, pin as stopped
-- pause (p): pauses the service
-- cont (c): resumes the service
-+ reset (r): resets the service (fail-count)
- alarm (a): sends alarm
+- cont (c): resumes the service
+- down (d): stops the service, pin as stopped
++ event (e): enables ./event
+~ exit (x): does nothing (actually exits the runsv instance)
- hup (h): sends hup
- int (i): sends interrupt
+- kill (k): sends kill, pin as stopped
+- once (o): starts the service, pin as started once
+- pause (p): pauses the service
- quit (q): sends quit
++ reset (r): resets the service (fail-count)
+- term (t): same as down
+- up (u): starts the services, pin as started
- usr1 (1): sends usr1
- usr2 (2): sends usr2
-~ exit (x): does nothing (actually exits the runsv instance)
-\ No newline at end of file
++ xdown (D): stops the service, don't pin as stopped
++ xup (U): stops the service, don't pin as stopped
+\ No newline at end of file
diff --git a/contrib/death.txt b/contrib/death.txt
@@ -0,0 +1,13 @@
+FISS DEATH FILE
+===============
+
++--+--+--+--+--+--+--+--+-----------------------+--+--+
+| +--+--+--+--+--+--+--+--+ |
+| STATUS CHANGE | --- |CL|SI| COD |
+| +--+--+--+--+--+--+--+--+ |
++--+--+--+--+--+--+--+--+-----------------------+--+--+
+
+STATUS CHANGE = unix seconds
+CL = by client
+SI = signaled
+COD = exit code or signal
+\ No newline at end of file
diff --git a/contrib/dependency.txt b/contrib/dependency.txt
@@ -0,0 +1,4 @@
+DEPENDENCIES
+============
+
+svdir power ups all the
+\ No newline at end of file
diff --git a/contrib/event.txt b/contrib/event.txt
@@ -0,0 +1,9 @@
+FISS EVENTS
+===========
+
+- cont (c): service continues
+- down (d): service went down
+- pause (p): service is paused
+- term (t): service died by exiting
+- up (u): service went up
+- crash (x): service died by signal
diff --git a/contrib/exit-codes.txt b/contrib/exit-codes.txt
@@ -0,0 +1,4 @@
+0 = succes
+100 = client-error (like invalid argument)
+101 = temporary problem
+127 = directory or file not found
+\ No newline at end of file
diff --git a/contrib/serialize.txt b/contrib/serialize.txt
@@ -1,42 +1,48 @@
-FISS CONTROL RESPONSE
-=====================
+FISS STATUS
+===========
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-FISS: | STATUS CHANGE |ST|RC|FC|FL| PID |PS|WU|--|SR|
+RUNIT: | STS | STN | PID |PS|WU|TR|SR|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-RUNIT: | STATUS CHANGE | NANOSEC | PID |PS|WU|TR|SR|
- +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-
-STATUS CHANGE = unix seconds + 4611686018427387914ULL (tai, lower endian)
-ST = state (see below)
-RC = last return code (0 if not exited yet)
-FC = fail count
-FL = flags (see below)
-PID = current pid (big endian)
-PS = is paused (int boolean)
-WU = wants up ('u' if want up, 'd' if want down)
-SR = state runit (0 is down, 1 is running, 2 is finishing, not available in daemontools, unused by fiss)
-
-NANOSEC = unix nanoseconds (written, never read)
-TR = was terminated (int boolean)
-
-FLAGS
------
-+--+--+--+--+--+--+--+--+
-| -- |TR|OC|RS| LEX |
-+--+--+--+--+--+--+--+--+
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+FISS: | STS |ST|RC|DC| | PID |PS|WU|TR|SR|
+ +--+--+--+--+--+--+--+--+--+--+--+||+--+--+--+--+--+--+--+--+
+ vv
+ +--+--+--+--+--+--+--+--+
+ | |DP|AC|TR|OC|RS| LEX |
+ +--+--+--+--+--+--+--+--+
+
+STS = status-change unix seconds + 4611686018427387914ULL (tai, lower endian)
+STN = status-change unix nanoseconds (written, never read)
+ST = state (see below)
+RC = last return code (0 if not exited yet)
+DC = death count
+DP = is dependency of a service
+AC = is active
TR = is terminating
OC = started once
RS = should restart
LEX = last exit (0 = never exitted, 1 = normally, 2 = signaled)
---- = nothing yet
-
-; as dependency : wants-up != (restart || once)
+PID = current pid (big endian)
+PS = is paused (int boolean)
+WU = wants up ('u' if want up, 'd' if want down)
+TR = was terminated (int boolean)
+SR = state runit (0 = down, 1 = running, 2 = finishing; not available in daemontools, not read by fiss)
STATE
-----
+ inactive
+ dependency
+ setup
+ starting
+ active_foreground
+ active_background
+ active_dummy
+ stopping
+ finishing
+
0 = inactive - is not enabled and does nothing
1 = setup - ./setup is running
2 = starting - ./start is running
diff --git a/contrib/supervise.txt b/contrib/supervise.txt
@@ -0,0 +1,18 @@
+SUPERVISE FILES
+===============
+
+This document describes files stored in <service>/supervise.
+<service>/supervise actually lives in /run/fiss/supervise/<service> and
+is a symbolic link to it. These files are essential to fiss, if the service-dir would be deleted,
+the supervise directory is still alive and can be closed normally.
+
+ + new in fiss, - same as in runit, ~ different behaviour
+
+- control FIFO to control the service (described in ./command.txt)
++ death data-file containing recent deaths of ./run (described in ./death.txt)
++ event FIFO can be subscribed (by ./supervise-dir) to get events (must be enabled)
+- lock file which is locked if superviser is active
+- ok FIFO which is just openened (as read by superviser) to check whether superviser is doing good
+- pid text-file containing the pid of ./run
+- stat text-file containing current status
+- status data-file containing status of status
diff --git a/lib/Makefile b/lib/Makefile
@@ -0,0 +1,6 @@
+TOPDIR = ..
+-include $(TOPDIR)/config.mk
+
+SUBDIRS += libbio libfmt libutf libutil
+
+include $(TOPDIR)/mk/subdir.mk
diff --git a/lib/libbio/Makefile b/lib/libbio/Makefile
@@ -0,0 +1,25 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = bbuffered.o \
+ bfildes.o \
+ bflush.o \
+ bgetc.o \
+ bgetd.o \
+ bgetrune.o \
+ binit.o \
+ boffset.o \
+ bprint.o \
+ bputc.o \
+ bputrune.o \
+ brdline.o \
+ brdstr.o \
+ bread.o \
+ bseek.o \
+ bvprint.o \
+ bwrite.o
+
+HEADERS = bio.h
+LIBRARY = libbio.a
+
+include $(TOPDIR)/mk/lib.mk
+\ No newline at end of file
diff --git a/lib/libbio/NOTICE b/lib/libbio/NOTICE
@@ -0,0 +1,23 @@
+This is a Unix port of the Plan 9 buffered I/O library.
+Please send comments about the packaging to Russ Cox <[email protected]>.
+
+Copyright © 2021 Plan 9 FoundationR
+Revisions Copyright © 2000-2005 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
+
+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 substantial 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.
diff --git a/lib/libbio/README b/lib/libbio/README
@@ -0,0 +1,5 @@
+This software was packaged for Unix by Russ Cox.
+Please send comments to [email protected].
+
+https://9fans.github.io/plan9port/unix
+
diff --git a/lib/libbio/bbuffered.c b/lib/libbio/bbuffered.c
@@ -0,0 +1,19 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+int Bbuffered(Biobuf* bp) {
+ switch (bp->state) {
+ case Bracteof:
+ case Bractive:
+ return -bp->icount;
+
+ case Bwactive:
+ return bp->bsize + bp->ocount;
+
+ case Binactive:
+ return 0;
+ }
+ fprint(2, "Bbuffered: unknown state %d\n", bp->state);
+ return 0;
+}
diff --git a/lib/libbio/bcat.c b/lib/libbio/bcat.c
@@ -0,0 +1,43 @@
+#include "bio.h"
+
+#include <fmt.h>
+
+Biobuf bout;
+
+void bcat(Biobuf* b, char* name) {
+ char buf[1000];
+ int n;
+
+ while ((n = Bread(b, buf, sizeof buf)) > 0) {
+ if (Bwrite(&bout, buf, n) < 0)
+ fprint(2, "writing during %s: %r\n", name);
+ }
+ if (n < 0)
+ fprint(2, "reading %s: %r\n", name);
+}
+
+int main(int argc, char** argv) {
+ int i;
+ Biobuf b, *bp;
+ Fmt fmt;
+
+ Binit(&bout, 1, O_WRONLY);
+ Bfmtinit(&fmt, &bout);
+ fmtprint(&fmt, "hello, world\n");
+ Bfmtflush(&fmt);
+
+ if (argc == 1) {
+ Binit(&b, 0, O_RDONLY);
+ bcat(&b, "<stdin>");
+ } else {
+ for (i = 1; i < argc; i++) {
+ if ((bp = Bopen(argv[i], O_RDONLY)) == 0) {
+ fprint(2, "Bopen %s: %r\n", argv[i]);
+ continue;
+ }
+ bcat(bp, argv[i]);
+ Bterm(bp);
+ }
+ }
+ exit(0);
+}
diff --git a/lib/libbio/bfildes.c b/lib/libbio/bfildes.c
@@ -0,0 +1,8 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+int Bfildes(Biobuf* bp) {
+
+ return bp->fid;
+}
diff --git a/lib/libbio/bflush.c b/lib/libbio/bflush.c
@@ -0,0 +1,34 @@
+#include "common.h"
+#include "lib9.h"
+
+#include <bio.h>
+
+int Bflush(Biobuf* bp) {
+ int n, c;
+
+ switch (bp->state) {
+ case Bwactive:
+ n = bp->bsize + bp->ocount;
+ if (n == 0)
+ return 0;
+ c = write(bp->fid, bp->bbuf, n);
+ if (n == c) {
+ bp->offset += n;
+ bp->ocount = -bp->bsize;
+ return 0;
+ }
+ bp->state = Binactive;
+ bp->ocount = 0;
+ break;
+
+ case Bracteof:
+ bp->state = Bractive;
+ FALLTHROUGH;
+
+ case Bractive:
+ bp->icount = 0;
+ bp->gbuf = bp->ebuf;
+ return 0;
+ }
+ return Beof;
+}
diff --git a/lib/libbio/bgetc.c b/lib/libbio/bgetc.c
@@ -0,0 +1,50 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+int Bgetc(Biobuf* bp) {
+ int i;
+
+loop:
+ i = bp->icount;
+ if (i != 0) {
+ bp->icount = i + 1;
+ return bp->ebuf[i];
+ }
+ if (bp->state != Bractive) {
+ if (bp->state == Bracteof)
+ bp->state = Bractive;
+ return Beof;
+ }
+ /*
+ * get next buffer, try to keep Bungetsize
+ * characters pre-catenated from the previous
+ * buffer to allow that many ungets.
+ */
+ memmove(bp->bbuf - Bungetsize, bp->ebuf - Bungetsize, Bungetsize);
+ i = read(bp->fid, bp->bbuf, bp->bsize);
+ bp->gbuf = bp->bbuf;
+ if (i <= 0) {
+ bp->state = Bracteof;
+ if (i < 0)
+ bp->state = Binactive;
+ return Beof;
+ }
+ if (i < bp->bsize) {
+ memmove(bp->ebuf - i - Bungetsize, bp->bbuf - Bungetsize, i + Bungetsize);
+ bp->gbuf = bp->ebuf - i;
+ }
+ bp->icount = -i;
+ bp->offset += i;
+ goto loop;
+}
+
+int Bungetc(Biobuf* bp) {
+
+ if (bp->state == Bracteof)
+ bp->state = Bractive;
+ if (bp->state != Bractive)
+ return Beof;
+ bp->icount--;
+ return 1;
+}
diff --git a/lib/libbio/bgetd.c b/lib/libbio/bgetd.c
@@ -0,0 +1,33 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+struct bgetd {
+ Biobuf* b;
+ int eof;
+};
+
+static int
+Bgetdf(void* vp) {
+ int c;
+ struct bgetd* bg = vp;
+
+ c = Bgetc(bg->b);
+ if (c == Beof)
+ bg->eof = 1;
+ return c;
+}
+
+int Bgetd(Biobuf* bp, double* dp) {
+ double d;
+ struct bgetd b;
+
+ b.b = bp;
+ b.eof = 0;
+ d = fmtcharstod(Bgetdf, &b);
+ if (b.eof)
+ return -1;
+ Bungetc(bp);
+ *dp = d;
+ return 1;
+}
diff --git a/lib/libbio/bgetrune.c b/lib/libbio/bgetrune.c
@@ -0,0 +1,44 @@
+#include "lib9.h"
+
+#include <bio.h>
+#include <utf.h>
+
+long Bgetrune(Biobuf* bp) {
+ int c, i;
+ Rune rune;
+ char str[UTFmax];
+
+ c = Bgetc(bp);
+ if (c < Runeself) { /* one char */
+ bp->runesize = 1;
+ return c;
+ }
+ str[0] = c;
+
+ for (i = 1;;) {
+ c = Bgetc(bp);
+ if (c < 0)
+ return c;
+ str[i++] = c;
+
+ if (fullrune(str, i)) {
+ bp->runesize = chartorune(&rune, str);
+ while (i > bp->runesize) {
+ Bungetc(bp);
+ i--;
+ }
+ return rune;
+ }
+ }
+}
+
+int Bungetrune(Biobuf* bp) {
+
+ if (bp->state == Bracteof)
+ bp->state = Bractive;
+ if (bp->state != Bractive)
+ return Beof;
+ bp->icount -= bp->runesize;
+ bp->runesize = 0;
+ return 1;
+}
diff --git a/lib/libbio/binit.c b/lib/libbio/binit.c
@@ -0,0 +1,141 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+enum {
+ MAXBUFS = 20
+};
+
+static Biobuf* wbufs[MAXBUFS];
+static int atexitflag;
+
+static void
+batexit(void) {
+ Biobuf* bp;
+ int i;
+
+ for (i = 0; i < MAXBUFS; i++) {
+ bp = wbufs[i];
+ if (bp != 0) {
+ wbufs[i] = 0;
+ Bflush(bp);
+ }
+ }
+}
+
+static void
+deinstall(Biobuf* bp) {
+ int i;
+
+ for (i = 0; i < MAXBUFS; i++)
+ if (wbufs[i] == bp)
+ wbufs[i] = 0;
+}
+
+static void
+install(Biobuf* bp) {
+ int i;
+
+ deinstall(bp);
+ for (i = 0; i < MAXBUFS; i++)
+ if (wbufs[i] == 0) {
+ wbufs[i] = bp;
+ break;
+ }
+ if (atexitflag == 0) {
+ atexitflag = 1;
+ atexit(batexit);
+ }
+}
+
+int Binits(Biobuf* bp, int f, int mode, unsigned char* p, int size) {
+
+ p += Bungetsize; /* make room for Bungets */
+ size -= Bungetsize;
+
+ switch (mode & ~(OCEXEC | ORCLOSE | OTRUNC)) {
+ default:
+ fprint(2, "Bopen: unknown mode %d\n", mode);
+ return Beof;
+
+ case OREAD:
+ bp->state = Bractive;
+ bp->ocount = 0;
+ break;
+
+ case OWRITE:
+ install(bp);
+ bp->state = Bwactive;
+ bp->ocount = -size;
+ break;
+ }
+ bp->bbuf = p;
+ bp->ebuf = p + size;
+ bp->bsize = size;
+ bp->icount = 0;
+ bp->gbuf = bp->ebuf;
+ bp->fid = f;
+ bp->flag = 0;
+ bp->rdline = 0;
+ bp->offset = 0;
+ bp->runesize = 0;
+ return 0;
+}
+
+
+int Binit(Biobuf* bp, int f, int mode) {
+ return Binits(bp, f, mode, bp->b, sizeof(bp->b));
+}
+
+Biobuf*
+Bfdopen(int f, int mode) {
+ Biobuf* bp;
+
+ bp = malloc(sizeof(Biobuf));
+ if (bp == 0)
+ return 0;
+ Binits(bp, f, mode, bp->b, sizeof(bp->b));
+ bp->flag = Bmagic;
+ return bp;
+}
+
+Biobuf*
+Bopen(char* name, int mode) {
+ Biobuf* bp;
+ int f;
+
+ switch (mode & ~(OCEXEC | ORCLOSE | OTRUNC)) {
+ default:
+ fprint(2, "Bopen: unknown mode %d\n", mode);
+ return 0;
+
+ case OREAD:
+ f = open(name, mode);
+ if (f < 0)
+ return 0;
+ break;
+
+ case OWRITE:
+ f = create(name, mode, 0666);
+ if (f < 0)
+ return 0;
+ }
+ bp = Bfdopen(f, mode);
+ if (bp == 0)
+ close(f);
+ return bp;
+}
+
+int Bterm(Biobuf* bp) {
+ int ret;
+
+ deinstall(bp);
+ ret = Bflush(bp);
+ if (bp->flag == Bmagic) {
+ bp->flag = 0;
+ if (close(bp->fid) < 0)
+ ret = -1;
+ free(bp);
+ }
+ return ret;
+}
diff --git a/lib/libbio/bio.3 b/lib/libbio/bio.3
@@ -0,0 +1,371 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.TH BIO 3
+.SH NAME
+Bopen, Bfdopen, Binit, Binits, Brdline, Brdstr, Bgetc, Bgetrune, Bgetd, Bungetc, Bungetrune, Bread, Bseek, Boffset, Bfildes, Blinelen, Bputc, Bputrune, Bprint, Bvprint, Bwrite, Bflush, Bterm, Bbuffered \- buffered input/output
+.SH SYNOPSIS
+.ta \w'\fLBiobuf* 'u
+.B #include <utf.h>
+.br
+.B #include <fmt.h>
+.br
+.B #include <bio.h>
+.PP
+.B
+Biobuf* Bopen(char *file, int mode)
+.PP
+.B
+Biobuf* Bfdopen(int fd, int mode)
+.PP
+.B
+int Binit(Biobuf *bp, int fd, int mode)
+.PP
+.B
+int Binits(Biobufhdr *bp, int fd, int mode, uchar *buf, int size)
+.PP
+.B
+int Bterm(Biobufhdr *bp)
+.PP
+.B
+int Bprint(Biobufhdr *bp, char *format, ...)
+.PP
+.B
+int Bvprint(Biobufhdr *bp, char *format, va_list arglist);
+.PP
+.B
+void* Brdline(Biobufhdr *bp, int delim)
+.PP
+.B
+char* Brdstr(Biobufhdr *bp, int delim, int nulldelim)
+.PP
+.B
+int Blinelen(Biobufhdr *bp)
+.PP
+.B
+vlong Boffset(Biobufhdr *bp)
+.PP
+.B
+int Bfildes(Biobufhdr *bp)
+.PP
+.B
+int Bgetc(Biobufhdr *bp)
+.PP
+.B
+long Bgetrune(Biobufhdr *bp)
+.PP
+.B
+int Bgetd(Biobufhdr *bp, double *d)
+.PP
+.B
+int Bungetc(Biobufhdr *bp)
+.PP
+.B
+int Bungetrune(Biobufhdr *bp)
+.PP
+.B
+vlong Bseek(Biobufhdr *bp, vlong n, int type)
+.PP
+.B
+int Bputc(Biobufhdr *bp, int c)
+.PP
+.B
+int Bputrune(Biobufhdr *bp, long c)
+.PP
+.B
+long Bread(Biobufhdr *bp, void *addr, long nbytes)
+.PP
+.B
+long Bwrite(Biobufhdr *bp, void *addr, long nbytes)
+.PP
+.B
+int Bflush(Biobufhdr *bp)
+.PP
+.B
+int Bbuffered(Biobufhdr *bp)
+.PP
+.SH DESCRIPTION
+These routines implement fast buffered I/O.
+I/O on different file descriptors is independent.
+.PP
+.I Bopen
+opens
+.I file
+for mode
+.B O_RDONLY
+or creates for mode
+.BR O_WRONLY .
+It calls
+.IR malloc (3)
+to allocate a buffer.
+.PP
+.I Bfdopen
+allocates a buffer for the already-open file descriptor
+.I fd
+for mode
+.B O_RDONLY
+or
+.BR O_WRONLY .
+It calls
+.IR malloc (3)
+to allocate a buffer.
+.PP
+.I Binit
+initializes a standard size buffer, type
+.IR Biobuf ,
+with the open file descriptor passed in
+by the user.
+.I Binits
+initializes a non-standard size buffer, type
+.IR Biobufhdr ,
+with the open file descriptor,
+buffer area, and buffer size passed in
+by the user.
+.I Biobuf
+and
+.I Biobufhdr
+are related by the declaration:
+.IP
+.EX
+typedef struct Biobuf Biobuf;
+struct Biobuf
+{
+ Biobufhdr;
+ uchar b[Bungetsize+Bsize];
+};
+.EE
+.PP
+Arguments
+of types pointer to Biobuf and pointer to Biobufhdr
+can be used interchangeably in the following routines.
+.PP
+.IR Bopen ,
+.IR Binit ,
+or
+.I Binits
+should be called before any of the
+other routines on that buffer.
+.I Bfildes
+returns the integer file descriptor of the associated open file.
+.PP
+.I Bterm
+flushes the buffer for
+.IR bp .
+If the buffer was allocated by
+.IR Bopen ,
+the buffer is
+.I freed
+and the file is closed.
+.PP
+.I Brdline
+reads a string from the file associated with
+.I bp
+up to and including the first
+.I delim
+character.
+The delimiter character at the end of the line is
+not altered.
+.I Brdline
+returns a pointer to the start of the line or
+.L 0
+on end-of-file or read error.
+.I Blinelen
+returns the length (including the delimiter)
+of the most recent string returned by
+.IR Brdline .
+.PP
+.I Brdstr
+returns a
+.IR malloc (3)-allocated
+buffer containing the next line of input delimited by
+.IR delim ,
+terminated by a NUL (0) byte.
+Unlike
+.IR Brdline ,
+which returns when its buffer is full even if no delimiter has been found,
+.I Brdstr
+will return an arbitrarily long line in a single call.
+If
+.I nulldelim
+is set, the terminal delimiter will be overwritten with a NUL.
+After a successful call to
+.IR Brdstr ,
+the return value of
+.I Blinelen
+will be the length of the returned buffer, excluding the NUL.
+.PP
+.I Bgetc
+returns the next character from
+.IR bp ,
+or a negative value
+at end of file.
+.I Bungetc
+may be called immediately after
+.I Bgetc
+to allow the same character to be reread.
+.PP
+.I Bgetrune
+calls
+.I Bgetc
+to read the bytes of the next
+.SM UTF
+sequence in the input stream and returns the value of the rune
+represented by the sequence.
+It returns a negative value
+at end of file.
+.I Bungetrune
+may be called immediately after
+.I Bgetrune
+to allow the same
+.SM UTF
+sequence to be reread as either bytes or a rune.
+.I Bungetc
+and
+.I Bungetrune
+may back up a maximum of five bytes.
+.PP
+.I Bgetd
+uses
+.I fmtcharstod
+(see
+.IR fmtstrtod (3))
+and
+.I Bgetc
+to read the formatted
+floating-point number in the input stream,
+skipping initial blanks and tabs.
+The value is stored in
+.BR *d.
+.PP
+.I Bread
+reads
+.I nbytes
+of data from
+.I bp
+into memory starting at
+.IR addr .
+The number of bytes read is returned on success
+and a negative value is returned if a read error occurred.
+.PP
+.I Bseek
+applies
+.IR lseek (2)
+to
+.IR bp .
+It returns the new file offset.
+.I Boffset
+returns the file offset of the next character to be processed.
+.PP
+.I Bputc
+outputs the low order 8 bits of
+.I c
+on
+.IR bp .
+If this causes a
+.IR write
+to occur and there is an error,
+a negative value is returned.
+Otherwise, a zero is returned.
+.PP
+.I Bputrune
+calls
+.I Bputc
+to output the low order
+16 bits of
+.I c
+as a rune
+in
+.SM UTF
+format
+on the output stream.
+.PP
+.I Bprint
+is a buffered interface to
+.IR print (3).
+If this causes a
+.IR write
+to occur and there is an error,
+a negative value
+.RB ( Beof )
+is returned.
+Otherwise, the number of bytes output is returned.
+.I Bvprint
+does the same except it takes as argument a
+.B va_list
+parameter, so it can be called within a variadic function.
+.PP
+.I Bwrite
+outputs
+.I nbytes
+of data starting at
+.I addr
+to
+.IR bp .
+If this causes a
+.IR write
+to occur and there is an error,
+a negative value is returned.
+Otherwise, the number of bytes written is returned.
+.PP
+.I Bflush
+causes any buffered output associated with
+.I bp
+to be written.
+The return is as for
+.IR Bputc .
+.I Bflush
+is called on
+exit for every buffer still open
+for writing.
+.PP
+.I Bbuffered
+returns the number of bytes in the buffer.
+When reading, this is the number of bytes still available from the last
+read on the file; when writing, it is the number of bytes ready to be
+written.
+.SH SOURCE
+.B https://9fans.github.io/plan9port/unix
+.SH SEE ALSO
+.IR open (2),
+.IR print (3),
+.IR atexit (3),
+.IR utf (7),
+.SH DIAGNOSTICS
+.I Bio
+routines that return integers yield
+.B Beof
+if
+.I bp
+is not the descriptor of an open file.
+.I Bopen
+returns zero if the file cannot be opened in the given mode.
+All routines set
+.I errstr
+on error.
+.SH BUGS
+.I Brdline
+returns an error on strings longer than the buffer associated
+with the file
+and also if the end-of-file is encountered
+before a delimiter.
+.I Blinelen
+will tell how many characters are available
+in these cases.
+In the case of a true end-of-file,
+.I Blinelen
+will return zero.
+At the cost of allocating a buffer,
+.I Brdstr
+sidesteps these issues.
+.PP
+The data returned by
+.I Brdline
+may be overwritten by calls to any other
+.I bio
+routine on the same
+.IR bp.
diff --git a/lib/libbio/bio.h b/lib/libbio/bio.h
@@ -0,0 +1,85 @@
+#ifndef _BIO_H_
+#define _BIO_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifdef AUTOLIB
+AUTOLIB(bio)
+#endif
+
+#include <fcntl.h> /* for O_RDONLY, O_WRONLY */
+
+typedef struct Biobuf Biobuf;
+
+enum {
+ Bsize = 8 * 1024,
+ Bungetsize = 4, /* space for ungetc */
+ Bmagic = 0x314159,
+ Beof = -1,
+ Bbad = -2,
+
+ Binactive = 0, /* states */
+ Bractive,
+ Bwactive,
+ Bracteof,
+
+ Bend
+};
+
+struct Biobuf {
+ int icount; /* neg num of bytes at eob */
+ int ocount; /* num of bytes at bob */
+ int rdline; /* num of bytes after rdline */
+ int runesize; /* num of bytes of last getrune */
+ int state; /* r/w/inactive */
+ int fid; /* open file */
+ int flag; /* magic if malloc'ed */
+ long long offset; /* offset of buffer in file */
+ int bsize; /* size of buffer */
+ unsigned char* bbuf; /* pointer to beginning of buffer */
+ unsigned char* ebuf; /* pointer to end of buffer */
+ unsigned char* gbuf; /* pointer to good data in buf */
+ unsigned char b[Bungetsize + Bsize];
+};
+
+#define BGETC(bp) \
+ ((bp)->icount ? (bp)->bbuf[(bp)->bsize + (bp)->icount++] : Bgetc((bp)))
+#define BPUTC(bp, c) \
+ ((bp)->ocount ? (bp)->bbuf[(bp)->bsize + (bp)->ocount++] = (c), 0 : Bputc((bp), (c)))
+#define BOFFSET(bp) \
+ (((bp)->state == Bractive) ? (bp)->offset + (bp)->icount : (((bp)->state == Bwactive) ? (bp)->offset + ((bp)->bsize + (bp)->ocount) : -1))
+#define BLINELEN(bp) \
+ (bp)->rdline
+#define BFILDES(bp) \
+ (bp)->fid
+
+int Bbuffered(Biobuf*);
+Biobuf* Bfdopen(int, int);
+int Bfildes(Biobuf*);
+int Bflush(Biobuf*);
+int Bgetc(Biobuf*);
+int Bgetd(Biobuf*, double*);
+long Bgetrune(Biobuf*);
+int Binit(Biobuf*, int, int);
+int Binits(Biobuf*, int, int, unsigned char*, int);
+int Blinelen(Biobuf*);
+long long Boffset(Biobuf*);
+Biobuf* Bopen(char*, int);
+int Bprint(Biobuf*, char*, ...);
+int Bputc(Biobuf*, int);
+int Bputrune(Biobuf*, long);
+void* Brdline(Biobuf*, int);
+char* Brdstr(Biobuf*, int, int);
+long Bread(Biobuf*, void*, long);
+long long Bseek(Biobuf*, long long, int);
+int Bterm(Biobuf*);
+int Bungetc(Biobuf*);
+int Bungetrune(Biobuf*);
+long Bwrite(Biobuf*, void*, long);
+int Bvprint(Biobuf*, char*, ...);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib/libbio/boffset.c b/lib/libbio/boffset.c
@@ -0,0 +1,24 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+vlong Boffset(Biobuf* bp) {
+ vlong n;
+
+ switch (bp->state) {
+ default:
+ fprint(2, "Boffset: unknown state %d\n", bp->state);
+ n = Beof;
+ break;
+
+ case Bracteof:
+ case Bractive:
+ n = bp->offset + bp->icount;
+ break;
+
+ case Bwactive:
+ n = bp->offset + (bp->bsize + bp->ocount);
+ break;
+ }
+ return n;
+}
diff --git a/lib/libbio/bprint.c b/lib/libbio/bprint.c
@@ -0,0 +1,13 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+int Bprint(Biobuf* bp, char* fmt, ...) {
+ int n;
+ va_list arg;
+
+ va_start(arg, fmt);
+ n = Bvprint(bp, fmt, arg);
+ va_end(arg);
+ return n;
+}
diff --git a/lib/libbio/bputc.c b/lib/libbio/bputc.c
@@ -0,0 +1,19 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+int Bputc(Biobuf* bp, int c) {
+ int i;
+
+ for (;;) {
+ i = bp->ocount;
+ if (i) {
+ bp->ebuf[i++] = c;
+ bp->ocount = i;
+ return 0;
+ }
+ if (Bflush(bp) == Beof)
+ break;
+ }
+ return Beof;
+}
diff --git a/lib/libbio/bputrune.c b/lib/libbio/bputrune.c
@@ -0,0 +1,22 @@
+#include "lib9.h"
+
+#include <bio.h>
+#include <utf.h>
+
+int Bputrune(Biobuf* bp, long c) {
+ Rune rune;
+ char str[UTFmax];
+ int n;
+
+ rune = c;
+ if (rune < Runeself) {
+ Bputc(bp, rune);
+ return 1;
+ }
+ n = runetochar(str, &rune);
+ if (n == 0)
+ return Bbad;
+ if (Bwrite(bp, str, n) != n)
+ return Beof;
+ return n;
+}
diff --git a/lib/libbio/brdline.c b/lib/libbio/brdline.c
@@ -0,0 +1,91 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+void* Brdline(Biobuf* bp, int delim) {
+ char *ip, *ep;
+ int i, j;
+
+ i = -bp->icount;
+ if (i == 0) {
+ /*
+ * eof or other error
+ */
+ if (bp->state != Bractive) {
+ if (bp->state == Bracteof)
+ bp->state = Bractive;
+ bp->rdline = 0;
+ bp->gbuf = bp->ebuf;
+ return 0;
+ }
+ }
+
+ /*
+ * first try in remainder of buffer (gbuf doesn't change)
+ */
+ ip = (char*) bp->ebuf - i;
+ ep = memchr(ip, delim, i);
+ if (ep) {
+ j = (ep - ip) + 1;
+ bp->rdline = j;
+ bp->icount += j;
+ return ip;
+ }
+
+ /*
+ * copy data to beginning of buffer
+ */
+ if (i < bp->bsize)
+ memmove(bp->bbuf, ip, i);
+ bp->gbuf = bp->bbuf;
+
+ /*
+ * append to buffer looking for the delim
+ */
+ ip = (char*) bp->bbuf + i;
+ while (i < bp->bsize) {
+ j = read(bp->fid, ip, bp->bsize - i);
+ if (j <= 0) {
+ /*
+ * end of file with no delim
+ */
+ memmove(bp->ebuf - i, bp->bbuf, i);
+ bp->rdline = i;
+ bp->icount = -i;
+ bp->gbuf = bp->ebuf - i;
+ return 0;
+ }
+ bp->offset += j;
+ i += j;
+ ep = memchr(ip, delim, j);
+ if (ep) {
+ /*
+ * found in new piece
+ * copy back up and reset everything
+ */
+ ip = (char*) bp->ebuf - i;
+ if (i < bp->bsize) {
+ memmove(ip, bp->bbuf, i);
+ bp->gbuf = (unsigned char*) ip;
+ }
+ j = (ep - (char*) bp->bbuf) + 1;
+ bp->rdline = j;
+ bp->icount = j - i;
+ return ip;
+ }
+ ip += j;
+ }
+
+ /*
+ * full buffer without finding
+ */
+ bp->rdline = bp->bsize;
+ bp->icount = -bp->bsize;
+ bp->gbuf = bp->bbuf;
+ return 0;
+}
+
+int Blinelen(Biobuf* bp) {
+
+ return bp->rdline;
+}
diff --git a/lib/libbio/brdstr.c b/lib/libbio/brdstr.c
@@ -0,0 +1,109 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+static char*
+badd(char* p, int* np, char* data, int ndata, int delim, int nulldelim) {
+ int n;
+
+ n = *np;
+ p = realloc(p, n + ndata + 1);
+ if (p) {
+ memmove(p + n, data, ndata);
+ n += ndata;
+ if (n > 0 && nulldelim && p[n - 1] == delim)
+ p[--n] = '\0';
+ else
+ p[n] = '\0';
+ *np = n;
+ }
+ return p;
+}
+
+char* Brdstr(Biobuf* bp, int delim, int nulldelim) {
+ char *ip, *ep, *p;
+ int i, j;
+
+ i = -bp->icount;
+ bp->rdline = 0;
+ if (i == 0) {
+ /*
+ * eof or other error
+ */
+ if (bp->state != Bractive) {
+ if (bp->state == Bracteof)
+ bp->state = Bractive;
+ bp->gbuf = bp->ebuf;
+ return nil;
+ }
+ }
+
+ /*
+ * first try in remainder of buffer (gbuf doesn't change)
+ */
+ ip = (char*) bp->ebuf - i;
+ ep = memchr(ip, delim, i);
+ if (ep) {
+ j = (ep - ip) + 1;
+ bp->icount += j;
+ return badd(nil, &bp->rdline, ip, j, delim, nulldelim);
+ }
+
+ /*
+ * copy data to beginning of buffer
+ */
+ if (i < bp->bsize)
+ memmove(bp->bbuf, ip, i);
+ bp->gbuf = bp->bbuf;
+
+ /*
+ * append to buffer looking for the delim
+ */
+ p = nil;
+ for (;;) {
+ ip = (char*) bp->bbuf + i;
+ while (i < bp->bsize) {
+ j = read(bp->fid, ip, bp->bsize - i);
+ if (j <= 0 && i == 0)
+ return p;
+ if (j <= 0 && i > 0) {
+ /*
+ * end of file but no delim. pretend we got a delim
+ * by making the delim \0 and smashing it with nulldelim.
+ */
+ j = 1;
+ ep = ip;
+ delim = '\0';
+ nulldelim = 1;
+ *ep = delim; /* there will be room for this */
+ } else {
+ bp->offset += j;
+ ep = memchr(ip, delim, j);
+ }
+ i += j;
+ if (ep) {
+ /*
+ * found in new piece
+ * copy back up and reset everything
+ */
+ ip = (char*) bp->ebuf - i;
+ if (i < bp->bsize) {
+ memmove(ip, bp->bbuf, i);
+ bp->gbuf = (unsigned char*) ip;
+ }
+ j = (ep - (char*) bp->bbuf) + 1;
+ bp->icount = j - i;
+ return badd(p, &bp->rdline, ip, j, delim, nulldelim);
+ }
+ ip += j;
+ }
+
+ /*
+ * full buffer without finding; add to user string and continue
+ */
+ p = badd(p, &bp->rdline, (char*) bp->bbuf, bp->bsize, 0, 0);
+ i = 0;
+ bp->icount = 0;
+ bp->gbuf = bp->ebuf;
+ }
+}
diff --git a/lib/libbio/bread.c b/lib/libbio/bread.c
@@ -0,0 +1,44 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+long Bread(Biobuf* bp, void* ap, long count) {
+ long c;
+ unsigned char* p;
+ int i, n, ic;
+
+ p = ap;
+ c = count;
+ ic = bp->icount;
+
+ while (c > 0) {
+ n = -ic;
+ if (n > c)
+ n = c;
+ if (n == 0) {
+ if (bp->state != Bractive)
+ break;
+ i = read(bp->fid, bp->bbuf, bp->bsize);
+ if (i <= 0) {
+ bp->state = Bracteof;
+ if (i < 0)
+ bp->state = Binactive;
+ break;
+ }
+ bp->gbuf = bp->bbuf;
+ bp->offset += i;
+ if (i < bp->bsize) {
+ memmove(bp->ebuf - i, bp->bbuf, i);
+ bp->gbuf = bp->ebuf - i;
+ }
+ ic = -i;
+ continue;
+ }
+ memmove(p, bp->ebuf + ic, n);
+ c -= n;
+ ic += n;
+ p += n;
+ }
+ bp->icount = ic;
+ return count - c;
+}
diff --git a/lib/libbio/bseek.c b/lib/libbio/bseek.c
@@ -0,0 +1,62 @@
+#include "common.h"
+#include "lib9.h"
+
+#include <bio.h>
+
+long long
+Bseek(Biobuf* bp, long long offset, int base) {
+ vlong n, d;
+ int bufsz;
+
+ switch (bp->state) {
+ default:
+ fprint(2, "Bseek: unknown state %d\n", bp->state);
+ return Beof;
+
+ case Bracteof:
+ bp->state = Bractive;
+ bp->icount = 0;
+ bp->gbuf = bp->ebuf;
+
+ FALLTHROUGH;
+ case Bractive:
+ n = offset;
+ if (base == 1) {
+ n += Boffset(bp);
+ base = 0;
+ }
+
+ /*
+ * try to seek within buffer
+ */
+ if (base == 0) {
+ d = n - Boffset(bp);
+ bufsz = bp->ebuf - bp->gbuf;
+ if (-bufsz <= d && d <= bufsz) {
+ bp->icount += d;
+ if (d >= 0) {
+ if (bp->icount <= 0)
+ return n;
+ } else {
+ if (bp->ebuf - bp->gbuf >= -bp->icount)
+ return n;
+ }
+ }
+ }
+
+ /*
+ * reset the buffer
+ */
+ n = lseek(bp->fid, n, base);
+ bp->icount = 0;
+ bp->gbuf = bp->ebuf;
+ break;
+
+ case Bwactive:
+ Bflush(bp);
+ n = seek(bp->fid, offset, base);
+ break;
+ }
+ bp->offset = n;
+ return n;
+}
diff --git a/lib/libbio/bvprint.c b/lib/libbio/bvprint.c
@@ -0,0 +1,36 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+static int
+fmtBflush(Fmt* f) {
+ Biobuf* bp;
+
+ bp = f->farg;
+ bp->ocount = (char*) f->to - (char*) f->stop;
+ if (Bflush(bp) < 0)
+ return 0;
+ f->stop = bp->ebuf;
+ f->to = (char*) f->stop + bp->ocount;
+ f->start = f->to;
+ return 1;
+}
+
+int Bvprint(Biobuf* bp, char* fmt, va_list arg) {
+ int n;
+ Fmt f;
+
+ f.runes = 0;
+ f.stop = bp->ebuf;
+ f.start = (char*) f.stop + bp->ocount;
+ f.to = f.start;
+ f.flush = fmtBflush;
+ f.farg = bp;
+ f.nfmt = 0;
+ fmtlocaleinit(&f, nil, nil, nil);
+ n = fmtvprint(&f, fmt, arg);
+ bp->ocount = (char*) f.to - (char*) f.stop;
+ if (n == 0)
+ n = f.nfmt;
+ return n;
+}
diff --git a/lib/libbio/bwrite.c b/lib/libbio/bwrite.c
@@ -0,0 +1,37 @@
+#include "lib9.h"
+
+#include <bio.h>
+
+long Bwrite(Biobuf* bp, void* ap, long count) {
+ long c;
+ unsigned char* p;
+ int i, n, oc;
+
+ p = ap;
+ c = count;
+ oc = bp->ocount;
+
+ while (c > 0) {
+ n = -oc;
+ if (n > c)
+ n = c;
+ if (n == 0) {
+ if (bp->state != Bwactive)
+ return Beof;
+ i = write(bp->fid, bp->bbuf, bp->bsize);
+ if (i != bp->bsize) {
+ bp->state = Binactive;
+ return Beof;
+ }
+ bp->offset += i;
+ oc = -bp->bsize;
+ continue;
+ }
+ memmove(bp->ebuf + oc, p, n);
+ oc += n;
+ c -= n;
+ p += n;
+ }
+ bp->ocount = oc;
+ return count - c;
+}
diff --git a/lib/libbio/lib9.h b/lib/libbio/lib9.h
@@ -0,0 +1,24 @@
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE
+
+#include <fcntl.h>
+#include <fmt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utf.h>
+
+#define OREAD O_RDONLY
+#define OWRITE O_WRONLY
+
+#define OCEXEC 0
+#define ORCLOSE 0
+#define OTRUNC 0
+
+#define nil ((void*) 0)
+
+typedef long long vlong;
+typedef unsigned long long uvlong;
+
+#define seek(fd, offset, whence) lseek(fd, offset, whence)
+#define create(name, mode, perm) creat(name, perm)
diff --git a/lib/libfmt/Makefile b/lib/libfmt/Makefile
@@ -0,0 +1,47 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = dofmt.o \
+ dorfmt.o \
+ errfmt.o \
+ fltfmt.o \
+ fmt.o \
+ fmtfd.o \
+ fmtfdflush.o \
+ fmtlocale.o \
+ fmtlock.o \
+ fmtnull.o \
+ fmtprint.o \
+ fmtquote.o \
+ fmtrune.o \
+ fmtstr.o \
+ fmtvprint.o \
+ fprint.o \
+ print.o \
+ runefmtstr.o \
+ runeseprint.o \
+ runesmprint.o \
+ runesnprint.o \
+ runesprint.o \
+ runevseprint.o \
+ runevsmprint.o \
+ runevsnprint.o \
+ seprint.o \
+ smprint.o \
+ snprint.o \
+ sprint.o \
+ strtod.o \
+ vfprint.o \
+ vseprint.o \
+ vsmprint.o \
+ vsnprint.o \
+ charstod.o \
+ pow10.o \
+ nan64.o
+
+HEADERS = fmt.h fmtdef.h nan.h plan9.h
+LIBRARY = libfmt.a
+
+CFLAGS += -Wuse-after-free=1
+
+include $(TOPDIR)/mk/lib.mk
diff --git a/lib/libfmt/NOTICE b/lib/libfmt/NOTICE
@@ -0,0 +1,22 @@
+This is a Unix port of the Plan 9 formatted I/O library, by Rob Pike and Ken Thompson.
+Please send comments about the packaging to Russ Cox <[email protected]>.
+
+Copyright © 2021 Plan 9 Foundation
+
+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 substantial 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.
diff --git a/lib/libfmt/README b/lib/libfmt/README
@@ -0,0 +1,5 @@
+This software was packaged for Unix by Russ Cox.
+Please send comments to [email protected].
+
+https://9fans.github.io/plan9port/unix
+
diff --git a/lib/libfmt/charstod.c b/lib/libfmt/charstod.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * Reads a floating-point number by interpreting successive characters
+ * returned by (*f)(vp). The last call it makes to f terminates the
+ * scan, so is not a character in the number. It may therefore be
+ * necessary to back up the input stream up one byte after calling charstod.
+ */
+
+double
+fmtcharstod(int (*f)(void*), void* vp) {
+ double num, dem;
+ int neg, eneg, dig, exp, c;
+
+ num = 0;
+ neg = 0;
+ dig = 0;
+ exp = 0;
+ eneg = 0;
+
+ c = (*f)(vp);
+ while (c == ' ' || c == '\t')
+ c = (*f)(vp);
+ if (c == '-' || c == '+') {
+ if (c == '-')
+ neg = 1;
+ c = (*f)(vp);
+ }
+ while (c >= '0' && c <= '9') {
+ num = num * 10 + c - '0';
+ c = (*f)(vp);
+ }
+ if (c == '.')
+ c = (*f)(vp);
+ while (c >= '0' && c <= '9') {
+ num = num * 10 + c - '0';
+ dig++;
+ c = (*f)(vp);
+ }
+ if (c == 'e' || c == 'E') {
+ c = (*f)(vp);
+ if (c == '-' || c == '+') {
+ if (c == '-') {
+ dig = -dig;
+ eneg = 1;
+ }
+ c = (*f)(vp);
+ }
+ while (c >= '0' && c <= '9') {
+ exp = exp * 10 + c - '0';
+ c = (*f)(vp);
+ }
+ }
+ exp -= dig;
+ if (exp < 0) {
+ exp = -exp;
+ eneg = !eneg;
+ }
+ dem = __fmtpow10(exp);
+ if (eneg)
+ num /= dem;
+ else
+ num *= dem;
+ if (neg)
+ return -num;
+ return num;
+}
diff --git a/lib/libfmt/dofmt.c b/lib/libfmt/dofmt.c
@@ -0,0 +1,584 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+/* Copyright (c) 2004 Google Inc.; see LICENSE */
+
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/* format the output into f->to and return the number of characters fmted */
+int dofmt(Fmt* f, char* fmt) {
+ Rune rune, *rt, *rs;
+ int r;
+ char *t, *s;
+ int n, nfmt;
+
+ nfmt = f->nfmt;
+ for (;;) {
+ if (f->runes) {
+ rt = (Rune*) f->to;
+ rs = (Rune*) f->stop;
+ while ((r = *(uchar*) fmt) && r != '%') {
+ if (r < Runeself)
+ fmt++;
+ else {
+ fmt += chartorune(&rune, fmt);
+ r = rune;
+ }
+ FMTRCHAR(f, rt, rs, r);
+ }
+ fmt++;
+ f->nfmt += rt - (Rune*) f->to;
+ f->to = rt;
+ if (!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ } else {
+ t = (char*) f->to;
+ s = (char*) f->stop;
+ while ((r = *(uchar*) fmt) && r != '%') {
+ if (r < Runeself) {
+ FMTCHAR(f, t, s, r);
+ fmt++;
+ } else {
+ n = chartorune(&rune, fmt);
+ if (t + n > s) {
+ t = (char*) __fmtflush(f, t, n);
+ if (t != nil)
+ s = (char*) f->stop;
+ else
+ return -1;
+ }
+ while (n--)
+ *t++ = *fmt++;
+ }
+ }
+ fmt++;
+ f->nfmt += t - (char*) f->to;
+ f->to = t;
+ if (!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = (char*) __fmtdispatch(f, fmt, 0);
+ if (fmt == nil)
+ return -1;
+ }
+}
+
+void* __fmtflush(Fmt* f, void* t, int len) {
+ if (f->runes)
+ f->nfmt += (Rune*) t - (Rune*) f->to;
+ else
+ f->nfmt += (char*) t - (char*) f->to;
+ f->to = t;
+ if (f->flush == 0 || (*f->flush)(f) == 0 || (char*) f->to + len > (char*) f->stop) {
+ f->stop = f->to;
+ return nil;
+ }
+ return f->to;
+}
+
+/*
+ * put a formatted block of memory sz bytes long of n runes into the output buffer,
+ * left/right justified in a field of at least f->width characters (if FmtWidth is set)
+ */
+int __fmtpad(Fmt* f, int n) {
+ char *t, *s;
+ int i;
+
+ t = (char*) f->to;
+ s = (char*) f->stop;
+ for (i = 0; i < n; i++)
+ FMTCHAR(f, t, s, ' ');
+ f->nfmt += t - (char*) f->to;
+ f->to = t;
+ return 0;
+}
+
+int __rfmtpad(Fmt* f, int n) {
+ Rune *t, *s;
+ int i;
+
+ t = (Rune*) f->to;
+ s = (Rune*) f->stop;
+ for (i = 0; i < n; i++)
+ FMTRCHAR(f, t, s, ' ');
+ f->nfmt += t - (Rune*) f->to;
+ f->to = t;
+ return 0;
+}
+
+int __fmtcpy(Fmt* f, const void* vm, int n, int sz) {
+ Rune *rt, *rs, r;
+ char *t, *s, *m, *me;
+ ulong fl;
+ int nc, w;
+
+ m = (char*) vm;
+ me = m + sz;
+ fl = f->flags;
+ w = 0;
+ if (fl & FmtWidth)
+ w = f->width;
+ if ((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if (f->runes) {
+ if (!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = (Rune*) f->to;
+ rs = (Rune*) f->stop;
+ for (nc = n; nc > 0; nc--) {
+ r = *(uchar*) m;
+ if (r < Runeself)
+ m++;
+ else if ((me - m) >= UTFmax || fullrune(m, me - m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune*) f->to;
+ f->to = rt;
+ if (fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+ return -1;
+ } else {
+ if (!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+ return -1;
+ t = (char*) f->to;
+ s = (char*) f->stop;
+ for (nc = n; nc > 0; nc--) {
+ r = *(uchar*) m;
+ if (r < Runeself)
+ m++;
+ else if ((me - m) >= UTFmax || fullrune(m, me - m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char*) f->to;
+ f->to = t;
+ if (fl & FmtLeft && __fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int __fmtrcpy(Fmt* f, const void* vm, int n) {
+ Rune r, *m, *me, *rt, *rs;
+ char *t, *s;
+ ulong fl;
+ int w;
+
+ m = (Rune*) vm;
+ fl = f->flags;
+ w = 0;
+ if (fl & FmtWidth)
+ w = f->width;
+ if ((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if (f->runes) {
+ if (!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = (Rune*) f->to;
+ rs = (Rune*) f->stop;
+ for (me = m + n; m < me; m++)
+ FMTRCHAR(f, rt, rs, *m);
+ f->nfmt += rt - (Rune*) f->to;
+ f->to = rt;
+ if (fl & FmtLeft && __rfmtpad(f, w - n) < 0)
+ return -1;
+ } else {
+ if (!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
+ return -1;
+ t = (char*) f->to;
+ s = (char*) f->stop;
+ for (me = m + n; m < me; m++) {
+ r = *m;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char*) f->to;
+ f->to = t;
+ if (fl & FmtLeft && __fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/* fmt out one character */
+int __charfmt(Fmt* f) {
+ char x[1];
+
+ x[0] = va_arg(f->args, int);
+ f->prec = 1;
+ return __fmtcpy(f, (const char*) x, 1, 1);
+}
+
+/* fmt out one rune */
+int __runefmt(Fmt* f) {
+ Rune x[1];
+
+ x[0] = va_arg(f->args, int);
+ return __fmtrcpy(f, (const void*) x, 1);
+}
+
+/* public helper routine: fmt out a null terminated string already in hand */
+int fmtstrcpy(Fmt* f, char* s) {
+ int i, j;
+
+ if (!s)
+ return __fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if (f->flags & FmtPrec) {
+#ifdef PLAN9PORT
+ Rune r;
+ i = 0;
+ for (j = 0; j < f->prec && s[i]; j++)
+ i += chartorune(&r, s + i);
+#else
+ /* ANSI requires precision in bytes, not Runes */
+ for (i = 0; i < f->prec; i++)
+ if (s[i] == 0)
+ break;
+ j = utfnlen(s, i); /* won't print partial at end */
+#endif
+ return __fmtcpy(f, s, j, i);
+ }
+ return __fmtcpy(f, s, utflen(s), strlen(s));
+}
+
+/* fmt out a null terminated utf string */
+int __strfmt(Fmt* f) {
+ char* s;
+
+ s = va_arg(f->args, char*);
+ return fmtstrcpy(f, s);
+}
+
+/* public helper routine: fmt out a null terminated rune string already in hand */
+int fmtrunestrcpy(Fmt* f, Rune* s) {
+ Rune* e;
+ int n, p;
+
+ if (!s)
+ return __fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if (f->flags & FmtPrec) {
+ p = f->prec;
+ for (n = 0; n < p; n++)
+ if (s[n] == 0)
+ break;
+ } else {
+ for (e = s; *e; e++)
+ ;
+ n = e - s;
+ }
+ return __fmtrcpy(f, s, n);
+}
+
+/* fmt out a null terminated rune string */
+int __runesfmt(Fmt* f) {
+ Rune* s;
+
+ s = va_arg(f->args, Rune*);
+ return fmtrunestrcpy(f, s);
+}
+
+/* fmt a % */
+int __percentfmt(Fmt* f) {
+ Rune x[1];
+
+ x[0] = f->r;
+ f->prec = 1;
+ return __fmtrcpy(f, (const void*) x, 1);
+}
+
+/* fmt an integer */
+int __ifmt(Fmt* f) {
+ char buf[140], *p, *conv;
+ /* 140: for 64 bits of binary + 3-byte sep every 4 digits */
+ uvlong vu;
+ ulong u;
+ int neg, base, i, n, fl, w, isv;
+ int ndig, len, excess, bytelen;
+ char* grouping;
+ char* thousands;
+
+ neg = 0;
+ fl = f->flags;
+ isv = 0;
+ vu = 0;
+ u = 0;
+#ifndef PLAN9PORT
+ /*
+ * Unsigned verbs for ANSI C
+ */
+ switch (f->r) {
+ case 'o':
+ case 'p':
+ case 'u':
+ case 'x':
+ case 'X':
+ fl |= FmtUnsigned;
+ fl &= ~(FmtSign | FmtSpace);
+ break;
+ }
+#endif
+ if (f->r == 'p') {
+ u = (ulong) va_arg(f->args, void*);
+ f->r = 'x';
+ fl |= FmtUnsigned;
+ } else if (fl & FmtVLong) {
+ isv = 1;
+ if (fl & FmtUnsigned)
+ vu = va_arg(f->args, uvlong);
+ else
+ vu = va_arg(f->args, vlong);
+ } else if (fl & FmtLong) {
+ if (fl & FmtUnsigned)
+ u = va_arg(f->args, ulong);
+ else
+ u = va_arg(f->args, long);
+ } else if (fl & FmtByte) {
+ if (fl & FmtUnsigned)
+ u = (uchar) va_arg(f->args, int);
+ else
+ u = (char) va_arg(f->args, int);
+ } else if (fl & FmtShort) {
+ if (fl & FmtUnsigned)
+ u = (ushort) va_arg(f->args, int);
+ else
+ u = (short) va_arg(f->args, int);
+ } else {
+ if (fl & FmtUnsigned)
+ u = va_arg(f->args, uint);
+ else
+ u = va_arg(f->args, int);
+ }
+ conv = "0123456789abcdef";
+ grouping = "\4"; /* for hex, octal etc. (undefined by spec but nice) */
+ thousands = f->thousands;
+ switch (f->r) {
+ case 'd':
+ case 'i':
+ case 'u':
+ base = 10;
+ grouping = f->grouping;
+ break;
+ case 'X':
+ conv = "0123456789ABCDEF";
+ /* fall through */
+ case 'x':
+ base = 16;
+ thousands = ":";
+ break;
+ case 'b':
+ base = 2;
+ thousands = ":";
+ break;
+ case 'o':
+ base = 8;
+ break;
+ default:
+ return -1;
+ }
+ if (!(fl & FmtUnsigned)) {
+ if (isv && (vlong) vu < 0) {
+ vu = -(vlong) vu;
+ neg = 1;
+ } else if (!isv && (long) u < 0) {
+ u = -(long) u;
+ neg = 1;
+ }
+ }
+ p = buf + sizeof buf - 1;
+ n = 0; /* in runes */
+ excess = 0; /* number of bytes > number runes */
+ ndig = 0;
+ len = utflen(thousands);
+ bytelen = strlen(thousands);
+ if (isv) {
+ while (vu) {
+ i = vu % base;
+ vu /= base;
+ if ((fl & FmtComma) && n % 4 == 3) {
+ *p-- = ',';
+ n++;
+ }
+ if ((fl & FmtApost) && __needsep(&ndig, &grouping)) {
+ n += len;
+ excess += bytelen - len;
+ p -= bytelen;
+ memmove(p + 1, thousands, bytelen);
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ } else {
+ while (u) {
+ i = u % base;
+ u /= base;
+ if ((fl & FmtComma) && n % 4 == 3) {
+ *p-- = ',';
+ n++;
+ }
+ if ((fl & FmtApost) && __needsep(&ndig, &grouping)) {
+ n += len;
+ excess += bytelen - len;
+ p -= bytelen;
+ memmove(p + 1, thousands, bytelen);
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }
+ if (n == 0) {
+ /*
+ * "The result of converting a zero value with
+ * a precision of zero is no characters." - ANSI
+ *
+ * "For o conversion, # increases the precision, if and only if
+ * necessary, to force the first digit of the result to be a zero
+ * (if the value and precision are both 0, a single 0 is printed)." - ANSI
+ */
+ if (!(fl & FmtPrec) || f->prec != 0 || (f->r == 'o' && (fl & FmtSharp))) {
+ *p-- = '0';
+ n = 1;
+ if (fl & FmtApost)
+ __needsep(&ndig, &grouping);
+ }
+
+ /*
+ * Zero values don't get 0x.
+ */
+ if (f->r == 'x' || f->r == 'X')
+ fl &= ~FmtSharp;
+ }
+ for (w = f->prec; n < w && p > buf + 3; n++) {
+ if ((fl & FmtApost) && __needsep(&ndig, &grouping)) {
+ n += len;
+ excess += bytelen - len;
+ p -= bytelen;
+ memmove(p + 1, thousands, bytelen);
+ }
+ *p-- = '0';
+ }
+ if (neg || (fl & (FmtSign | FmtSpace)))
+ n++;
+ if (fl & FmtSharp) {
+ if (base == 16)
+ n += 2;
+ else if (base == 8) {
+ if (p[1] == '0')
+ fl &= ~FmtSharp;
+ else
+ n++;
+ }
+ }
+ if ((fl & FmtZero) && !(fl & (FmtLeft | FmtPrec))) {
+ w = 0;
+ if (fl & FmtWidth)
+ w = f->width;
+ for (; n < w && p > buf + 3; n++) {
+ if ((fl & FmtApost) && __needsep(&ndig, &grouping)) {
+ n += len;
+ excess += bytelen - len;
+ p -= bytelen;
+ memmove(p + 1, thousands, bytelen);
+ }
+ *p-- = '0';
+ }
+ f->flags &= ~FmtWidth;
+ }
+ if (fl & FmtSharp) {
+ if (base == 16)
+ *p-- = f->r;
+ if (base == 16 || base == 8)
+ *p-- = '0';
+ }
+ if (neg)
+ *p-- = '-';
+ else if (fl & FmtSign)
+ *p-- = '+';
+ else if (fl & FmtSpace)
+ *p-- = ' ';
+ f->flags &= ~FmtPrec;
+ return __fmtcpy(f, p + 1, n, n + excess);
+}
+
+int __countfmt(Fmt* f) {
+ void* p;
+ ulong fl;
+
+ fl = f->flags;
+ p = va_arg(f->args, void*);
+ if (fl & FmtVLong) {
+ *(vlong*) p = f->nfmt;
+ } else if (fl & FmtLong) {
+ *(long*) p = f->nfmt;
+ } else if (fl & FmtByte) {
+ *(char*) p = f->nfmt;
+ } else if (fl & FmtShort) {
+ *(short*) p = f->nfmt;
+ } else {
+ *(int*) p = f->nfmt;
+ }
+ return 0;
+}
+
+int __flagfmt(Fmt* f) {
+ switch (f->r) {
+ case ',':
+ f->flags |= FmtComma;
+ break;
+ case '-':
+ f->flags |= FmtLeft;
+ break;
+ case '+':
+ f->flags |= FmtSign;
+ break;
+ case '#':
+ f->flags |= FmtSharp;
+ break;
+ case '\'':
+ f->flags |= FmtApost;
+ break;
+ case ' ':
+ f->flags |= FmtSpace;
+ break;
+ case 'u':
+ f->flags |= FmtUnsigned;
+ break;
+ case 'h':
+ if (f->flags & FmtShort)
+ f->flags |= FmtByte;
+ f->flags |= FmtShort;
+ break;
+ case 'L':
+ f->flags |= FmtLDouble;
+ break;
+ case 'l':
+ if (f->flags & FmtLong)
+ f->flags |= FmtVLong;
+ f->flags |= FmtLong;
+ break;
+ }
+ return 1;
+}
+
+/* default error format */
+int __badfmt(Fmt* f) {
+ char x[2 + UTFmax];
+ int n;
+
+ x[0] = '%';
+ n = 1 + runetochar(x + 1, &f->r);
+ x[n++] = '%';
+ f->prec = n;
+ __fmtcpy(f, (const void*) x, n, n);
+ return 0;
+}
diff --git a/lib/libfmt/dorfmt.c b/lib/libfmt/dorfmt.c
@@ -0,0 +1,49 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/* format the output into f->to and return the number of characters fmted */
+
+/* BUG: THIS FILE IS NOT UPDATED TO THE NEW SPEC */
+int dorfmt(Fmt* f, const Rune* fmt) {
+ Rune *rt, *rs;
+ int r;
+ char *t, *s;
+ int nfmt;
+
+ nfmt = f->nfmt;
+ for (;;) {
+ if (f->runes) {
+ rt = (Rune*) f->to;
+ rs = (Rune*) f->stop;
+ while ((r = *fmt++) && r != '%') {
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune*) f->to;
+ f->to = rt;
+ if (!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ } else {
+ t = (char*) f->to;
+ s = (char*) f->stop;
+ while ((r = *fmt++) && r != '%') {
+ FMTRUNE(f, t, f->stop, r);
+ }
+ f->nfmt += t - (char*) f->to;
+ f->to = t;
+ if (!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = (Rune*) __fmtdispatch(f, (Rune*) fmt, 1);
+ if (fmt == nil)
+ return -1;
+ }
+ return 0; /* not reached */
+}
diff --git a/lib/libfmt/errfmt.c b/lib/libfmt/errfmt.c
@@ -0,0 +1,15 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+
+int __errfmt(Fmt* f) {
+ char* s;
+
+ s = strerror(errno);
+ return fmtstrcpy(f, s);
+}
diff --git a/lib/libfmt/fltfmt.c b/lib/libfmt/fltfmt.c
@@ -0,0 +1,805 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "common.h"
+#include "fmt.h"
+#include "fmtdef.h"
+#include "nan.h"
+#include "plan9.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <float.h>
+#include <fmt.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum {
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NSIGNIF = 17
+};
+
+/*
+ * first few powers of 10, enough for about 1/2 of the
+ * total space for doubles.
+ */
+static double pows10[] = {
+ 1e0,
+ 1e1,
+ 1e2,
+ 1e3,
+ 1e4,
+ 1e5,
+ 1e6,
+ 1e7,
+ 1e8,
+ 1e9,
+ 1e10,
+ 1e11,
+ 1e12,
+ 1e13,
+ 1e14,
+ 1e15,
+ 1e16,
+ 1e17,
+ 1e18,
+ 1e19,
+ 1e20,
+ 1e21,
+ 1e22,
+ 1e23,
+ 1e24,
+ 1e25,
+ 1e26,
+ 1e27,
+ 1e28,
+ 1e29,
+ 1e30,
+ 1e31,
+ 1e32,
+ 1e33,
+ 1e34,
+ 1e35,
+ 1e36,
+ 1e37,
+ 1e38,
+ 1e39,
+ 1e40,
+ 1e41,
+ 1e42,
+ 1e43,
+ 1e44,
+ 1e45,
+ 1e46,
+ 1e47,
+ 1e48,
+ 1e49,
+ 1e50,
+ 1e51,
+ 1e52,
+ 1e53,
+ 1e54,
+ 1e55,
+ 1e56,
+ 1e57,
+ 1e58,
+ 1e59,
+ 1e60,
+ 1e61,
+ 1e62,
+ 1e63,
+ 1e64,
+ 1e65,
+ 1e66,
+ 1e67,
+ 1e68,
+ 1e69,
+ 1e70,
+ 1e71,
+ 1e72,
+ 1e73,
+ 1e74,
+ 1e75,
+ 1e76,
+ 1e77,
+ 1e78,
+ 1e79,
+ 1e80,
+ 1e81,
+ 1e82,
+ 1e83,
+ 1e84,
+ 1e85,
+ 1e86,
+ 1e87,
+ 1e88,
+ 1e89,
+ 1e90,
+ 1e91,
+ 1e92,
+ 1e93,
+ 1e94,
+ 1e95,
+ 1e96,
+ 1e97,
+ 1e98,
+ 1e99,
+ 1e100,
+ 1e101,
+ 1e102,
+ 1e103,
+ 1e104,
+ 1e105,
+ 1e106,
+ 1e107,
+ 1e108,
+ 1e109,
+ 1e110,
+ 1e111,
+ 1e112,
+ 1e113,
+ 1e114,
+ 1e115,
+ 1e116,
+ 1e117,
+ 1e118,
+ 1e119,
+ 1e120,
+ 1e121,
+ 1e122,
+ 1e123,
+ 1e124,
+ 1e125,
+ 1e126,
+ 1e127,
+ 1e128,
+ 1e129,
+ 1e130,
+ 1e131,
+ 1e132,
+ 1e133,
+ 1e134,
+ 1e135,
+ 1e136,
+ 1e137,
+ 1e138,
+ 1e139,
+ 1e140,
+ 1e141,
+ 1e142,
+ 1e143,
+ 1e144,
+ 1e145,
+ 1e146,
+ 1e147,
+ 1e148,
+ 1e149,
+ 1e150,
+ 1e151,
+ 1e152,
+ 1e153,
+ 1e154,
+ 1e155,
+ 1e156,
+ 1e157,
+ 1e158,
+ 1e159,
+};
+#define npows10 ((int) (sizeof(pows10) / sizeof(pows10[0])))
+#define pow10(x) fmtpow10(x)
+
+static double
+pow10(int n) {
+ double d;
+ int neg;
+
+ neg = 0;
+ if (n < 0) {
+ neg = 1;
+ n = -n;
+ }
+
+ if (n < npows10)
+ d = pows10[n];
+ else {
+ d = pows10[npows10 - 1];
+ for (;;) {
+ n -= npows10 - 1;
+ if (n < npows10) {
+ d *= pows10[n];
+ break;
+ }
+ d *= pows10[npows10 - 1];
+ }
+ }
+ if (neg)
+ return 1. / d;
+ return d;
+}
+
+/*
+ * add 1 to the decimal integer string a of length n.
+ * if 99999 overflows into 10000, return 1 to tell caller
+ * to move the virtual decimal point.
+ */
+static int
+xadd1(char* a, int n) {
+ char* b;
+ int c;
+
+ if (n < 0 || n > NSIGNIF)
+ return 0;
+ for (b = a + n - 1; b >= a; b--) {
+ c = *b + 1;
+ if (c <= '9') {
+ *b = c;
+ return 0;
+ }
+ *b = '0';
+ }
+ /*
+ * need to overflow adding digit.
+ * shift number down and insert 1 at beginning.
+ * decimal is known to be 0s or we wouldn't
+ * have gotten this far. (e.g., 99999+1 => 00000)
+ */
+ a[0] = '1';
+ return 1;
+}
+
+/*
+ * subtract 1 from the decimal integer string a.
+ * if 10000 underflows into 09999, make it 99999
+ * and return 1 to tell caller to move the virtual
+ * decimal point. this way, xsub1 is inverse of xadd1.
+ */
+static int
+xsub1(char* a, int n) {
+ char* b;
+ int c;
+
+ if (n < 0 || n > NSIGNIF)
+ return 0;
+ for (b = a + n - 1; b >= a; b--) {
+ c = *b - 1;
+ if (c >= '0') {
+ if (c == '0' && b == a) {
+ /*
+ * just zeroed the top digit; shift everyone up.
+ * decimal is known to be 9s or we wouldn't
+ * have gotten this far. (e.g., 10000-1 => 09999)
+ */
+ *b = '9';
+ return 1;
+ }
+ *b = c;
+ return 0;
+ }
+ *b = '9';
+ }
+ /*
+ * can't get here. the number a is always normalized
+ * so that it has a nonzero first digit.
+ */
+ abort();
+}
+
+/*
+ * format exponent like sprintf(p, "e%+02d", e)
+ */
+static void
+xfmtexp(char* p, int e, int ucase) {
+ char se[9];
+ int i;
+
+ *p++ = ucase ? 'E' : 'e';
+ if (e < 0) {
+ *p++ = '-';
+ e = -e;
+ } else
+ *p++ = '+';
+ i = 0;
+ while (e) {
+ se[i++] = e % 10 + '0';
+ e /= 10;
+ }
+ while (i < 2)
+ se[i++] = '0';
+ while (i > 0)
+ *p++ = se[--i];
+ *p++ = '\0';
+}
+
+/*
+ * compute decimal integer m, exp such that:
+ * f = m*10^exp
+ * m is as short as possible with losing exactness
+ * assumes special cases (NaN, +Inf, -Inf) have been handled.
+ */
+static void
+xdtoa(double f, char* s, int* exp, int* neg, int* ns) {
+ int c, d, e2, e, ee, i, ndigit, oerrno;
+ char tmp[NSIGNIF + 10];
+ double g;
+
+ oerrno = errno; /* in case strtod smashes errno */
+
+ /*
+ * make f non-negative.
+ */
+ *neg = 0;
+ if (f < 0) {
+ f = -f;
+ *neg = 1;
+ }
+
+ /*
+ * must handle zero specially.
+ */
+ if (f == 0) {
+ *exp = 0;
+ s[0] = '0';
+ s[1] = '\0';
+ *ns = 1;
+ return;
+ }
+
+ /*
+ * find g,e such that f = g*10^e.
+ * guess 10-exponent using 2-exponent, then fine tune.
+ */
+ frexp(f, &e2);
+ e = (int) (e2 * .301029995664);
+ g = f * pow10(-e);
+ while (g < 1) {
+ e--;
+ g = f * pow10(-e);
+ }
+ while (g >= 10) {
+ e++;
+ g = f * pow10(-e);
+ }
+
+ /*
+ * convert NSIGNIF digits as a first approximation.
+ */
+ for (i = 0; i < NSIGNIF; i++) {
+ d = (int) g;
+ s[i] = d + '0';
+ g = (g - d) * 10;
+ }
+ s[i] = 0;
+
+ /*
+ * adjust e because s is 314159... not 3.14159...
+ */
+ e -= NSIGNIF - 1;
+ xfmtexp(s + NSIGNIF, e, 0);
+
+ /*
+ * adjust conversion until strtod(s) == f exactly.
+ */
+ for (i = 0; i < 10; i++) {
+ g = fmtstrtod(s, nil);
+ if (f > g) {
+ if (xadd1(s, NSIGNIF)) {
+ /* gained a digit */
+ e--;
+ xfmtexp(s + NSIGNIF, e, 0);
+ }
+ continue;
+ }
+ if (f < g) {
+ if (xsub1(s, NSIGNIF)) {
+ /* lost a digit */
+ e++;
+ xfmtexp(s + NSIGNIF, e, 0);
+ }
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * play with the decimal to try to simplify.
+ */
+
+ /*
+ * bump last few digits up to 9 if we can
+ */
+ for (i = NSIGNIF - 1; i >= NSIGNIF - 3; i--) {
+ c = s[i];
+ if (c != '9') {
+ s[i] = '9';
+ g = fmtstrtod(s, nil);
+ if (g != f) {
+ s[i] = c;
+ break;
+ }
+ }
+ }
+
+ /*
+ * add 1 in hopes of turning 9s to 0s
+ */
+ if (s[NSIGNIF - 1] == '9') {
+ strcpy(tmp, s);
+ ee = e;
+ if (xadd1(tmp, NSIGNIF)) {
+ ee--;
+ xfmtexp(tmp + NSIGNIF, ee, 0);
+ }
+ g = fmtstrtod(tmp, nil);
+ if (g == f) {
+ strcpy(s, tmp);
+ e = ee;
+ }
+ }
+
+ /*
+ * bump last few digits down to 0 as we can.
+ */
+ for (i = NSIGNIF - 1; i >= NSIGNIF - 3; i--) {
+ c = s[i];
+ if (c != '0') {
+ s[i] = '0';
+ g = fmtstrtod(s, nil);
+ if (g != f) {
+ s[i] = c;
+ break;
+ }
+ }
+ }
+
+ /*
+ * remove trailing zeros.
+ */
+ ndigit = NSIGNIF;
+ while (ndigit > 1 && s[ndigit - 1] == '0') {
+ e++;
+ --ndigit;
+ }
+ s[ndigit] = 0;
+ *exp = e;
+ *ns = ndigit;
+ errno = oerrno;
+}
+
+#ifdef PLAN9PORT
+static char* special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" };
+#else
+static char* special[] = { "nan", "NAN", "inf", "INF", "-inf", "-INF" };
+#endif
+
+int __efgfmt(Fmt* fmt) {
+ char buf[NSIGNIF + 10], *dot, *digits, *p, *s, suf[10], *t;
+ double f;
+ int c, chr, dotwid, e, exp, fl, ndigits, neg, newndigits;
+ int pad, point, prec, realchr, sign, sufwid, ucase, wid, z1, z2;
+ Rune r, *rs, *rt;
+
+ if (fmt->flags & FmtLong)
+ f = va_arg(fmt->args, long double);
+ else
+ f = va_arg(fmt->args, double);
+
+ /*
+ * extract formatting flags
+ */
+ fl = fmt->flags;
+ fmt->flags = 0;
+ prec = FDEFLT;
+ if (fl & FmtPrec)
+ prec = fmt->prec;
+ chr = fmt->r;
+ ucase = 0;
+ switch (chr) {
+ case 'A':
+ case 'E':
+ case 'F':
+ case 'G':
+ chr += 'a' - 'A';
+ ucase = 1;
+ break;
+ }
+
+ /*
+ * pick off special numbers.
+ */
+ if (__isNaN(f)) {
+ s = special[0 + ucase];
+ special:
+ fmt->flags = fl & (FmtWidth | FmtLeft);
+ return __fmtcpy(fmt, s, strlen(s), strlen(s));
+ }
+ if (__isInf(f, 1)) {
+ s = special[2 + ucase];
+ goto special;
+ }
+ if (__isInf(f, -1)) {
+ s = special[4 + ucase];
+ goto special;
+ }
+
+ /*
+ * get exact representation.
+ */
+ digits = buf;
+ xdtoa(f, digits, &exp, &neg, &ndigits);
+
+ /*
+ * get locale's decimal point.
+ */
+ dot = fmt->decimal;
+ if (dot == nil)
+ dot = ".";
+ dotwid = utflen(dot);
+
+ /*
+ * now the formatting fun begins.
+ * compute parameters for actual fmt:
+ *
+ * pad: number of spaces to insert before/after field.
+ * z1: number of zeros to insert before digits
+ * z2: number of zeros to insert after digits
+ * point: number of digits to print before decimal point
+ * ndigits: number of digits to use from digits[]
+ * suf: trailing suffix, like "e-5"
+ */
+ realchr = chr;
+ switch (chr) {
+ case 'g':
+ /*
+ * convert to at most prec significant digits. (prec=0 means 1)
+ */
+ if (prec == 0)
+ prec = 1;
+ if (ndigits > prec) {
+ if (digits[prec] >= '5' && xadd1(digits, prec))
+ exp++;
+ exp += ndigits - prec;
+ ndigits = prec;
+ }
+
+ /*
+ * extra rules for %g (implemented below):
+ * trailing zeros removed after decimal unless FmtSharp.
+ * decimal point only if digit follows.
+ */
+
+ FALLTHROUGH;
+ /* fall through to %e */
+ default:
+ case 'e':
+ /*
+ * one significant digit before decimal, no leading zeros.
+ */
+ point = 1;
+ z1 = 0;
+
+ /*
+ * decimal point is after ndigits digits right now.
+ * slide to be after first.
+ */
+ e = exp + (ndigits - 1);
+
+ /*
+ * if this is %g, check exponent and convert prec
+ */
+ if (realchr == 'g') {
+ if (-4 <= e && e < prec)
+ goto casef;
+ prec--; /* one digit before decimal; rest after */
+ }
+
+ /*
+ * compute trailing zero padding or truncate digits.
+ */
+ if (1 + prec >= ndigits)
+ z2 = 1 + prec - ndigits;
+ else {
+ /*
+ * truncate digits
+ */
+ assert(realchr != 'g');
+ newndigits = 1 + prec;
+ if (digits[newndigits] >= '5' && xadd1(digits, newndigits)) {
+ /*
+ * had 999e4, now have 100e5
+ */
+ e++;
+ }
+ ndigits = newndigits;
+ z2 = 0;
+ }
+ xfmtexp(suf, e, ucase);
+ sufwid = strlen(suf);
+ break;
+
+ casef:
+ case 'f':
+ /*
+ * determine where digits go with respect to decimal point
+ */
+ if (ndigits + exp > 0) {
+ point = ndigits + exp;
+ z1 = 0;
+ } else {
+ point = 1;
+ z1 = 1 + -(ndigits + exp);
+ }
+
+ /*
+ * %g specifies prec = number of significant digits
+ * convert to number of digits after decimal point
+ */
+ if (realchr == 'g')
+ prec += z1 - point;
+
+ /*
+ * compute trailing zero padding or truncate digits.
+ */
+ if (point + prec >= z1 + ndigits)
+ z2 = point + prec - (z1 + ndigits);
+ else {
+ /*
+ * truncate digits
+ */
+ assert(realchr != 'g');
+ newndigits = point + prec - z1;
+ if (newndigits < 0) {
+ z1 += newndigits;
+ newndigits = 0;
+ } else if (newndigits == 0) {
+ /* perhaps round up */
+ if (digits[0] >= '5') {
+ digits[0] = '1';
+ newndigits = 1;
+ goto newdigit;
+ }
+ } else if (digits[newndigits] >= '5' && xadd1(digits, newndigits)) {
+ /*
+ * digits was 999, is now 100; make it 1000
+ */
+ digits[newndigits++] = '0';
+ newdigit:
+ /*
+ * account for new digit
+ */
+ if (z1) /* 0.099 => 0.100 or 0.99 => 1.00*/
+ z1--;
+ else /* 9.99 => 10.00 */
+ point++;
+ }
+ z2 = 0;
+ ndigits = newndigits;
+ }
+ sufwid = 0;
+ break;
+ }
+
+ /*
+ * if %g is given without FmtSharp, remove trailing zeros.
+ * must do after truncation, so that e.g. print %.3g 1.001
+ * produces 1, not 1.00. sorry, but them's the rules.
+ */
+ if (realchr == 'g' && !(fl & FmtSharp)) {
+ if (z1 + ndigits + z2 >= point) {
+ if (z1 + ndigits < point)
+ z2 = point - (z1 + ndigits);
+ else {
+ z2 = 0;
+ while (z1 + ndigits > point && digits[ndigits - 1] == '0')
+ ndigits--;
+ }
+ }
+ }
+
+ /*
+ * compute width of all digits and decimal point and suffix if any
+ */
+ wid = z1 + ndigits + z2;
+ if (wid > point)
+ wid += dotwid;
+ else if (wid == point) {
+ if (fl & FmtSharp)
+ wid += dotwid;
+ else
+ point++; /* do not print any decimal point */
+ }
+ wid += sufwid;
+
+ /*
+ * determine sign
+ */
+ sign = 0;
+ if (neg)
+ sign = '-';
+ else if (fl & FmtSign)
+ sign = '+';
+ else if (fl & FmtSpace)
+ sign = ' ';
+ if (sign)
+ wid++;
+
+ /*
+ * compute padding
+ */
+ pad = 0;
+ if ((fl & FmtWidth) && fmt->width > wid)
+ pad = fmt->width - wid;
+ if (pad && !(fl & FmtLeft) && (fl & FmtZero)) {
+ z1 += pad;
+ point += pad;
+ pad = 0;
+ }
+
+ /*
+ * format the actual field. too bad about doing this twice.
+ */
+ if (fmt->runes) {
+ if (pad && !(fl & FmtLeft) && __rfmtpad(fmt, pad) < 0)
+ return -1;
+ rt = (Rune*) fmt->to;
+ rs = (Rune*) fmt->stop;
+ if (sign)
+ FMTRCHAR(fmt, rt, rs, sign);
+ while (z1 > 0 || ndigits > 0 || z2 > 0) {
+ if (z1 > 0) {
+ z1--;
+ c = '0';
+ } else if (ndigits > 0) {
+ ndigits--;
+ c = *digits++;
+ } else {
+ z2--;
+ c = '0';
+ }
+ FMTRCHAR(fmt, rt, rs, c);
+ if (--point == 0) {
+ for (p = dot; *p;) {
+ p += chartorune(&r, p);
+ FMTRCHAR(fmt, rt, rs, r);
+ }
+ }
+ }
+ fmt->nfmt += rt - (Rune*) fmt->to;
+ fmt->to = rt;
+ if (sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
+ return -1;
+ if (pad && (fl & FmtLeft) && __rfmtpad(fmt, pad) < 0)
+ return -1;
+ } else {
+ if (pad && !(fl & FmtLeft) && __fmtpad(fmt, pad) < 0)
+ return -1;
+ t = (char*) fmt->to;
+ s = (char*) fmt->stop;
+ if (sign)
+ FMTCHAR(fmt, t, s, sign);
+ while (z1 > 0 || ndigits > 0 || z2 > 0) {
+ if (z1 > 0) {
+ z1--;
+ c = '0';
+ } else if (ndigits > 0) {
+ ndigits--;
+ c = *digits++;
+ } else {
+ z2--;
+ c = '0';
+ }
+ FMTCHAR(fmt, t, s, c);
+ if (--point == 0)
+ for (p = dot; *p; p++)
+ FMTCHAR(fmt, t, s, *p);
+ }
+ fmt->nfmt += t - (char*) fmt->to;
+ fmt->to = t;
+ if (sufwid && __fmtcpy(fmt, suf, sufwid, sufwid) < 0)
+ return -1;
+ if (pad && (fl & FmtLeft) && __fmtpad(fmt, pad) < 0)
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/libfmt/fmt.c b/lib/libfmt/fmt.c
@@ -0,0 +1,232 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * As of 2020, older systems like RHEL 6 and AIX still do not have C11 atomics.
+ * On those systems, make the code use volatile int accesses and hope for the best.
+ * (Most uses of fmtinstall are not actually racing with calls to print that lookup
+ * formats. The code used volatile here for years without too many problems,
+ * even though that's technically racy. A mutex is not OK, because we want to
+ * be able to call print from signal handlers.)
+ *
+ * RHEL is using an old GCC (atomics were added in GCC 4.9).
+ * AIX is using its own IBM compiler (XL C).
+ */
+#if __IBMC__ || !__clang__ && __GNUC__ && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9))
+# warning not using C11 stdatomic on legacy system
+# define _Atomic volatile
+# define atomic_load(x) (*(x))
+# define atomic_store(x, y) (*(x) = (y))
+# define ATOMIC_VAR_INIT(x) (x)
+#else
+# include <stdatomic.h>
+#endif
+
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+enum {
+ Maxfmt = 128
+};
+
+typedef struct Convfmt Convfmt;
+struct Convfmt {
+ int c;
+ Fmts fmt;
+};
+
+static struct
+{
+ /*
+ * lock updates to fmt by calling __fmtlock, __fmtunlock.
+ * reads can start at nfmt and work backward without
+ * further locking. later fmtinstalls take priority over earlier
+ * ones because of the backwards loop.
+ * once installed, a format is never overwritten.
+ */
+ _Atomic int nfmt;
+ Convfmt fmt[Maxfmt];
+} fmtalloc = {
+#ifdef PLAN9PORT
+ ATOMIC_VAR_INIT(27),
+#else
+ ATOMIC_VAR_INIT(30),
+#endif
+ {
+ { ' ', __flagfmt },
+ { '#', __flagfmt },
+ { '%', __percentfmt },
+ { '\'', __flagfmt },
+ { '+', __flagfmt },
+ { ',', __flagfmt },
+ { '-', __flagfmt },
+ { 'C', __runefmt }, /* Plan 9 addition */
+ { 'E', __efgfmt },
+#ifndef PLAN9PORT
+ { 'F', __efgfmt }, /* ANSI only */
+#endif
+ { 'G', __efgfmt },
+#ifndef PLAN9PORT
+ { 'L', __flagfmt }, /* ANSI only */
+#endif
+ { 'S', __runesfmt }, /* Plan 9 addition */
+ { 'X', __ifmt },
+ { 'b', __ifmt }, /* Plan 9 addition */
+ { 'c', __charfmt },
+ { 'd', __ifmt },
+ { 'e', __efgfmt },
+ { 'f', __efgfmt },
+ { 'g', __efgfmt },
+ { 'h', __flagfmt },
+#ifndef PLAN9PORT
+ { 'i', __ifmt }, /* ANSI only */
+#endif
+ { 'l', __flagfmt },
+ { 'n', __countfmt },
+ { 'o', __ifmt },
+ { 'p', __ifmt },
+ { 'r', __errfmt },
+ { 's', __strfmt },
+#ifdef PLAN9PORT
+ { 'u', __flagfmt },
+#else
+ { 'u', __ifmt },
+#endif
+ { 'x', __ifmt },
+ }
+};
+
+int (*fmtdoquote)(int);
+
+/*
+ * __fmtlock() must be set
+ */
+static int
+__fmtinstall(int c, Fmts f) {
+ Convfmt* p;
+ int i;
+
+ if (c <= 0 || c >= 65536)
+ return -1;
+ if (!f)
+ f = __badfmt;
+
+ i = atomic_load(&fmtalloc.nfmt);
+ if (i == Maxfmt)
+ return -1;
+ p = &fmtalloc.fmt[i];
+ p->c = c;
+ p->fmt = f;
+ atomic_store(&fmtalloc.nfmt, i + 1);
+
+ return 0;
+}
+
+int fmtinstall(int c, int (*f)(Fmt*)) {
+ int ret;
+
+ __fmtlock();
+ ret = __fmtinstall(c, f);
+ __fmtunlock();
+ return ret;
+}
+
+static Fmts
+fmtfmt(int c) {
+ Convfmt *p, *ep;
+
+ ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
+ for (p = ep; p-- > fmtalloc.fmt;)
+ if (p->c == c)
+ return p->fmt;
+
+ return __badfmt;
+}
+
+void* __fmtdispatch(Fmt* f, void* fmt, int isrunes) {
+ Rune rune, r;
+ int i, n;
+
+ f->flags = 0;
+ f->width = f->prec = 0;
+
+ for (;;) {
+ if (isrunes) {
+ r = *(Rune*) fmt;
+ fmt = (Rune*) fmt + 1;
+ } else {
+ fmt = (char*) fmt + chartorune(&rune, (char*) fmt);
+ r = rune;
+ }
+ f->r = r;
+ switch (r) {
+ case '\0':
+ return nil;
+ case '.':
+ f->flags |= FmtWidth | FmtPrec;
+ continue;
+ case '0':
+ if (!(f->flags & FmtWidth)) {
+ f->flags |= FmtZero;
+ continue;
+ }
+ /* fall through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i = 0;
+ while (r >= '0' && r <= '9') {
+ i = i * 10 + r - '0';
+ if (isrunes) {
+ r = *(Rune*) fmt;
+ fmt = (Rune*) fmt + 1;
+ } else {
+ r = *(char*) fmt;
+ fmt = (char*) fmt + 1;
+ }
+ }
+ if (isrunes)
+ fmt = (Rune*) fmt - 1;
+ else
+ fmt = (char*) fmt - 1;
+ numflag:
+ if (f->flags & FmtWidth) {
+ f->flags |= FmtPrec;
+ f->prec = i;
+ } else {
+ f->flags |= FmtWidth;
+ f->width = i;
+ }
+ continue;
+ case '*':
+ i = va_arg(f->args, int);
+ if (i < 0) {
+ /*
+ * negative precision =>
+ * ignore the precision.
+ */
+ if (f->flags & FmtPrec) {
+ f->flags &= ~FmtPrec;
+ f->prec = 0;
+ continue;
+ }
+ i = -i;
+ f->flags |= FmtLeft;
+ }
+ goto numflag;
+ }
+ n = (*fmtfmt(r))(f);
+ if (n < 0)
+ return nil;
+ if (n == 0)
+ return fmt;
+ }
+}
diff --git a/lib/libfmt/fmt.h b/lib/libfmt/fmt.h
@@ -0,0 +1,116 @@
+#ifndef _FMT_H_
+#define _FMT_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+
+#include <stdarg.h>
+#include <utf.h>
+
+typedef struct Fmt Fmt;
+struct Fmt {
+ unsigned char runes; /* output buffer is runes or chars? */
+ void* start; /* of buffer */
+ void* to; /* current place in the buffer */
+ void* stop; /* end of the buffer; overwritten if flush fails */
+ int (*flush)(Fmt*); /* called when to == stop */
+ void* farg; /* to make flush a closure */
+ int nfmt; /* num chars formatted so far */
+ va_list args; /* args passed to dofmt */
+ Rune r; /* % format Rune */
+ int width;
+ int prec;
+ unsigned long flags;
+ char* decimal; /* decimal point; cannot be "" */
+
+ /* For %'d */
+ char* thousands; /* separator for thousands */
+
+ /*
+ * Each char is an integer indicating #digits before next separator. Values:
+ * \xFF: no more grouping (or \x7F; defined to be CHAR_MAX in POSIX)
+ * \x00: repeat previous indefinitely
+ * \x**: count that many
+ */
+ char* grouping; /* descriptor of separator placement */
+};
+
+enum {
+ FmtWidth = 1,
+ FmtLeft = FmtWidth << 1,
+ FmtPrec = FmtLeft << 1,
+ FmtSharp = FmtPrec << 1,
+ FmtSpace = FmtSharp << 1,
+ FmtSign = FmtSpace << 1,
+ FmtApost = FmtSign << 1,
+ FmtZero = FmtApost << 1,
+ FmtUnsigned = FmtZero << 1,
+ FmtShort = FmtUnsigned << 1,
+ FmtLong = FmtShort << 1,
+ FmtVLong = FmtLong << 1,
+ FmtComma = FmtVLong << 1,
+ FmtByte = FmtComma << 1,
+ FmtLDouble = FmtByte << 1,
+
+ FmtFlag = FmtLDouble << 1
+};
+
+extern int (*fmtdoquote)(int);
+
+/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/fmt/?*.c | grep -v static |grep -v __ */
+int dofmt(Fmt* f, char* fmt);
+int dorfmt(Fmt* f, const Rune* fmt);
+double fmtcharstod(int (*f)(void*), void* vp);
+int fmtfdflush(Fmt* f);
+int fmtfdinit(Fmt* f, int fd, char* buf, int size);
+int fmtinstall(int c, int (*f)(Fmt*));
+int fmtnullinit(Fmt*);
+void fmtlocaleinit(Fmt*, char*, char*, char*);
+int fmtprint(Fmt* f, char* fmt, ...);
+int fmtrune(Fmt* f, int r);
+int fmtrunestrcpy(Fmt* f, Rune* s);
+int fmtstrcpy(Fmt* f, char* s);
+char* fmtstrflush(Fmt* f);
+int fmtstrinit(Fmt* f);
+double fmtstrtod(const char* as, char** aas);
+int fmtvprint(Fmt* f, char* fmt, va_list args);
+int fprint(int fd, char* fmt, ...);
+int print(char* fmt, ...);
+void quotefmtinstall(void);
+int quoterunestrfmt(Fmt* f);
+int quotestrfmt(Fmt* f);
+Rune* runefmtstrflush(Fmt* f);
+int runefmtstrinit(Fmt* f);
+Rune* runeseprint(Rune* buf, Rune* e, char* fmt, ...);
+Rune* runesmprint(char* fmt, ...);
+int runesnprint(Rune* buf, int len, char* fmt, ...);
+int runesprint(Rune* buf, char* fmt, ...);
+Rune* runevseprint(Rune* buf, Rune* e, char* fmt, va_list args);
+Rune* runevsmprint(char* fmt, va_list args);
+int runevsnprint(Rune* buf, int len, char* fmt, va_list args);
+char* seprint(char* buf, char* e, char* fmt, ...);
+char* smprint(char* fmt, ...);
+int snprint(char* buf, int len, char* fmt, ...);
+int sprint(char* buf, char* fmt, ...);
+int vfprint(int fd, char* fmt, va_list args);
+char* vseprint(char* buf, char* e, char* fmt, va_list args);
+char* vsmprint(char* fmt, va_list args);
+int vsnprint(char* buf, int len, char* fmt, va_list args);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib/libfmt/fmtdef.h b/lib/libfmt/fmtdef.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+
+/*
+ * dofmt -- format to a buffer
+ * the number of characters formatted is returned,
+ * or -1 if there was an error.
+ * if the buffer is ever filled, flush is called.
+ * it should reset the buffer and return whether formatting should continue.
+ */
+
+typedef int (*Fmts)(Fmt*);
+
+typedef struct Quoteinfo Quoteinfo;
+struct Quoteinfo {
+ int quoted; /* if set, string must be quoted */
+ int nrunesin; /* number of input runes that can be accepted */
+ int nbytesin; /* number of input bytes that can be accepted */
+ int nrunesout; /* number of runes that will be generated */
+ int nbytesout; /* number of bytes that will be generated */
+};
+
+/* Edit .+1,/^$/ |cfn |grep -v static | grep __ */
+double __Inf(int sign);
+double __NaN(void);
+int __badfmt(Fmt* f);
+int __charfmt(Fmt* f);
+int __countfmt(Fmt* f);
+int __efgfmt(Fmt* fmt);
+int __errfmt(Fmt* f);
+int __flagfmt(Fmt* f);
+int __fmtFdFlush(Fmt* f);
+int __fmtcpy(Fmt* f, const void* vm, int n, int sz);
+void* __fmtdispatch(Fmt* f, void* fmt, int isrunes);
+void* __fmtflush(Fmt* f, void* t, int len);
+int __fmtpad(Fmt* f, int n);
+double __fmtpow10(int n);
+int __fmtrcpy(Fmt* f, const void* vm, int n);
+void __fmtlock(void);
+void __fmtunlock(void);
+int __ifmt(Fmt* f);
+int __isInf(double d, int sign);
+int __isNaN(double d);
+int __needsep(int*, char**);
+int __needsquotes(char* s, int* quotelenp);
+int __percentfmt(Fmt* f);
+void __quotesetup(char* s, Rune* r, int nin, int nout, Quoteinfo* q, int sharp, int runesout);
+int __quotestrfmt(int runesin, Fmt* f);
+int __rfmtpad(Fmt* f, int n);
+int __runefmt(Fmt* f);
+int __runeneedsquotes(Rune* r, int* quotelenp);
+int __runesfmt(Fmt* f);
+int __strfmt(Fmt* f);
+
+#define FMTCHAR(f, t, s, c) \
+ do { \
+ if (t + 1 > (char*) s) { \
+ t = (char*) __fmtflush(f, t, 1); \
+ if (t != nil) \
+ s = (char*) f->stop; \
+ else \
+ return -1; \
+ } \
+ *t++ = c; \
+ } while (0)
+
+#define FMTRCHAR(f, t, s, c) \
+ do { \
+ if (t + 1 > (Rune*) s) { \
+ t = (Rune*) __fmtflush(f, t, sizeof(Rune)); \
+ if (t != nil) \
+ s = (Rune*) f->stop; \
+ else \
+ return -1; \
+ } \
+ *t++ = c; \
+ } while (0)
+
+#define FMTRUNE(f, t, s, r) \
+ do { \
+ Rune _rune; \
+ int _runelen; \
+ if (t + UTFmax > (char*) s && t + (_runelen = runelen(r)) > (char*) s) { \
+ t = (char*) __fmtflush(f, t, _runelen); \
+ if (t != nil) \
+ s = (char*) f->stop; \
+ else \
+ return -1; \
+ } \
+ if (r < Runeself) \
+ *t++ = r; \
+ else { \
+ _rune = r; \
+ t += runetochar(t, &_rune); \
+ } \
+ } while (0)
+
+#ifdef va_copy
+# define VA_COPY(a, b) va_copy(a, b)
+# define VA_END(a) va_end(a)
+#else
+# define VA_COPY(a, b) (a) = (b)
+# define VA_END(a)
+#endif
diff --git a/lib/libfmt/fmtfd.c b/lib/libfmt/fmtfd.c
@@ -0,0 +1,33 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * public routine for final flush of a formatting buffer
+ * to a file descriptor; returns total char count.
+ */
+int fmtfdflush(Fmt* f) {
+ if (__fmtFdFlush(f) <= 0)
+ return -1;
+ return f->nfmt;
+}
+
+/*
+ * initialize an output buffer for buffered printing
+ */
+int fmtfdinit(Fmt* f, int fd, char* buf, int size) {
+ f->runes = 0;
+ f->start = buf;
+ f->to = buf;
+ f->stop = buf + size;
+ f->flush = __fmtFdFlush;
+ f->farg = (void*) (uintptr_t) fd;
+ f->flags = 0;
+ f->nfmt = 0;
+ fmtlocaleinit(f, nil, nil, nil);
+ return 0;
+}
diff --git a/lib/libfmt/fmtfdflush.c b/lib/libfmt/fmtfdflush.c
@@ -0,0 +1,21 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <unistd.h>
+
+/*
+ * generic routine for flushing a formatting buffer
+ * to a file descriptor
+ */
+int __fmtFdFlush(Fmt* f) {
+ int n;
+
+ n = (char*) f->to - (char*) f->start;
+ if (n && write((uintptr) f->farg, f->start, n) != n)
+ return 0;
+ f->to = f->start;
+ return 1;
+}
diff --git a/lib/libfmt/fmtinstall.3 b/lib/libfmt/fmtinstall.3
@@ -0,0 +1,379 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.TH FMTINSTALL 3
+.SH NAME
+fmtinstall, dofmt, dorfmt, fmtprint, fmtvprint, fmtrune, fmtstrcpy, fmtrunestrcpy, fmtfdinit, fmtfdflush, fmtstrinit, fmtstrflush, runefmtstrinit, runefmtstrflush, errfmt \- support for user-defined print formats and output routines
+.SH SYNOPSIS
+.B #include <utf.h>
+.br
+.B #include <fmt.h>
+.PP
+.ft L
+.nf
+.ta \w' 'u +\w' 'u +\w' 'u +\w' 'u +\w' 'u
+typedef struct Fmt Fmt;
+struct Fmt{
+ uchar runes; /* output buffer is runes or chars? */
+ void *start; /* of buffer */
+ void *to; /* current place in the buffer */
+ void *stop; /* end of the buffer; overwritten if flush fails */
+ int (*flush)(Fmt*); /* called when to == stop */
+ void *farg; /* to make flush a closure */
+ int nfmt; /* num chars formatted so far */
+ va_list args; /* args passed to dofmt */
+ int r; /* % format Rune */
+ int width;
+ int prec;
+ ulong flags;
+};
+
+enum{
+ FmtWidth = 1,
+ FmtLeft = FmtWidth << 1,
+ FmtPrec = FmtLeft << 1,
+ FmtSharp = FmtPrec << 1,
+ FmtSpace = FmtSharp << 1,
+ FmtSign = FmtSpace << 1,
+ FmtZero = FmtSign << 1,
+ FmtUnsigned = FmtZero << 1,
+ FmtShort = FmtUnsigned << 1,
+ FmtLong = FmtShort << 1,
+ FmtVLong = FmtLong << 1,
+ FmtComma = FmtVLong << 1,
+
+ FmtFlag = FmtComma << 1
+};
+.fi
+.PP
+.B
+.ta \w'\fLchar* 'u
+
+.PP
+.B
+int fmtfdinit(Fmt *f, int fd, char *buf, int nbuf);
+.PP
+.B
+int fmtfdflush(Fmt *f);
+.PP
+.B
+int fmtstrinit(Fmt *f);
+.PP
+.B
+char* fmtstrflush(Fmt *f);
+.PP
+.B
+int runefmtstrinit(Fmt *f);
+.PP
+.B
+Rune* runefmtstrflush(Fmt *f);
+
+.PP
+.B
+int fmtinstall(int c, int (*fn)(Fmt*));
+.PP
+.B
+int dofmt(Fmt *f, char *fmt);
+.PP
+.B
+int dorfmt(Fmt*, Rune *fmt);
+.PP
+.B
+int fmtprint(Fmt *f, char *fmt, ...);
+.PP
+.B
+int fmtvprint(Fmt *f, char *fmt, va_list v);
+.PP
+.B
+int fmtrune(Fmt *f, int r);
+.PP
+.B
+int fmtstrcpy(Fmt *f, char *s);
+.PP
+.B
+int fmtrunestrcpy(Fmt *f, Rune *s);
+.PP
+.B
+int errfmt(Fmt *f);
+.SH DESCRIPTION
+The interface described here allows the construction of custom
+.IR print (3)
+verbs and output routines.
+In essence, they provide access to the workings of the formatted print code.
+.PP
+The
+.IR print (3)
+suite maintains its state with a data structure called
+.BR Fmt .
+A typical call to
+.IR print (3)
+or its relatives initializes a
+.B Fmt
+structure, passes it to subsidiary routines to process the output,
+and finishes by emitting any saved state recorded in the
+.BR Fmt .
+The details of the
+.B Fmt
+are unimportant to outside users, except insofar as the general
+design influences the interface.
+The
+.B Fmt
+records whether the output is in runes or bytes,
+the verb being processed, its precision and width,
+and buffering parameters.
+Most important, it also records a
+.I flush
+routine that the library will call if a buffer overflows.
+When printing to a file descriptor, the flush routine will
+emit saved characters and reset the buffer; when printing
+to an allocated string, it will resize the string to receive more output.
+The flush routine is nil when printing to fixed-size buffers.
+User code need never provide a flush routine; this is done internally
+by the library.
+.SS Custom output routines
+To write a custom output routine, such as an error handler that
+formats and prints custom error messages, the output sequence can be run
+from outside the library using the routines described here.
+There are two main cases: output to an open file descriptor
+and output to a string.
+.PP
+To write to a file descriptor, call
+.I fmtfdinit
+to initialize the local
+.B Fmt
+structure
+.IR f ,
+giving the file descriptor
+.IR fd ,
+the buffer
+.IR buf ,
+and its size
+.IR nbuf .
+Then call
+.IR fmtprint
+or
+.IR fmtvprint
+to generate the output.
+These behave like
+.B fprint
+(see
+.IR print (3))
+or
+.B vfprint
+except that the characters are buffered until
+.I fmtfdflush
+is called and the return value is either 0 or \-1.
+A typical example of this sequence appears in the Examples section.
+.PP
+The same basic sequence applies when outputting to an allocated string:
+call
+.I fmtstrinit
+to initialize the
+.BR Fmt ,
+then call
+.I fmtprint
+and
+.I fmtvprint
+to generate the output.
+Finally,
+.I fmtstrflush
+will return the allocated string, which should be freed after use.
+To output to a rune string, use
+.I runefmtstrinit
+and
+.IR runefmtstrflush .
+Regardless of the output style or type,
+.I fmtprint
+or
+.I fmtvprint
+generates the characters.
+.SS Custom format verbs
+.I Fmtinstall
+is used to install custom verbs and flags labeled by character
+.IR c ,
+which may be any non-zero Unicode character.
+.I Fn
+should be declared as
+.IP
+.EX
+int fn(Fmt*)
+.EE
+.PP
+.IB Fp ->r
+is the flag or verb character to cause
+.I fn
+to be called.
+In
+.IR fn ,
+.IB fp ->width ,
+.IB fp ->prec
+are the width and precision, and
+.IB fp ->flags
+the decoded flags for the verb (see
+.IR print (3)
+for a description of these items).
+The standard flag values are:
+.B FmtSign
+.RB ( + ),
+.B FmtLeft
+.RB ( - ),
+.B FmtSpace
+.RB ( '\ ' ),
+.B FmtSharp
+.RB ( # ),
+.B FmtComma
+.RB ( , ),
+.B FmtLong
+.RB ( l ),
+.B FmtShort
+.RB ( h ),
+.B FmtUnsigned
+.RB ( u ),
+and
+.B FmtVLong
+.RB ( ll ).
+The flag bits
+.B FmtWidth
+and
+.B FmtPrec
+identify whether a width and precision were specified.
+.PP
+.I Fn
+is passed a pointer to the
+.B Fmt
+structure recording the state of the output.
+If
+.IB fp ->r
+is a verb (rather than a flag),
+.I fn
+should use
+.B Fmt->args
+to fetch its argument from the list,
+then format it, and return zero.
+If
+.IB fp ->r
+is a flag,
+.I fn
+should return one.
+All interpretation of
+.IB fp ->width\f1,
+.IB fp ->prec\f1,
+and
+.IB fp-> flags
+is left up to the conversion routine.
+.I Fmtinstall
+returns 0 if the installation succeeds, \-1 if it fails.
+.PP
+.IR Fmtprint
+and
+.IR fmtvprint
+may be called to
+help prepare output in custom conversion routines.
+However, these functions clear the width, precision, and flags.
+Both functions return 0 for success and \-1 for failure.
+.PP
+The functions
+.I dofmt
+and
+.I dorfmt
+are the underlying formatters; they
+use the existing contents of
+.B Fmt
+and should be called only by sophisticated conversion routines.
+These routines return the number of characters (bytes of UTF or runes)
+produced.
+.PP
+Some internal functions may be useful to format primitive types.
+They honor the width, precision and flags as described in
+.IR print (3).
+.I Fmtrune
+formats a single character
+.BR r .
+.I Fmtstrcpy
+formats a string
+.BR s ;
+.I fmtrunestrcpy
+formats a rune string
+.BR s .
+.I Errfmt
+formats the system error string.
+All these routines return zero for successful execution.
+Conversion routines that call these functions will work properly
+regardless of whether the output is bytes or runes.
+.\" .PP
+.\" .IR 2c (1)
+.\" describes the C directive
+.\" .B #pragma
+.\" .B varargck
+.\" that can be used to provide type-checking for custom print verbs and output routines.
+.SH EXAMPLES
+This function prints an error message with a variable
+number of arguments and then quits.
+Compared to the corresponding example in
+.IR print (3),
+this version uses a smaller buffer, will never truncate
+the output message, but might generate multiple
+.B write
+system calls to produce its output.
+.IP
+.EX
+.ta 6n +6n +6n +6n +6n +6n +6n +6n +6n
+#pragma varargck argpos error 1
+
+void fatal(char *fmt, ...)
+{
+ Fmt f;
+ char buf[64];
+ va_list arg;
+
+ fmtfdinit(&f, 1, buf, sizeof buf);
+ fmtprint(&f, "fatal: ");
+ va_start(arg, fmt);
+ fmtvprint(&f, fmt, arg);
+ va_end(arg);
+ fmtprint(&f, "\en");
+ fmtfdflush(&f);
+ exits("fatal error");
+}
+.EE
+.PP
+This example adds a verb to print complex numbers.
+.IP
+.EX
+typedef
+struct {
+ double r, i;
+} Complex;
+
+#pragma varargck type "X" Complex
+
+int
+Xfmt(Fmt *f)
+{
+ Complex c;
+
+ c = va_arg(f->args, Complex);
+ return fmtprint(f, "(%g,%g)", c.r, c.i);
+}
+
+main(...)
+{
+ Complex x = (Complex){ 1.5, -2.3 };
+
+ fmtinstall('X', Xfmt);
+ print("x = %X\en", x);
+}
+.EE
+.SH SOURCE
+.B https://9fans.github.io/plan9port/unix
+.SH SEE ALSO
+.IR print (3),
+.IR utf (7)
+.SH DIAGNOSTICS
+These routines return negative numbers or nil for errors and set
+.IR errstr .
diff --git a/lib/libfmt/fmtlocale.c b/lib/libfmt/fmtlocale.c
@@ -0,0 +1,51 @@
+/* Copyright (c) 2004 Google Inc.; see LICENSE */
+
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * Fill in the internationalization stuff in the State structure.
+ * For nil arguments, provide the sensible defaults:
+ * decimal is a period
+ * thousands separator is a comma
+ * thousands are marked every three digits
+ */
+void fmtlocaleinit(Fmt* f, char* decimal, char* thousands, char* grouping) {
+ if (decimal == nil || decimal[0] == '\0')
+ decimal = ".";
+ if (thousands == nil)
+ thousands = ",";
+ if (grouping == nil)
+ grouping = "\3";
+ f->decimal = decimal;
+ f->thousands = thousands;
+ f->grouping = grouping;
+}
+
+/*
+ * We are about to emit a digit in e.g. %'d. If that digit would
+ * overflow a thousands (e.g.) grouping, tell the caller to emit
+ * the thousands separator. Always advance the digit counter
+ * and pointer into the grouping descriptor.
+ */
+int __needsep(int* ndig, char** grouping) {
+ int group;
+
+ (*ndig)++;
+ group = *(unsigned char*) *grouping;
+ /* CHAR_MAX means no further grouping. \0 means we got the empty string */
+ if (group == 0xFF || group == 0x7f || group == 0x00)
+ return 0;
+ if (*ndig > group) {
+ /* if we're at end of string, continue with this grouping; else advance */
+ if ((*grouping)[1] != '\0')
+ (*grouping)++;
+ *ndig = 1;
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib/libfmt/fmtlock.c b/lib/libfmt/fmtlock.c
@@ -0,0 +1,12 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+void __fmtlock(void) {
+}
+
+void __fmtunlock(void) {
+}
diff --git a/lib/libfmt/fmtnull.c b/lib/libfmt/fmtnull.c
@@ -0,0 +1,30 @@
+/* Copyright (c) 2004 Google Inc.; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * Absorb output without using resources.
+ */
+static Rune nullbuf[32];
+
+static int
+__fmtnullflush(Fmt* f) {
+ f->to = nullbuf;
+ f->nfmt = 0;
+ return 0;
+}
+
+int fmtnullinit(Fmt* f) {
+ memset(f, 0, sizeof *f);
+ f->runes = 1;
+ f->start = nullbuf;
+ f->to = nullbuf;
+ f->stop = nullbuf + nelem(nullbuf);
+ f->flush = __fmtnullflush;
+ fmtlocaleinit(f, nil, nil, nil);
+ return 0;
+}
diff --git a/lib/libfmt/fmtprint.c b/lib/libfmt/fmtprint.c
@@ -0,0 +1,34 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int fmtprint(Fmt* f, char* fmt, ...) {
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_COPY(va, f->args);
+ VA_END(f->args);
+ va_start(f->args, fmt);
+ n = dofmt(f, fmt);
+ va_end(f->args);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_COPY(f->args, va);
+ VA_END(va);
+ if (n >= 0)
+ return 0;
+ return n;
+}
diff --git a/lib/libfmt/fmtquote.c b/lib/libfmt/fmtquote.c
@@ -0,0 +1,245 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by __quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ * NUL in input
+ * count exceeded in input
+ * count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void __quotesetup(char* s, Rune* r, int nin, int nout, Quoteinfo* q, int sharp, int runesout) {
+ int w;
+ Rune c;
+
+ q->quoted = 0;
+ q->nbytesout = 0;
+ q->nrunesout = 0;
+ q->nbytesin = 0;
+ q->nrunesin = 0;
+ if (sharp || nin == 0 || (s && *s == '\0') || (r && *r == '\0')) {
+ if (nout < 2)
+ return;
+ q->quoted = 1;
+ q->nbytesout = 2;
+ q->nrunesout = 2;
+ }
+ for (; nin != 0; nin--) {
+ if (s)
+ w = chartorune(&c, s);
+ else {
+ c = *r;
+ w = runelen(c);
+ }
+
+ if (c == '\0')
+ break;
+ if (runesout) {
+ if (q->nrunesout + 1 > nout)
+ break;
+ } else {
+ if (q->nbytesout + w > nout)
+ break;
+ }
+
+ if ((c <= L' ') || (c == L'\'') || (fmtdoquote != nil && fmtdoquote(c))) {
+ if (!q->quoted) {
+ if (runesout) {
+ if (1 + q->nrunesout + 1 + 1 > nout) /* no room for quotes */
+ break;
+ } else {
+ if (1 + q->nbytesout + w + 1 > nout) /* no room for quotes */
+ break;
+ }
+ q->nrunesout += 2; /* include quotes */
+ q->nbytesout += 2; /* include quotes */
+ q->quoted = 1;
+ }
+ if (c == '\'') {
+ if (runesout) {
+ if (1 + q->nrunesout + 1 > nout) /* no room for quotes */
+ break;
+ } else {
+ if (1 + q->nbytesout + w > nout) /* no room for quotes */
+ break;
+ }
+ q->nbytesout++;
+ q->nrunesout++; /* quotes reproduce as two characters */
+ }
+ }
+
+ /* advance input */
+ if (s)
+ s += w;
+ else
+ r++;
+ q->nbytesin += w;
+ q->nrunesin++;
+
+ /* advance output */
+ q->nbytesout += w;
+ q->nrunesout++;
+
+#ifndef PLAN9PORT
+ /* ANSI requires precision in bytes, not Runes. */
+ nin -= w - 1; /* and then n-- in the loop */
+#endif
+ }
+}
+
+static int
+qstrfmt(char* sin, Rune* rin, Quoteinfo* q, Fmt* f) {
+ Rune r, *rm, *rme;
+ char *t, *s, *m, *me;
+ Rune *rt, *rs;
+ ulong fl;
+ int nc, w;
+
+ m = sin;
+ me = m + q->nbytesin;
+ rm = rin;
+ rme = rm + q->nrunesin;
+
+ fl = f->flags;
+ w = 0;
+ if (fl & FmtWidth)
+ w = f->width;
+ if (f->runes) {
+ if (!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ } else {
+ if (!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ t = (char*) f->to;
+ s = (char*) f->stop;
+ rt = (Rune*) f->to;
+ rs = (Rune*) f->stop;
+ if (f->runes)
+ FMTRCHAR(f, rt, rs, '\'');
+ else
+ FMTRUNE(f, t, s, '\'');
+ for (nc = q->nrunesin; nc > 0; nc--) {
+ if (sin) {
+ r = *(uchar*) m;
+ if (r < Runeself)
+ m++;
+ else if ((me - m) >= UTFmax || fullrune(m, me - m))
+ m += chartorune(&r, m);
+ else
+ break;
+ } else {
+ if (rm >= rme)
+ break;
+ r = *(uchar*) rm++;
+ }
+ if (f->runes) {
+ FMTRCHAR(f, rt, rs, r);
+ if (r == '\'')
+ FMTRCHAR(f, rt, rs, r);
+ } else {
+ FMTRUNE(f, t, s, r);
+ if (r == '\'')
+ FMTRUNE(f, t, s, r);
+ }
+ }
+
+ if (f->runes) {
+ FMTRCHAR(f, rt, rs, '\'');
+ USED(rs);
+ f->nfmt += rt - (Rune*) f->to;
+ f->to = rt;
+ if (fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ } else {
+ FMTRUNE(f, t, s, '\'');
+ USED(s);
+ f->nfmt += t - (char*) f->to;
+ f->to = t;
+ if (fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int __quotestrfmt(int runesin, Fmt* f) {
+ int nin, outlen;
+ Rune* r;
+ char* s;
+ Quoteinfo q;
+
+ nin = -1;
+ if (f->flags & FmtPrec)
+ nin = f->prec;
+ if (runesin) {
+ r = va_arg(f->args, Rune*);
+ s = nil;
+ } else {
+ s = va_arg(f->args, char*);
+ r = nil;
+ }
+ if (!s && !r)
+ return __fmtcpy(f, (void*) "<nil>", 5, 5);
+
+ if (f->flush)
+ outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
+ else if (f->runes)
+ outlen = (Rune*) f->stop - (Rune*) f->to;
+ else
+ outlen = (char*) f->stop - (char*) f->to;
+
+ __quotesetup(s, r, nin, outlen, &q, f->flags & FmtSharp, f->runes);
+ /*print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); */
+
+ if (runesin) {
+ if (!q.quoted)
+ return __fmtrcpy(f, r, q.nrunesin);
+ return qstrfmt(nil, r, &q, f);
+ }
+
+ if (!q.quoted)
+ return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
+ return qstrfmt(s, nil, &q, f);
+}
+
+int quotestrfmt(Fmt* f) {
+ return __quotestrfmt(0, f);
+}
+
+int quoterunestrfmt(Fmt* f) {
+ return __quotestrfmt(1, f);
+}
+
+void quotefmtinstall(void) {
+ fmtinstall('q', quotestrfmt);
+ fmtinstall('Q', quoterunestrfmt);
+}
+
+int __needsquotes(char* s, int* quotelenp) {
+ Quoteinfo q;
+
+ __quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nbytesout;
+
+ return q.quoted;
+}
+
+int __runeneedsquotes(Rune* r, int* quotelenp) {
+ Quoteinfo q;
+
+ __quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nrunesout;
+
+ return q.quoted;
+}
diff --git a/lib/libfmt/fmtrune.c b/lib/libfmt/fmtrune.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int fmtrune(Fmt* f, int r) {
+ Rune* rt;
+ char* t;
+ int n;
+
+ if (f->runes) {
+ rt = (Rune*) f->to;
+ FMTRCHAR(f, rt, f->stop, r);
+ f->to = rt;
+ n = 1;
+ } else {
+ t = (char*) f->to;
+ FMTRUNE(f, t, f->stop, r);
+ n = t - (char*) f->to;
+ f->to = t;
+ }
+ f->nfmt += n;
+ return 0;
+}
diff --git a/lib/libfmt/fmtstr.c b/lib/libfmt/fmtstr.c
@@ -0,0 +1,15 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+char* fmtstrflush(Fmt* f) {
+ if (f->start == nil)
+ return nil;
+ *(char*) f->to = '\0';
+ f->to = f->start;
+ return (char*) f->start;
+}
diff --git a/lib/libfmt/fmtvprint.c b/lib/libfmt/fmtvprint.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int fmtvprint(Fmt* f, char* fmt, va_list args) {
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_COPY(va, f->args);
+ VA_END(f->args);
+ VA_COPY(f->args, args);
+ n = dofmt(f, fmt);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ VA_END(f->args);
+ VA_COPY(f->args, va);
+ VA_END(va);
+ if (n >= 0)
+ return 0;
+ return n;
+}
diff --git a/lib/libfmt/fprint.c b/lib/libfmt/fprint.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+int fprint(int fd, char* fmt, ...) {
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(fd, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib/libfmt/nan.h b/lib/libfmt/nan.h
@@ -0,0 +1,4 @@
+extern double __NaN(void);
+extern double __Inf(int);
+extern int __isNaN(double);
+extern int __isInf(double, int);
diff --git a/lib/libfmt/nan64.c b/lib/libfmt/nan64.c
@@ -0,0 +1,71 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+
+/*
+ * 64-bit IEEE not-a-number routines.
+ * This is big/little-endian portable assuming that
+ * the 64-bit doubles and 64-bit integers have the
+ * same byte ordering.
+ */
+
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <assert.h>
+
+static uvlong uvnan = ((uvlong) 0x7FF00000 << 32) | 0x00000001;
+static uvlong uvinf = ((uvlong) 0x7FF00000 << 32) | 0x00000000;
+static uvlong uvneginf = ((uvlong) 0xFFF00000 << 32) | 0x00000000;
+
+/* gcc sees through the obvious casts. */
+static uvlong
+d2u(double d) {
+ union {
+ uvlong v;
+ double d;
+ } u;
+ assert(sizeof(u.d) == sizeof(u.v));
+ u.d = d;
+ return u.v;
+}
+
+static double
+u2d(uvlong v) {
+ union {
+ uvlong v;
+ double d;
+ } u;
+ assert(sizeof(u.d) == sizeof(u.v));
+ u.v = v;
+ return u.d;
+}
+
+double
+__NaN(void) {
+ return u2d(uvnan);
+}
+
+int __isNaN(double d) {
+ uvlong x;
+
+ x = d2u(d);
+ /* IEEE 754: exponent bits 0x7FF and non-zero mantissa */
+ return (x & uvinf) == uvinf && (x & ~uvneginf) != 0;
+}
+
+double
+__Inf(int sign) {
+ return u2d(sign < 0 ? uvneginf : uvinf);
+}
+
+int __isInf(double d, int sign) {
+ uvlong x;
+
+ x = d2u(d);
+ if (sign == 0)
+ return x == uvinf || x == uvneginf;
+ else if (sign > 0)
+ return x == uvinf;
+ else
+ return x == uvneginf;
+}
diff --git a/lib/libfmt/plan9.h b/lib/libfmt/plan9.h
@@ -0,0 +1,37 @@
+#include <inttypes.h>
+
+/*
+ * compiler directive on Plan 9
+ */
+#ifndef USED
+# define USED(x) (void) (x)
+#endif
+
+/*
+ * easiest way to make sure these are defined
+ */
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+#define uintptr _fmtuintptr
+
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned long long uvlong;
+typedef long long vlong;
+typedef uintptr_t uintptr;
+
+/*
+ * nil cannot be ((void*)0) on ANSI C,
+ * because it is used for function pointers
+ */
+#undef nil
+#define nil 0
+
+#undef nelem
+#define nelem(x) (sizeof(x) / sizeof(x)[0])
diff --git a/lib/libfmt/pow10.c b/lib/libfmt/pow10.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * this table might overflow 127-bit exponent representations.
+ * in that case, truncate it after 1.0e38.
+ * it is important to get all one can from this
+ * routine since it is used in atof to scale numbers.
+ * the presumption is that C converts fp numbers better
+ * than multipication of lower powers of 10.
+ */
+
+static double tab[] = {
+ 1.0e0,
+ 1.0e1,
+ 1.0e2,
+ 1.0e3,
+ 1.0e4,
+ 1.0e5,
+ 1.0e6,
+ 1.0e7,
+ 1.0e8,
+ 1.0e9,
+ 1.0e10,
+ 1.0e11,
+ 1.0e12,
+ 1.0e13,
+ 1.0e14,
+ 1.0e15,
+ 1.0e16,
+ 1.0e17,
+ 1.0e18,
+ 1.0e19,
+ 1.0e20,
+ 1.0e21,
+ 1.0e22,
+ 1.0e23,
+ 1.0e24,
+ 1.0e25,
+ 1.0e26,
+ 1.0e27,
+ 1.0e28,
+ 1.0e29,
+ 1.0e30,
+ 1.0e31,
+ 1.0e32,
+ 1.0e33,
+ 1.0e34,
+ 1.0e35,
+ 1.0e36,
+ 1.0e37,
+ 1.0e38,
+ 1.0e39,
+ 1.0e40,
+ 1.0e41,
+ 1.0e42,
+ 1.0e43,
+ 1.0e44,
+ 1.0e45,
+ 1.0e46,
+ 1.0e47,
+ 1.0e48,
+ 1.0e49,
+ 1.0e50,
+ 1.0e51,
+ 1.0e52,
+ 1.0e53,
+ 1.0e54,
+ 1.0e55,
+ 1.0e56,
+ 1.0e57,
+ 1.0e58,
+ 1.0e59,
+ 1.0e60,
+ 1.0e61,
+ 1.0e62,
+ 1.0e63,
+ 1.0e64,
+ 1.0e65,
+ 1.0e66,
+ 1.0e67,
+ 1.0e68,
+ 1.0e69,
+};
+
+double
+__fmtpow10(int n) {
+ int m;
+
+ if (n < 0) {
+ n = -n;
+ if (n < (int) (sizeof(tab) / sizeof(tab[0])))
+ return 1 / tab[n];
+ m = n / 2;
+ return __fmtpow10(-m) * __fmtpow10(m - n);
+ }
+ if (n < (int) (sizeof(tab) / sizeof(tab[0])))
+ return tab[n];
+ m = n / 2;
+ return __fmtpow10(m) * __fmtpow10(n - m);
+}
diff --git a/lib/libfmt/print.3 b/lib/libfmt/print.3
@@ -0,0 +1,482 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.\" diffs from /usr/local/plan9/man/man3/print.3:
+.\"
+.\" - include different headers
+.\" - drop reference to bio(3)
+.\" - change exits to exit
+.\" - text about unsigned verbs
+.\" - source pointer
+.\"
+.TH PRINT 3
+.SH NAME
+print, fprint, sprint, snprint, seprint, smprint, runesprint, runesnprint, runeseprint, runesmprint, vfprint, vsnprint, vseprint, vsmprint, runevsnprint, runevseprint, runevsmprint \- print formatted output
+.SH SYNOPSIS
+.B #include <utf.h>
+.PP
+.B #include <fmt.h>
+.PP
+.ta \w'\fLchar* 'u
+.B
+int print(char *format, ...)
+.PP
+.B
+int fprint(int fd, char *format, ...)
+.PP
+.B
+int sprint(char *s, char *format, ...)
+.PP
+.B
+int snprint(char *s, int len, char *format, ...)
+.PP
+.B
+char* seprint(char *s, char *e, char *format, ...)
+.PP
+.B
+char* smprint(char *format, ...)
+.PP
+.B
+int runesprint(Rune *s, char *format, ...)
+.PP
+.B
+int runesnprint(Rune *s, int len, char *format, ...)
+.PP
+.B
+Rune* runeseprint(Rune *s, Rune *e, char *format, ...)
+.PP
+.B
+Rune* runesmprint(char *format, ...)
+.PP
+.B
+int vfprint(int fd, char *format, va_list v)
+.PP
+.B
+int vsnprint(char *s, int len, char *format, va_list v)
+.PP
+.B
+char* vseprint(char *s, char *e, char *format, va_list v)
+.PP
+.B
+char* vsmprint(char *format, va_list v)
+.PP
+.B
+int runevsnprint(Rune *s, int len, char *format, va_list v)
+.PP
+.B
+Rune* runevseprint(Rune *s, Rune *e, char *format, va_list v)
+.PP
+.B
+Rune* runevsmprint(Rune *format, va_list v)
+.PP
+.B
+.SH DESCRIPTION
+.I Print
+writes text to the standard output.
+.I Fprint
+writes to the named output
+file descriptor:
+a buffered form
+is described in
+.IR bio (3).
+.I Sprint
+places text
+followed by the NUL character
+.RB ( \e0 )
+in consecutive bytes starting at
+.IR s ;
+it is the user's responsibility to ensure that
+enough storage is available.
+Each function returns the number of bytes
+transmitted (not including the NUL
+in the case of
+.IR sprint ),
+or
+a negative value if an output error was encountered.
+.PP
+.I Snprint
+is like
+.IR sprint ,
+but will not place more than
+.I len
+bytes in
+.IR s .
+Its result is always NUL-terminated and holds the maximal
+number of complete UTF-8 characters that can fit.
+.I Seprint
+is like
+.IR snprint ,
+except that the end is indicated by a pointer
+.I e
+rather than a count and the return value points to the terminating NUL of the
+resulting string.
+.I Smprint
+is like
+.IR sprint ,
+except that it prints into and returns a string of the required length, which is
+allocated by
+.IR malloc (3).
+.PP
+The routines
+.IR runesprint ,
+.IR runesnprint ,
+.IR runeseprint ,
+and
+.I runesmprint
+are the same as
+.IR sprint ,
+.IR snprint ,
+.IR seprint
+and
+.I smprint
+except that their output is rune strings instead of byte strings.
+.PP
+Finally, the routines
+.IR vfprint ,
+.IR vsnprint ,
+.IR vseprint ,
+.IR vsmprint ,
+.IR runevsnprint ,
+.IR runevseprint ,
+and
+.I runevsmprint
+are like their
+.BR v-less
+relatives except they take as arguments a
+.B va_list
+parameter, so they can be called within a variadic function.
+The Example section shows a representative usage.
+.PP
+Each of these functions
+converts, formats, and prints its
+trailing arguments
+under control of a
+.IR format
+string.
+The
+format
+contains two types of objects:
+plain characters, which are simply copied to the
+output stream,
+and conversion specifications,
+each of which results in fetching of
+zero or more
+arguments.
+The results are undefined if there are arguments of the
+wrong type or too few
+arguments for the format.
+If the format is exhausted while
+arguments remain, the excess
+is ignored.
+.PP
+Each conversion specification has the following format:
+.IP
+.B "% [flags] verb
+.PP
+The verb is a single character and each flag is a single character or a
+(decimal) numeric string.
+Up to two numeric strings may be used;
+the first is called
+.IR width ,
+the second
+.IR precision .
+A period can be used to separate them, and if the period is
+present then
+.I width
+and
+.I precision
+are taken to be zero if missing, otherwise they are `omitted'.
+Either or both of the numbers may be replaced with the character
+.BR * ,
+meaning that the actual number will be obtained from the argument list
+as an integer.
+The flags and numbers are arguments to
+the
+.I verb
+described below.
+.PP
+The numeric verbs
+.BR d ,
+.BR i ,
+.BR u ,
+.BR o ,
+.BR b ,
+.BR x ,
+and
+.B X
+format their arguments in decimal, decimal,
+unsigned decimal, octal, binary, hexadecimal, and upper case hexadecimal.
+Each interprets the flags
+.BR 0 ,
+.BR h ,
+.BR hh ,
+.BR l ,
+.BR + ,
+.BR - ,
+.BR , ,
+and
+.B #
+to mean pad with zeros,
+short, byte, long, always print a sign, left justified, commas every three digits,
+and alternate format.
+Also, a space character in the flag
+position is like
+.BR + ,
+but prints a space instead of a plus sign for non-negative values.
+If neither
+short nor long is specified,
+then the argument is an
+.BR int .
+If an unsigned verb is specified,
+then the argument is interpreted as a
+positive number and no sign is output;
+space and
+.B +
+flags are ignored for unsigned verbs.
+If two
+.B l
+flags are given,
+then the argument is interpreted as a
+.B vlong
+(usually an 8-byte, sometimes a 4-byte integer).
+If
+.I precision
+is not omitted, the number is padded on the left with zeros
+until at least
+.I precision
+digits appear.
+If
+.I precision
+is explicitly 0, and the number is 0,
+no digits are generated, and alternate formatting
+does not apply.
+Then, if alternate format is specified,
+for
+.B o
+conversion, the number is preceded by a
+.B 0
+if it doesn't already begin with one.
+For non-zero numbers and
+.B x
+conversion, the number is preceded by
+.BR 0x ;
+for
+.B X
+conversion, the number is preceded by
+.BR 0X .
+Finally, if
+.I width
+is not omitted, the number is padded on the left (or right, if
+left justification is specified) with enough blanks to
+make the field at least
+.I width
+characters long.
+.PP
+The floating point verbs
+.BR f ,
+.BR e ,
+.BR E ,
+.BR g ,
+and
+.B G
+take a
+.B double
+argument.
+Each interprets the flags
+.BR 0 ,
+.BR L
+.BR + ,
+.BR - ,
+and
+.B #
+to mean pad with zeros,
+long double argument,
+always print a sign,
+left justified,
+and
+alternate format.
+.I Width
+is the minimum field width and,
+if the converted value takes up less than
+.I width
+characters, it is padded on the left (or right, if `left justified')
+with spaces.
+.I Precision
+is the number of digits that are converted after the decimal place for
+.BR e ,
+.BR E ,
+and
+.B f
+conversions,
+and
+.I precision
+is the maximum number of significant digits for
+.B g
+and
+.B G
+conversions.
+The
+.B f
+verb produces output of the form
+.RB [ - ] digits [ .digits\fR].
+.B E
+conversion appends an exponent
+.BR E [ - ] digits ,
+and
+.B e
+conversion appends an exponent
+.BR e [ - ] digits .
+The
+.B g
+verb will output the argument in either
+.B e
+or
+.B f
+with the goal of producing the smallest output.
+Also, trailing zeros are omitted from the fraction part of
+the output, and a trailing decimal point appears only if it is followed
+by a digit.
+The
+.B G
+verb is similar, but uses
+.B E
+format instead of
+.BR e .
+When alternate format is specified, the result will always contain a decimal point,
+and for
+.B g
+and
+.B G
+conversions, trailing zeros are not removed.
+.PP
+The
+.B s
+verb copies a string
+(pointer to
+.BR char )
+to the output.
+The number of characters copied
+.RI ( n )
+is the minimum
+of the size of the string and
+.IR precision .
+These
+.I n
+characters are justified within a field of
+.I width
+characters as described above.
+If a
+.I precision
+is given, it is safe for the string not to be nul-terminated
+as long as it is at least
+.I precision
+characters (not bytes!) long.
+The
+.B S
+verb is similar, but it interprets its pointer as an array
+of runes (see
+.IR utf (7));
+the runes are converted to
+.SM UTF
+before output.
+.PP
+The
+.B c
+verb copies a single
+.B char
+(promoted to
+.BR int )
+justified within a field of
+.I width
+characters as described above.
+The
+.B C
+verb is similar, but works on runes.
+.PP
+The
+.B p
+verb formats a pointer value.
+At the moment, it is a synonym for
+.BR x ,
+but that will change if pointers and integers are different sizes.
+.PP
+The
+.B r
+verb takes no arguments; it copies the error string returned by a call to
+.IR strerror (3)
+with an argument of
+.IR errno.
+.PP
+Custom verbs may be installed using
+.IR fmtinstall (3).
+.SH EXAMPLE
+This function prints an error message with a variable
+number of arguments and then quits.
+.IP
+.EX
+.ta 6n +6n +6n
+void fatal(char *msg, ...)
+{
+ char buf[1024], *out;
+ va_list arg;
+
+ out = seprint(buf, buf+sizeof buf, "Fatal error: ");
+ va_start(arg, msg);
+ out = vseprint(out, buf+sizeof buf, msg, arg);
+ va_end(arg);
+ write(2, buf, out-buf);
+ exit(1);
+}
+.EE
+.SH SOURCE
+.B https://9fans.github.io/plan9port/unix
+.SH SEE ALSO
+.IR fmtinstall (3),
+.IR fprintf (3),
+.IR utf (7)
+.SH DIAGNOSTICS
+Routines that write to a file descriptor or call
+.IR malloc
+set
+.IR errstr .
+.SH BUGS
+The formatting is close to that specified for ANSI
+.IR fprintf (3);
+the main difference is that
+.B b
+and
+.B r
+are not in ANSI and some
+.B C9X
+verbs and syntax are missing.
+Also, and distinctly not a bug,
+.I print
+and friends generate
+.SM UTF
+rather than
+.SM ASCII.
+.PP
+There is no
+.IR runeprint ,
+.IR runefprint ,
+etc. because runes are byte-order dependent and should not be written directly to a file; use the
+UTF output of
+.I print
+or
+.I fprint
+instead.
+Also,
+.I sprint
+is deprecated for safety reasons; use
+.IR snprint ,
+.IR seprint ,
+or
+.I smprint
+instead.
+Safety also precludes the existence of
+.IR runesprint .
diff --git a/lib/libfmt/print.c b/lib/libfmt/print.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+int print(char* fmt, ...) {
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(1, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib/libfmt/runefmtstr.c b/lib/libfmt/runefmtstr.c
@@ -0,0 +1,15 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+Rune* runefmtstrflush(Fmt* f) {
+ if (f->start == nil)
+ return nil;
+ *(Rune*) f->to = '\0';
+ f->to = f->start;
+ return f->start;
+}
diff --git a/lib/libfmt/runeseprint.c b/lib/libfmt/runeseprint.c
@@ -0,0 +1,17 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runeseprint(Rune* buf, Rune* e, char* fmt, ...) {
+ Rune* p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = runevseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib/libfmt/runesmprint.c b/lib/libfmt/runesmprint.c
@@ -0,0 +1,17 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runesmprint(char* fmt, ...) {
+ va_list args;
+ Rune* p;
+
+ va_start(args, fmt);
+ p = runevsmprint(fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib/libfmt/runesnprint.c b/lib/libfmt/runesnprint.c
@@ -0,0 +1,17 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int runesnprint(Rune* buf, int len, char* fmt, ...) {
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = runevsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib/libfmt/runesprint.c b/lib/libfmt/runesprint.c
@@ -0,0 +1,17 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int runesprint(Rune* buf, char* fmt, ...) {
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = runevsnprint(buf, 256, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib/libfmt/runevseprint.c b/lib/libfmt/runevseprint.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runevseprint(Rune* buf, Rune* e, char* fmt, va_list args) {
+ Fmt f;
+
+ if (e <= buf)
+ return nil;
+ f.runes = 1;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args, args);
+ fmtlocaleinit(&f, nil, nil, nil);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(Rune*) f.to = '\0';
+ return (Rune*) f.to;
+}
diff --git a/lib/libfmt/runevsmprint.c b/lib/libfmt/runevsmprint.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+/*
+ * Plan 9 port version must include libc.h in order to
+ * get Plan 9 debugging malloc, which sometimes returns
+ * different pointers than the standard malloc.
+ */
+#ifdef PLAN9PORT
+# include "fmtdef.h"
+
+# include <libc.h>
+# include <u.h>
+#else
+# include "fmt.h"
+# include "fmtdef.h"
+# include "plan9.h"
+
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+static int
+runeFmtStrFlush(Fmt* f) {
+ Rune* s;
+ int n;
+
+ if (f->start == nil)
+ return 0;
+ n = (uintptr) f->farg;
+ n *= 2;
+ s = (Rune*) f->start;
+ f->start = realloc(s, sizeof(Rune) * n);
+ if (f->start == nil) {
+ f->farg = nil;
+ f->to = nil;
+ f->stop = nil;
+ return 0;
+ }
+ f->farg = (void*) (uintptr) n;
+ f->to = (Rune*) f->start + ((Rune*) f->to - s);
+ f->stop = (Rune*) f->start + n - 1;
+ return 1;
+}
+
+int runefmtstrinit(Fmt* f) {
+ int n;
+
+ memset(f, 0, sizeof *f);
+ f->runes = 1;
+ n = 32;
+ f->start = malloc(sizeof(Rune) * n);
+ if (f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (Rune*) f->start + n - 1;
+ f->flush = runeFmtStrFlush;
+ f->farg = (void*) (uintptr) n;
+ f->nfmt = 0;
+ fmtlocaleinit(f, nil, nil, nil);
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+Rune* runevsmprint(char* fmt, va_list args) {
+ Fmt f;
+ int n;
+
+ if (runefmtstrinit(&f) < 0)
+ return nil;
+ VA_COPY(f.args, args);
+ n = dofmt(&f, fmt);
+ VA_END(f.args);
+ if (f.start == nil)
+ return nil;
+ if (n < 0) {
+ free(f.start);
+ return nil;
+ }
+ *(Rune*) f.to = '\0';
+ return (Rune*) f.start;
+}
diff --git a/lib/libfmt/runevsnprint.c b/lib/libfmt/runevsnprint.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int runevsnprint(Rune* buf, int len, char* fmt, va_list args) {
+ Fmt f;
+
+ if (len <= 0)
+ return -1;
+ f.runes = 1;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args, args);
+ fmtlocaleinit(&f, nil, nil, nil);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(Rune*) f.to = '\0';
+ return (Rune*) f.to - buf;
+}
diff --git a/lib/libfmt/seprint.c b/lib/libfmt/seprint.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+char* seprint(char* buf, char* e, char* fmt, ...) {
+ char* p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = vseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib/libfmt/smprint.c b/lib/libfmt/smprint.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+char* smprint(char* fmt, ...) {
+ va_list args;
+ char* p;
+
+ va_start(args, fmt);
+ p = vsmprint(fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib/libfmt/snprint.c b/lib/libfmt/snprint.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+int snprint(char* buf, int len, char* fmt, ...) {
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib/libfmt/sprint.c b/lib/libfmt/sprint.c
@@ -0,0 +1,29 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <fmt.h>
+#include <stdarg.h>
+
+int sprint(char* buf, char* fmt, ...) {
+ int n;
+ uint len;
+ va_list args;
+
+ len = 1 << 30; /* big number, but sprint is deprecated anyway */
+ /*
+ * on PowerPC, the stack is near the top of memory, so
+ * we must be sure not to overflow a 32-bit pointer.
+ *
+ * careful! gcc-4.2 assumes buf+len < buf can never be true and
+ * optimizes the test away. casting to uintptr works around this bug.
+ */
+ if ((uintptr) buf + len < (uintptr) buf)
+ len = -(uintptr) buf - 1;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib/libfmt/strtod.c b/lib/libfmt/strtod.c
@@ -0,0 +1,511 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "common.h"
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+static ulong
+umuldiv(ulong a, ulong b, ulong c) {
+ double d;
+
+ d = ((double) a * (double) b) / (double) c;
+ if (d >= 4294967295.)
+ d = 4294967295.;
+ return (ulong) d;
+}
+
+/*
+ * This routine will convert to arbitrary precision
+ * floating point entirely in multi-precision fixed.
+ * The answer is the closest floating point number to
+ * the given decimal number. Exactly half way are
+ * rounded ala ieee rules.
+ * Method is to scale input decimal between .500 and .999...
+ * with external power of 2, then binary search for the
+ * closest mantissa to this decimal number.
+ * Nmant is is the required precision. (53 for ieee dp)
+ * Nbits is the max number of bits/word. (must be <= 28)
+ * Prec is calculated - the number of words of fixed mantissa.
+ */
+enum {
+ Nbits = 28, /* bits safely represented in a ulong */
+ Nmant = 53, /* bits of precision required */
+ Prec = (Nmant + Nbits + 1) / Nbits, /* words of Nbits each to represent mantissa */
+ Sigbit = 1 << (Prec * Nbits - Nmant), /* first significant bit of Prec-th word */
+ Ndig = 1500,
+ One = (ulong) (1 << Nbits),
+ Half = (ulong) (One >> 1),
+ Maxe = 310,
+
+ Fsign = 1 << 0, /* found - */
+ Fesign = 1 << 1, /* found e- */
+ Fdpoint = 1 << 2, /* found . */
+
+ S0 = 0, /* _ _S0 +S1 #S2 .S3 */
+ S1, /* _+ #S2 .S3 */
+ S2, /* _+# #S2 .S4 eS5 */
+ S3, /* _+. #S4 */
+ S4, /* _+#.# #S4 eS5 */
+ S5, /* _+#.#e +S6 #S7 */
+ S6, /* _+#.#e+ #S7 */
+ S7 /* _+#.#e+# #S7 */
+};
+
+static int xcmp(char*, char*);
+static int fpcmp(char*, ulong*);
+static void frnorm(ulong*);
+static void divascii(char*, int*, int*, int*);
+static void mulascii(char*, int*, int*, int*);
+
+typedef struct Tab Tab;
+struct Tab {
+ int bp;
+ int siz;
+ char* cmp;
+};
+
+double
+fmtstrtod(const char* as, char** aas) {
+ int na, ex, dp, bp, c, i, flag, state;
+ ulong low[Prec], hig[Prec], mid[Prec];
+ double d;
+ char * s, a[Ndig];
+
+ flag = 0; /* Fsign, Fesign, Fdpoint */
+ na = 0; /* number of digits of a[] */
+ dp = 0; /* na of decimal point */
+ ex = 0; /* exonent */
+
+ state = S0;
+ for (s = (char*) as;; s++) {
+ c = *s;
+ if (c >= '0' && c <= '9') {
+ switch (state) {
+ case S0:
+ case S1:
+ case S2:
+ state = S2;
+ break;
+ case S3:
+ case S4:
+ state = S4;
+ break;
+
+ case S5:
+ case S6:
+ case S7:
+ state = S7;
+ ex = ex * 10 + (c - '0');
+ continue;
+ }
+ if (na == 0 && c == '0') {
+ dp--;
+ continue;
+ }
+ if (na < Ndig - 50)
+ a[na++] = c;
+ continue;
+ }
+ switch (c) {
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ if (state == S0)
+ continue;
+ break;
+ case '-':
+ if (state == S0)
+ flag |= Fsign;
+ else
+ flag |= Fesign;
+ FALLTHROUGH;
+ case '+':
+ if (state == S0)
+ state = S1;
+ else if (state == S5)
+ state = S6;
+ else
+ break; /* syntax */
+ continue;
+ case '.':
+ flag |= Fdpoint;
+ dp = na;
+ if (state == S0 || state == S1) {
+ state = S3;
+ continue;
+ }
+ if (state == S2) {
+ state = S4;
+ continue;
+ }
+ break;
+ case 'e':
+ case 'E':
+ if (state == S2 || state == S4) {
+ state = S5;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+
+ /*
+ * clean up return char-pointer
+ */
+ switch (state) {
+ case S0:
+ if (xcmp(s, "nan") == 0) {
+ if (aas != nil)
+ *aas = s + 3;
+ goto retnan;
+ }
+ FALLTHROUGH;
+ case S1:
+ if (xcmp(s, "infinity") == 0) {
+ if (aas != nil)
+ *aas = s + 8;
+ goto retinf;
+ }
+ if (xcmp(s, "inf") == 0) {
+ if (aas != nil)
+ *aas = s + 3;
+ goto retinf;
+ }
+ FALLTHROUGH;
+ case S3:
+ if (aas != nil)
+ *aas = (char*) as;
+ goto ret0; /* no digits found */
+ FALLTHROUGH;
+ case S6:
+ s--; /* back over +- */
+ FALLTHROUGH;
+ case S5:
+ s--; /* back over e */
+ break;
+ }
+ if (aas != nil)
+ *aas = s;
+
+ if (flag & Fdpoint)
+ while (na > 0 && a[na - 1] == '0')
+ na--;
+ if (na == 0)
+ goto ret0; /* zero */
+ a[na] = 0;
+ if (!(flag & Fdpoint))
+ dp = na;
+ if (flag & Fesign)
+ ex = -ex;
+ dp += ex;
+ if (dp < -Maxe) {
+ errno = ERANGE;
+ goto ret0; /* underflow by exp */
+ } else if (dp > +Maxe)
+ goto retinf; /* overflow by exp */
+
+ /*
+ * normalize the decimal ascii number
+ * to range .[5-9][0-9]* e0
+ */
+ bp = 0; /* binary exponent */
+ while (dp > 0)
+ divascii(a, &na, &dp, &bp);
+ while (dp < 0 || a[0] < '5')
+ mulascii(a, &na, &dp, &bp);
+
+ /* close approx by naive conversion */
+ mid[0] = 0;
+ mid[1] = 1;
+ for (i = 0; (c = a[i]) != '\0'; i++) {
+ mid[0] = mid[0] * 10 + (c - '0');
+ mid[1] = mid[1] * 10;
+ if (i >= 8)
+ break;
+ }
+ low[0] = umuldiv(mid[0], One, mid[1]);
+ hig[0] = umuldiv(mid[0] + 1, One, mid[1]);
+ for (i = 1; i < Prec; i++) {
+ low[i] = 0;
+ hig[i] = One - 1;
+ }
+
+ /* binary search for closest mantissa */
+ for (;;) {
+ /* mid = (hig + low) / 2 */
+ c = 0;
+ for (i = 0; i < Prec; i++) {
+ mid[i] = hig[i] + low[i];
+ if (c)
+ mid[i] += One;
+ c = mid[i] & 1;
+ mid[i] >>= 1;
+ }
+ frnorm(mid);
+
+ /* compare */
+ c = fpcmp(a, mid);
+ if (c > 0) {
+ c = 1;
+ for (i = 0; i < Prec; i++)
+ if (low[i] != mid[i]) {
+ c = 0;
+ low[i] = mid[i];
+ }
+ if (c)
+ break; /* between mid and hig */
+ continue;
+ }
+ if (c < 0) {
+ for (i = 0; i < Prec; i++)
+ hig[i] = mid[i];
+ continue;
+ }
+
+ /* only hard part is if even/odd roundings wants to go up */
+ c = mid[Prec - 1] & (Sigbit - 1);
+ if (c == Sigbit / 2 && (mid[Prec - 1] & Sigbit) == 0)
+ mid[Prec - 1] -= c;
+ break; /* exactly mid */
+ }
+
+ /* normal rounding applies */
+ c = mid[Prec - 1] & (Sigbit - 1);
+ mid[Prec - 1] -= c;
+ if (c >= Sigbit / 2) {
+ mid[Prec - 1] += Sigbit;
+ frnorm(mid);
+ }
+ goto out;
+
+ret0:
+ return 0;
+
+retnan:
+ return __NaN();
+
+retinf:
+ /*
+ * Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */
+ errno = ERANGE;
+ if (flag & Fsign)
+ return -HUGE_VAL;
+ return HUGE_VAL;
+
+out:
+ d = 0;
+ for (i = 0; i < Prec; i++)
+ d = d * One + mid[i];
+ if (flag & Fsign)
+ d = -d;
+ d = ldexp(d, bp - Prec * Nbits);
+ if (d == 0) { /* underflow */
+ errno = ERANGE;
+ }
+ return d;
+}
+
+static void
+frnorm(ulong* f) {
+ int i, c;
+
+ c = 0;
+ for (i = Prec - 1; i > 0; i--) {
+ f[i] += c;
+ c = f[i] >> Nbits;
+ f[i] &= One - 1;
+ }
+ f[0] += c;
+}
+
+static int
+fpcmp(char* a, ulong* f) {
+ ulong tf[Prec];
+ int i, d, c;
+
+ for (i = 0; i < Prec; i++)
+ tf[i] = f[i];
+
+ for (;;) {
+ /* tf *= 10 */
+ for (i = 0; i < Prec; i++)
+ tf[i] = tf[i] * 10;
+ frnorm(tf);
+ d = (tf[0] >> Nbits) + '0';
+ tf[0] &= One - 1;
+
+ /* compare next digit */
+ c = *a;
+ if (c == 0) {
+ if ('0' < d)
+ return -1;
+ if (tf[0] != 0)
+ goto cont;
+ for (i = 1; i < Prec; i++)
+ if (tf[i] != 0)
+ goto cont;
+ return 0;
+ }
+ if (c > d)
+ return +1;
+ if (c < d)
+ return -1;
+ a++;
+ cont:;
+ }
+}
+
+static void
+divby(char* a, int* na, int b) {
+ int n, c;
+ char* p;
+
+ p = a;
+ n = 0;
+ while (n >> b == 0) {
+ c = *a++;
+ if (c == 0) {
+ while (n) {
+ c = n * 10;
+ if (c >> b)
+ break;
+ n = c;
+ }
+ goto xx;
+ }
+ n = n * 10 + c - '0';
+ (*na)--;
+ }
+ for (;;) {
+ c = n >> b;
+ n -= c << b;
+ *p++ = c + '0';
+ c = *a++;
+ if (c == 0)
+ break;
+ n = n * 10 + c - '0';
+ }
+ (*na)++;
+xx:
+ while (n) {
+ n = n * 10;
+ c = n >> b;
+ n -= c << b;
+ *p++ = c + '0';
+ (*na)++;
+ }
+ *p = 0;
+}
+
+static Tab tab1[] = {
+ { 1, 0, "" },
+ { 3, 1, "7" },
+ { 6, 2, "63" },
+ { 9, 3, "511" },
+ { 13, 4, "8191" },
+ { 16, 5, "65535" },
+ { 19, 6, "524287" },
+ { 23, 7, "8388607" },
+ { 26, 8, "67108863" },
+ { 27, 9, "134217727" }
+};
+
+static void
+divascii(char* a, int* na, int* dp, int* bp) {
+ int b, d;
+ Tab* t;
+
+ d = *dp;
+ if (d >= (int) (nelem(tab1)))
+ d = (int) (nelem(tab1)) - 1;
+ t = tab1 + d;
+ b = t->bp;
+ if (memcmp(a, t->cmp, t->siz) > 0)
+ d--;
+ *dp -= d;
+ *bp += b;
+ divby(a, na, b);
+}
+
+static void
+mulby(char* a, char* p, char* q, int b) {
+ int n, c;
+
+ n = 0;
+ *p = 0;
+ for (;;) {
+ q--;
+ if (q < a)
+ break;
+ c = *q - '0';
+ c = (c << b) + n;
+ n = c / 10;
+ c -= n * 10;
+ p--;
+ *p = c + '0';
+ }
+ while (n) {
+ c = n;
+ n = c / 10;
+ c -= n * 10;
+ p--;
+ *p = c + '0';
+ }
+}
+
+static Tab tab2[] = {
+ { 1, 1, "" }, /* dp = 0-0 */
+ { 3, 3, "125" },
+ { 6, 5, "15625" },
+ { 9, 7, "1953125" },
+ { 13, 10, "1220703125" },
+ { 16, 12, "152587890625" },
+ { 19, 14, "19073486328125" },
+ { 23, 17, "11920928955078125" },
+ { 26, 19, "1490116119384765625" },
+ { 27, 19, "7450580596923828125" }, /* dp 8-9 */
+};
+
+static void
+mulascii(char* a, int* na, int* dp, int* bp) {
+ char* p;
+ int d, b;
+ Tab* t;
+
+ d = -*dp;
+ if (d >= (int) (nelem(tab2)))
+ d = (int) (nelem(tab2)) - 1;
+ t = tab2 + d;
+ b = t->bp;
+ if (memcmp(a, t->cmp, t->siz) < 0)
+ d--;
+ p = a + *na;
+ *bp -= b;
+ *dp += d;
+ *na += d;
+ mulby(a, p + d, p, b);
+}
+
+static int
+xcmp(char* a, char* b) {
+ int c1, c2;
+
+ while ((c1 = *b++) != '\0') {
+ c2 = *a++;
+ if (isupper(c2))
+ c2 = tolower(c2);
+ if (c1 != c2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib/libfmt/test.c b/lib/libfmt/test.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+/* Copyright (c) 2004 Google Inc.; see LICENSE */
+
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <utf.h>
+
+int main(int argc, char* argv[]) {
+ quotefmtinstall();
+ print("hello world\n");
+ print("x: %x\n", 0x87654321);
+ print("u: %u\n", 0x87654321);
+ print("d: %d\n", 0x87654321);
+ print("s: %s\n", "hi there");
+ print("q: %q\n", "hi i'm here");
+ print("c: %c\n", '!');
+ print("g: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10);
+ print("e: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10);
+ print("f: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10);
+ print("smiley: %C\n", (Rune) 0x263a);
+ print("%g %.18g\n", 2e25, 2e25);
+ print("%2.18g\n", 1.0);
+ print("%2.18f\n", 1.0);
+ print("%f\n", 3.1415927 / 4);
+ print("%d\n", 23);
+ print("%i\n", 23);
+ print("%0.10d\n", 12345);
+
+ /* test %4$d formats */
+ print("%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222);
+ print("%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222);
+ print("%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20);
+ print("%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short) 111, 222, 20);
+ print("%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20);
+
+ /* test %'d formats */
+ print("%'d %'d %'d\n", 1, 2222, 33333333);
+ print("%'019d\n", 0);
+ print("%08d %08d %08d\n", 1, 2222, 33333333);
+ print("%'08d %'08d %'08d\n", 1, 2222, 33333333);
+ print("%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345);
+ print("%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL);
+ print("%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL);
+ print("%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL);
+ print("%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL);
+ print("%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL);
+ return 0;
+}
diff --git a/lib/libfmt/test2.c b/lib/libfmt/test2.c
@@ -0,0 +1,7 @@
+#include <fmt.h>
+#include <stdarg.h>
+#include <utf.h>
+
+int main(int argc, char** argv) {
+ print("%020.10d\n", 100);
+}
diff --git a/lib/libfmt/test3.c b/lib/libfmt/test3.c
@@ -0,0 +1,48 @@
+#include <libc.h>
+#include <stdio.h>
+#include <u.h>
+
+void test(char* fmt, ...) {
+ va_list arg;
+ char fmtbuf[100], stdbuf[100];
+
+ va_start(arg, fmt);
+ vsnprint(fmtbuf, sizeof fmtbuf, fmt, arg);
+ va_end(arg);
+
+ va_start(arg, fmt);
+ vsnprint(stdbuf, sizeof stdbuf, fmt, arg);
+ va_end(arg);
+
+ if (strcmp(fmtbuf, stdbuf) != 0)
+ print("fmt %s: fmt=\"%s\" std=\"%s\"\n", fmt, fmtbuf, stdbuf);
+
+ print("fmt %s: %s\n", fmt, fmtbuf);
+}
+
+
+int main(int argc, char* argv[]) {
+ test("%f", 3.14159);
+ test("%f", 3.14159e10);
+ test("%f", 3.14159e-10);
+
+ test("%e", 3.14159);
+ test("%e", 3.14159e10);
+ test("%e", 3.14159e-10);
+
+ test("%g", 3.14159);
+ test("%g", 3.14159e10);
+ test("%g", 3.14159e-10);
+
+ test("%g", 2e25);
+ test("%.18g", 2e25);
+
+ test("%2.18g", 1.0);
+ test("%2.18f", 1.0);
+ test("%f", 3.1415927 / 4);
+
+ test("%20.10d", 12345);
+ test("%0.10d", 12345);
+
+ return 0;
+}
diff --git a/lib/libfmt/vfprint.c b/lib/libfmt/vfprint.c
@@ -0,0 +1,20 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+int vfprint(int fd, char* fmt, va_list args) {
+ Fmt f;
+ char buf[256];
+ int n;
+
+ fmtfdinit(&f, fd, buf, sizeof(buf));
+ VA_COPY(f.args, args);
+ n = dofmt(&f, fmt);
+ VA_END(f.args);
+ if (n > 0 && __fmtFdFlush(&f) == 0)
+ return -1;
+ return n;
+}
diff --git a/lib/libfmt/vseprint.c b/lib/libfmt/vseprint.c
@@ -0,0 +1,26 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+
+char* vseprint(char* buf, char* e, char* fmt, va_list args) {
+ Fmt f;
+
+ if (e <= buf)
+ return nil;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = 0;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args, args);
+ fmtlocaleinit(&f, nil, nil, nil);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(char*) f.to = '\0';
+ return (char*) f.to;
+}
diff --git a/lib/libfmt/vsmprint.c b/lib/libfmt/vsmprint.c
@@ -0,0 +1,80 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+/*
+ * Plan 9 port version must include libc.h in order to
+ * get Plan 9 debugging malloc, which sometimes returns
+ * different pointers than the standard malloc.
+ */
+#ifdef PLAN9PORT
+# include "fmtdef.h"
+
+# include <libc.h>
+# include <u.h>
+#else
+# include "fmt.h"
+# include "fmtdef.h"
+# include "plan9.h"
+
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+static int
+fmtStrFlush(Fmt* f) {
+ char* s;
+ int n;
+
+ if (f->start == nil)
+ return 0;
+ n = (uintptr) f->farg;
+ n *= 2;
+ s = (char*) f->start;
+ f->start = realloc(s, n);
+ if (f->start == nil) {
+ f->farg = nil;
+ f->to = nil;
+ f->stop = nil;
+ free(s);
+ return 0;
+ }
+ f->farg = (void*) (uintptr) n;
+ f->to = (char*) f->start + ((char*) f->to - s);
+ f->stop = (char*) f->start + n - 1;
+ return 1;
+}
+
+int fmtstrinit(Fmt* f) {
+ int n;
+
+ memset(f, 0, sizeof *f);
+ f->runes = 0;
+ n = 32;
+ f->start = malloc(n);
+ if (f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (char*) f->start + n - 1;
+ f->flush = fmtStrFlush;
+ f->farg = (void*) (uintptr) n;
+ f->nfmt = 0;
+ fmtlocaleinit(f, nil, nil, nil);
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+char* vsmprint(char* fmt, va_list args) {
+ Fmt f;
+ int n;
+
+ if (fmtstrinit(&f) < 0)
+ return nil;
+ VA_COPY(f.args, args);
+ n = dofmt(&f, fmt);
+ VA_END(f.args);
+ if (n < 0) {
+ free(f.start);
+ return nil;
+ }
+ return fmtstrflush(&f);
+}
diff --git a/lib/libfmt/vsnprint.c b/lib/libfmt/vsnprint.c
@@ -0,0 +1,27 @@
+/* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
+#include "fmt.h"
+#include "fmtdef.h"
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+int vsnprint(char* buf, int len, char* fmt, va_list args) {
+ Fmt f;
+
+ if (len <= 0)
+ return -1;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = 0;
+ f.farg = nil;
+ f.nfmt = 0;
+ VA_COPY(f.args, args);
+ fmtlocaleinit(&f, nil, nil, nil);
+ dofmt(&f, fmt);
+ VA_END(f.args);
+ *(char*) f.to = '\0';
+ return (char*) f.to - buf;
+}
diff --git a/lib/libutf/Makefile b/lib/libutf/Makefile
@@ -0,0 +1,28 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = rune.o \
+ runestrcat.o \
+ runestrchr.o \
+ runestrcmp.o \
+ runestrcpy.o \
+ runestrdup.o \
+ runestrlen.o \
+ runestrecpy.o \
+ runestrncat.o \
+ runestrncmp.o \
+ runestrncpy.o \
+ runestrrchr.o \
+ runestrstr.o \
+ runetype.o \
+ utfecpy.o \
+ utflen.o \
+ utfnlen.o \
+ utfrrune.o \
+ utfrune.o \
+ utfutf.o
+
+HEADERS = plan9.h utf.h utfdef.h
+LIBRARY = libutf.a
+
+include $(TOPDIR)/mk/lib.mk
+\ No newline at end of file
diff --git a/lib/libutf/NOTICE b/lib/libutf/NOTICE
@@ -0,0 +1,22 @@
+This is a Unix port of the Plan 9 UTF-8 library, by Rob Pike and Ken Thompson.
+Please send comments about the packaging to Russ Cox <[email protected]>.
+
+Copyright © 2021 Plan 9 Foundation
+
+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 substantial 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.
diff --git a/lib/libutf/README b/lib/libutf/README
@@ -0,0 +1,5 @@
+This software was packaged for Unix by Russ Cox.
+Please send comments to [email protected].
+
+https://9fans.github.io/plan9port/unix
+
diff --git a/lib/libutf/isalpharune.3 b/lib/libutf/isalpharune.3
@@ -0,0 +1,57 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.TH ISALPHARUNE 3
+.SH NAME
+isalpharune, islowerrune, isspacerune, istitlerune, isupperrune, tolowerrune, totitlerune, toupperrune \- Unicode character classes and cases
+.SH SYNOPSIS
+.B #include <utf.h>
+.PP
+.B
+int isalpharune(Rune c)
+.PP
+.B
+int islowerrune(Rune c)
+.PP
+.B
+int isspacerune(Rune c)
+.PP
+.B
+int istitlerune(Rune c)
+.PP
+.B
+int isupperrune(Rune c)
+.PP
+.B
+Rune tolowerrune(Rune c)
+.PP
+.B
+Rune totitlerune(Rune c)
+.PP
+.B
+Rune toupperrune(Rune c)
+.SH DESCRIPTION
+These routines examine and operate on Unicode characters,
+in particular a subset of their properties as defined in the Unicode standard.
+Unicode defines some characters as alphabetic and specifies three cases:
+upper, lower, and title.
+Analogously to
+.IR isalpha (3)
+for
+.SM ASCII\c
+,
+these routines
+test types and modify cases for Unicode characters.
+The names are self-explanatory.
+.PP
+The case-conversion routines return the character unchanged if it has no case.
+.SH SOURCE
+.B https://9fans.github.io/plan9port/unix
+.SH "SEE ALSO
+.IR isalpha (3) ,
+.IR "The Unicode Standard" .
diff --git a/lib/libutf/plan9.h b/lib/libutf/plan9.h
@@ -0,0 +1,31 @@
+/*
+ * compiler directive on Plan 9
+ */
+#ifndef USED
+# define USED(x) \
+ if (x) \
+ ; \
+ else
+#endif
+
+/*
+ * easiest way to make sure these are defined
+ */
+#define uchar _utfuchar
+#define ushort _utfushort
+#define uint _utfuint
+#define ulong _utfulong
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+/*
+ * nil cannot be ((void*)0) on ANSI C,
+ * because it is used for function pointers
+ */
+#undef nil
+#define nil 0
+
+#undef nelem
+#define nelem(x) (sizeof(x) / sizeof(x)[0])
diff --git a/lib/libutf/rune.3 b/lib/libutf/rune.3
@@ -0,0 +1,194 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.TH RUNE 3
+.SH NAME
+runetochar, chartorune, runelen, runenlen, fullrune, utfecpy, utflen, utfnlen, utfrune, utfrrune, utfutf \- rune/UTF conversion
+.SH SYNOPSIS
+.ta \w'\fLchar*xx'u
+.B #include <utf.h>
+.PP
+.B
+int runetochar(char *s, Rune *r)
+.PP
+.B
+int chartorune(Rune *r, char *s)
+.PP
+.B
+int runelen(long r)
+.PP
+.B
+int runenlen(Rune *r, int n)
+.PP
+.B
+int fullrune(char *s, int n)
+.PP
+.B
+char* utfecpy(char *s1, char *es1, char *s2)
+.PP
+.B
+int utflen(char *s)
+.PP
+.B
+int utfnlen(char *s, long n)
+.PP
+.B
+char* utfrune(char *s, long c)
+.PP
+.B
+char* utfrrune(char *s, long c)
+.PP
+.B
+char* utfutf(char *s1, char *s2)
+.SH DESCRIPTION
+These routines convert to and from a
+.SM UTF
+byte stream and runes.
+.PP
+.I Runetochar
+copies one rune at
+.I r
+to at most
+.B UTFmax
+bytes starting at
+.I s
+and returns the number of bytes copied.
+.BR UTFmax ,
+defined as
+.B 3
+in
+.BR <libc.h> ,
+is the maximum number of bytes required to represent a rune.
+.PP
+.I Chartorune
+copies at most
+.B UTFmax
+bytes starting at
+.I s
+to one rune at
+.I r
+and returns the number of bytes copied.
+If the input is not exactly in
+.SM UTF
+format,
+.I chartorune
+will convert to 0x80 and return 1.
+.PP
+.I Runelen
+returns the number of bytes
+required to convert
+.I r
+into
+.SM UTF.
+.PP
+.I Runenlen
+returns the number of bytes
+required to convert the
+.I n
+runes pointed to by
+.I r
+into
+.SM UTF.
+.PP
+.I Fullrune
+returns 1 if the string
+.I s
+of length
+.I n
+is long enough to be decoded by
+.I chartorune
+and 0 otherwise.
+This does not guarantee that the string
+contains a legal
+.SM UTF
+encoding.
+This routine is used by programs that
+obtain input a byte at
+a time and need to know when a full rune
+has arrived.
+.PP
+The following routines are analogous to the
+corresponding string routines with
+.B utf
+substituted for
+.B str
+and
+.B rune
+substituted for
+.BR chr .
+.PP
+.I Utfecpy
+copies UTF sequences until a null sequence has been copied, but writes no
+sequences beyond
+.IR es1 .
+If any sequences are copied,
+.I s1
+is terminated by a null sequence, and a pointer to that sequence is returned.
+Otherwise, the original
+.I s1
+is returned.
+.PP
+.I Utflen
+returns the number of runes that
+are represented by the
+.SM UTF
+string
+.IR s .
+.PP
+.I Utfnlen
+returns the number of complete runes that
+are represented by the first
+.I n
+bytes of
+.SM UTF
+string
+.IR s .
+If the last few bytes of the string contain an incompletely coded rune,
+.I utfnlen
+will not count them; in this way, it differs from
+.IR utflen ,
+which includes every byte of the string.
+.PP
+.I Utfrune
+.RI ( utfrrune )
+returns a pointer to the first (last)
+occurrence of rune
+.I c
+in the
+.SM UTF
+string
+.IR s ,
+or 0 if
+.I c
+does not occur in the string.
+The NUL byte terminating a string is considered to
+be part of the string
+.IR s .
+.PP
+.I Utfutf
+returns a pointer to the first occurrence of
+the
+.SM UTF
+string
+.I s2
+as a
+.SM UTF
+substring of
+.IR s1 ,
+or 0 if there is none.
+If
+.I s2
+is the null string,
+.I utfutf
+returns
+.IR s1 .
+.SH SOURCE
+.B https://9fans.github.io/plan9port/unix
+.SH SEE ALSO
+.IR utf (7),
+.IR tcs (1)
diff --git a/lib/libutf/rune.c b/lib/libutf/rune.c
@@ -0,0 +1,205 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+enum {
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+ Bit5 = 2,
+
+ T1 = ((1 << (Bit1 + 1)) - 1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1 << (Bitx + 1)) - 1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1 << (Bit2 + 1)) - 1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1 << (Bit3 + 1)) - 1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1 << (Bit4 + 1)) - 1) ^ 0xFF, /* 1111 0000 */
+ T5 = ((1 << (Bit5 + 1)) - 1) ^ 0xFF, /* 1111 1000 */
+
+ Rune1 = (1 << (Bit1 + 0 * Bitx)) - 1, /* 0000 0000 0000 0000 0111 1111 */
+ Rune2 = (1 << (Bit2 + 1 * Bitx)) - 1, /* 0000 0000 0000 0111 1111 1111 */
+ Rune3 = (1 << (Bit3 + 2 * Bitx)) - 1, /* 0000 0000 1111 1111 1111 1111 */
+ Rune4 = (1 << (Bit4 + 3 * Bitx)) - 1, /* 0011 1111 1111 1111 1111 1111 */
+
+ Maskx = (1 << Bitx) - 1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror
+};
+
+int chartorune(Rune* rune, char* str) {
+ int c, c1, c2, c3;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*) str;
+ if (c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*) (str + 1) ^ Tx;
+ if (c1 & Testx)
+ goto bad;
+ if (c < T3) {
+ if (c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if (l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*) (str + 2) ^ Tx;
+ if (c2 & Testx)
+ goto bad;
+ if (c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if (l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * four character sequence
+ * 10000-10FFFF => T4 Tx Tx Tx
+ */
+ if (UTFmax >= 4) {
+ c3 = *(uchar*) (str + 3) ^ Tx;
+ if (c3 & Testx)
+ goto bad;
+ if (c < T5) {
+ l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
+ if (l <= Rune3)
+ goto bad;
+ if (l > Runemax)
+ goto bad;
+ *rune = l;
+ return 4;
+ }
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int runetochar(char* str, Rune* rune) {
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if (c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 00080-007FF => T2 Tx
+ */
+ if (c <= Rune2) {
+ str[0] = T2 | (c >> 1 * Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 00800-0FFFF => T3 Tx Tx
+ */
+ if (c > Runemax)
+ c = Runeerror;
+ if (c <= Rune3) {
+ str[0] = T3 | (c >> 2 * Bitx);
+ str[1] = Tx | ((c >> 1 * Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+ }
+
+ /*
+ * four character sequence
+ * 010000-1FFFFF => T4 Tx Tx Tx
+ */
+ str[0] = T4 | (c >> 3 * Bitx);
+ str[1] = Tx | ((c >> 2 * Bitx) & Maskx);
+ str[2] = Tx | ((c >> 1 * Bitx) & Maskx);
+ str[3] = Tx | (c & Maskx);
+ return 4;
+}
+
+int runelen(long c) {
+ Rune rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int runenlen(Rune* r, int nrune) {
+ int nb, c;
+
+ nb = 0;
+ while (nrune--) {
+ c = *r++;
+ if (c <= Rune1)
+ nb++;
+ else if (c <= Rune2)
+ nb += 2;
+ else if (c <= Rune3 || c > Runemax)
+ nb += 3;
+ else
+ nb += 4;
+ }
+ return nb;
+}
+
+int fullrune(char* str, int n) {
+ int c;
+
+ if (n <= 0)
+ return 0;
+ c = *(uchar*) str;
+ if (c < Tx)
+ return 1;
+ if (c < T3)
+ return n >= 2;
+ if (UTFmax == 3 || c < T4)
+ return n >= 3;
+ return n >= 4;
+}
diff --git a/lib/libutf/runestrcat.3 b/lib/libutf/runestrcat.3
@@ -0,0 +1,74 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.TH RUNESTRCAT 3
+.SH NAME
+runestrcat,
+runestrncat,
+runestrcmp,
+runestrncmp,
+runestrcpy,
+runestrncpy,
+runestrecpy,
+runestrlen,
+runestrchr,
+runestrrchr,
+runestrdup,
+runestrstr \- rune string operations
+.SH SYNOPSIS
+.B #include <u.h>
+.br
+.B #include <libc.h>
+.PP
+.ta \w'\fLRune* \fP'u
+.B
+Rune* runestrcat(Rune *s1, Rune *s2)
+.PP
+.B
+Rune* runestrncat(Rune *s1, Rune *s2, long n)
+.PP
+.B
+int runestrcmp(Rune *s1, Rune *s2)
+.PP
+.B
+int runestrncmp(Rune *s1, Rune *s2, long n)
+.PP
+.B
+Rune* runestrcpy(Rune *s1, Rune *s2)
+.PP
+.B
+Rune* runestrncpy(Rune *s1, Rune *s2, long n)
+.PP
+.B
+Rune* runestrecpy(Rune *s1, Rune *es1, Rune *s2)
+.PP
+.B
+long runestrlen(Rune *s)
+.PP
+.B
+Rune* runestrchr(Rune *s, Rune c)
+.PP
+.B
+Rune* runestrrchr(Rune *s, Rune c)
+.PP
+.B
+Rune* runestrdup(Rune *s)
+.PP
+.B
+Rune* runestrstr(Rune *s1, Rune *s2)
+.SH DESCRIPTION
+These functions are rune string analogues of
+the corresponding functions in
+.IR strcat (3).
+.SH SOURCE
+.B https://9fans.github.io/plan9port/unix
+.SH SEE ALSO
+.IR rune (3),
+.IR strcat (3)
+.SH BUGS
+The outcome of overlapping moves varies among implementations.
diff --git a/lib/libutf/runestrcat.c b/lib/libutf/runestrcat.c
@@ -0,0 +1,24 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrcat(Rune* s1, Rune* s2) {
+
+ runestrcpy(runestrchr(s1, 0), s2);
+ return s1;
+}
diff --git a/lib/libutf/runestrchr.c b/lib/libutf/runestrchr.c
@@ -0,0 +1,34 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrchr(Rune* s, Rune c) {
+ Rune c0 = c;
+ Rune c1;
+
+ if (c == 0) {
+ while (*s++)
+ ;
+ return s - 1;
+ }
+
+ while ((c1 = *s++))
+ if (c1 == c0)
+ return s - 1;
+ return 0;
+}
diff --git a/lib/libutf/runestrcmp.c b/lib/libutf/runestrcmp.c
@@ -0,0 +1,34 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int runestrcmp(Rune* s1, Rune* s2) {
+ Rune c1, c2;
+
+ for (;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+ if (c1 > c2)
+ return 1;
+ return -1;
+ }
+ if (c1 == 0)
+ return 0;
+ }
+}
diff --git a/lib/libutf/runestrcpy.c b/lib/libutf/runestrcpy.c
@@ -0,0 +1,27 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrcpy(Rune* s1, Rune* s2) {
+ Rune* os1;
+
+ os1 = s1;
+ while ((*s1++ = *s2++))
+ ;
+ return os1;
+}
diff --git a/lib/libutf/runestrdup.c b/lib/libutf/runestrdup.c
@@ -0,0 +1,29 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+Rune* runestrdup(Rune* s) {
+ Rune* ns;
+
+ ns = malloc(sizeof(Rune) * (runestrlen(s) + 1));
+ if (ns == 0)
+ return 0;
+
+ return runestrcpy(ns, s);
+}
diff --git a/lib/libutf/runestrecpy.c b/lib/libutf/runestrecpy.c
@@ -0,0 +1,31 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrecpy(Rune* s1, Rune* es1, Rune* s2) {
+ if (s1 >= es1)
+ return s1;
+
+ while ((*s1++ = *s2++)) {
+ if (s1 == es1) {
+ *--s1 = '\0';
+ break;
+ }
+ }
+ return s1;
+}
diff --git a/lib/libutf/runestrlen.c b/lib/libutf/runestrlen.c
@@ -0,0 +1,23 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+long runestrlen(Rune* s) {
+
+ return runestrchr(s, 0) - s;
+}
diff --git a/lib/libutf/runestrncat.c b/lib/libutf/runestrncat.c
@@ -0,0 +1,31 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrncat(Rune* s1, Rune* s2, long n) {
+ Rune* os1;
+
+ os1 = s1;
+ s1 = runestrchr(s1, 0);
+ while ((*s1++ = *s2++))
+ if (--n < 0) {
+ s1[-1] = 0;
+ break;
+ }
+ return os1;
+}
diff --git a/lib/libutf/runestrncmp.c b/lib/libutf/runestrncmp.c
@@ -0,0 +1,36 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int runestrncmp(Rune* s1, Rune* s2, long n) {
+ Rune c1, c2;
+
+ while (n > 0) {
+ c1 = *s1++;
+ c2 = *s2++;
+ n--;
+ if (c1 != c2) {
+ if (c1 > c2)
+ return 1;
+ return -1;
+ }
+ if (c1 == 0)
+ break;
+ }
+ return 0;
+}
diff --git a/lib/libutf/runestrncpy.c b/lib/libutf/runestrncpy.c
@@ -0,0 +1,32 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrncpy(Rune* s1, Rune* s2, long n) {
+ int i;
+ Rune* os1;
+
+ os1 = s1;
+ for (i = 0; i < n; i++)
+ if ((*s1++ = *s2++) == 0) {
+ while (++i < n)
+ *s1++ = 0;
+ return os1;
+ }
+ return os1;
+}
diff --git a/lib/libutf/runestrrchr.c b/lib/libutf/runestrrchr.c
@@ -0,0 +1,29 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+Rune* runestrrchr(Rune* s, Rune c) {
+ Rune* r;
+
+ if (c == 0)
+ return runestrchr(s, 0);
+ r = 0;
+ while ((s = runestrchr(s, c)))
+ r = s++;
+ return r;
+}
diff --git a/lib/libutf/runestrstr.c b/lib/libutf/runestrstr.c
@@ -0,0 +1,43 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+Rune* runestrstr(Rune* s1, Rune* s2) {
+ Rune * p, *pa, *pb;
+ unsigned int c0, c;
+
+ c0 = *s2;
+ if (c0 == 0)
+ return s1;
+ s2++;
+ for (p = runestrchr(s1, c0); p; p = runestrchr(p + 1, c0)) {
+ pa = p;
+ for (pb = s2;; pb++) {
+ c = *pb;
+ if (c == 0)
+ return p;
+ if (c != *++pa)
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/lib/libutf/runetype.c b/lib/libutf/runetype.c
@@ -0,0 +1,1119 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * alpha ranges -
+ * only covers ranges not in lower||upper
+ */
+static Rune __alpha2[] = {
+ 0x00d8, 0x00f6, /* Ø - ö */ 0
+ 0x00f8, 0x01f5, /* ø - ǵ */ 0
+ 0x0250, 0x02a8, /* ɐ - ʨ */ 0
+ 0x038e, 0x03a1, /* Ύ - Ρ */ 0
+ 0x03a3, 0x03ce, /* Σ - ώ */ 0
+ 0x03d0, 0x03d6, /* ϐ - ϖ */ 0
+ 0x03e2, 0x03f3, /* Ϣ - ϳ */ 0
+ 0x0490, 0x04c4, /* Ґ - ӄ */ 0
+ 0x0561, 0x0587, /* ա - և */ 0
+ 0x05d0, 0x05ea, /* א - ת */ 0
+ 0x05f0, 0x05f2, /* װ - ײ */ 0
+ 0x0621, 0x063a, /* ء - غ */ 0
+ 0x0640, 0x064a, /* ـ - ي */ 0
+ 0x0671, 0x06b7, /* ٱ - ڷ */ 0
+ 0x06ba, 0x06be, /* ں - ھ */ 0
+ 0x06c0, 0x06ce, /* ۀ - ێ */ 0
+ 0x06d0, 0x06d3, /* ې - ۓ */ 0
+ 0x0905, 0x0939, /* अ - ह */ 0x0
+ 0x0958, 0x0961, /* क़ - ॡ */ 0x0
+ 0x0985, 0x098c, /* অ - ঌ */ 0x0
+ 0x098f, 0x0990, /* এ - ঐ */ 0x0
+ 0x0993, 0x09a8, /* ও - ন */ 0x0
+ 0x09aa, 0x09b0, /* প - র */ 0x0
+ 0x09b6, 0x09b9, /* শ - হ */ 0x0
+ 0x09dc, 0x09dd, /* ড় - ঢ় */ 0x0
+ 0x09df, 0x09e1, /* য় - ৡ */ 0x0
+ 0x09f0, 0x09f1, /* ৰ - ৱ */ 0x0
+ 0x0a05, 0x0a0a, /* ਅ - ਊ */ 0x0
+ 0x0a0f, 0x0a10, /* ਏ - ਐ */ 0x0
+ 0x0a13, 0x0a28, /* ਓ - ਨ */ 0x0
+ 0x0a2a, 0x0a30, /* ਪ - ਰ */ 0x0
+ 0x0a32, 0x0a33, /* ਲ - ਲ਼ */ 0x0
+ 0x0a35, 0x0a36, /* ਵ - ਸ਼ */ 0x0
+ 0x0a38, 0x0a39, /* ਸ - ਹ */ 0x0
+ 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */ 0x0
+ 0x0a85, 0x0a8b, /* અ - ઋ */ 0x0
+ 0x0a8f, 0x0a91, /* એ - ઑ */ 0x0
+ 0x0a93, 0x0aa8, /* ઓ - ન */ 0x0
+ 0x0aaa, 0x0ab0, /* પ - ર */ 0x0
+ 0x0ab2, 0x0ab3, /* લ - ળ */ 0x0
+ 0x0ab5, 0x0ab9, /* વ - હ */ 0x0
+ 0x0b05, 0x0b0c, /* ଅ - ଌ */ 0x0
+ 0x0b0f, 0x0b10, /* ଏ - ଐ */ 0x0
+ 0x0b13, 0x0b28, /* ଓ - ନ */ 0x0
+ 0x0b2a, 0x0b30, /* ପ - ର */ 0x0
+ 0x0b32, 0x0b33, /* ଲ - ଳ */ 0x0
+ 0x0b36, 0x0b39, /* ଶ - ହ */ 0x0
+ 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */ 0x0
+ 0x0b5f, 0x0b61, /* ୟ - ୡ */ 0x0
+ 0x0b85, 0x0b8a, /* அ - ஊ */ 0x0
+ 0x0b8e, 0x0b90, /* எ - ஐ */ 0x0
+ 0x0b92, 0x0b95, /* ஒ - க */ 0x0
+ 0x0b99, 0x0b9a, /* ங - ச */ 0x0
+ 0x0b9e, 0x0b9f, /* ஞ - ட */ 0x0
+ 0x0ba3, 0x0ba4, /* ண - த */ 0x0
+ 0x0ba8, 0x0baa, /* ந - ப */ 0x0
+ 0x0bae, 0x0bb5, /* ம - வ */ 0x0
+ 0x0bb7, 0x0bb9, /* ஷ - ஹ */ 0x0
+ 0x0c05, 0x0c0c, /* అ - ఌ */ 0x0
+ 0x0c0e, 0x0c10, /* ఎ - ఐ */ 0x0
+ 0x0c12, 0x0c28, /* ఒ - న */ 0x0
+ 0x0c2a, 0x0c33, /* ప - ళ */ 0x0
+ 0x0c35, 0x0c39, /* వ - హ */ 0x0
+ 0x0c60, 0x0c61, /* ౠ - ౡ */ 0x0
+ 0x0c85, 0x0c8c, /* ಅ - ಌ */ 0x0
+ 0x0c8e, 0x0c90, /* ಎ - ಐ */ 0x0
+ 0x0c92, 0x0ca8, /* ಒ - ನ */ 0x0
+ 0x0caa, 0x0cb3, /* ಪ - ಳ */ 0x0
+ 0x0cb5, 0x0cb9, /* ವ - ಹ */ 0x0
+ 0x0ce0, 0x0ce1, /* ೠ - ೡ */ 0x0
+ 0x0d05, 0x0d0c, /* അ - ഌ */ 0x0
+ 0x0d0e, 0x0d10, /* എ - ഐ */ 0x0
+ 0x0d12, 0x0d28, /* ഒ - ന */ 0x0
+ 0x0d2a, 0x0d39, /* പ - ഹ */ 0x0
+ 0x0d60, 0x0d61, /* ൠ - ൡ */ 0x0
+ 0x0e01, 0x0e30, /* ก - ะ */ 0x0
+ 0x0e32, 0x0e33, /* า - ำ */ 0x0
+ 0x0e40, 0x0e46, /* เ - ๆ */ 0x0
+ 0x0e5a, 0x0e5b, /* ๚ - ๛ */ 0x0
+ 0x0e81, 0x0e82, /* ກ - ຂ */ 0x0
+ 0x0e87, 0x0e88, /* ງ - ຈ */ 0x0
+ 0x0e94, 0x0e97, /* ດ - ທ */ 0x0
+ 0x0e99, 0x0e9f, /* ນ - ຟ */ 0x0
+ 0x0ea1, 0x0ea3, /* ມ - ຣ */ 0x0
+ 0x0eaa, 0x0eab, /* ສ - ຫ */ 0x0
+ 0x0ead, 0x0eae, /* ອ - ຮ */ 0x0
+ 0x0eb2, 0x0eb3, /* າ - ຳ */ 0x0
+ 0x0ec0, 0x0ec4, /* ເ - ໄ */ 0x0
+ 0x0edc, 0x0edd, /* ໜ - ໝ */ 0x0
+ 0x0f18, 0x0f19, /* ༘ - ༙ */ 0x0
+ 0x0f40, 0x0f47, /* ཀ - ཇ */ 0x0
+ 0x0f49, 0x0f69, /* ཉ - ཀྵ */ 0x1
+ 0x10d0, 0x10f6, /* ა - ჶ */ 0x1
+ 0x1100, 0x1159, /* ᄀ - ᅙ */ 0x1
+ 0x115f, 0x11a2, /* ᅟ - ᆢ */ 0x1
+ 0x11a8, 0x11f9, /* ᆨ - ᇹ */ 0x1
+ 0x1e00, 0x1e9b, /* Ḁ - ẛ */ 0x1
+ 0x1f50, 0x1f57, /* ὐ - ὗ */ 0x1
+ 0x1f80, 0x1fb4, /* ᾀ - ᾴ */ 0x1
+ 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */ 0x1
+ 0x1fc2, 0x1fc4, /* ῂ - ῄ */ 0x1
+ 0x1fc6, 0x1fcc, /* ῆ - ῌ */ 0x1
+ 0x1fd0, 0x1fd3, /* ῐ - ΐ */ 0x1
+ 0x1fd6, 0x1fdb, /* ῖ - Ί */ 0x1
+ 0x1fe0, 0x1fec, /* ῠ - Ῥ */ 0x1
+ 0x1ff2, 0x1ff4, /* ῲ - ῴ */ 0x1
+ 0x1ff6, 0x1ffc, /* ῶ - ῼ */ 0x2
+ 0x210a, 0x2113, /* ℊ - ℓ */ 0x2
+ 0x2115, 0x211d, /* ℕ - ℝ */ 0x2
+ 0x2120, 0x2122, /* ℠ - ™ */ 0x2
+ 0x212a, 0x2131, /* K - ℱ */ 0x2
+ 0x2133, 0x2138, /* ℳ - ℸ */ 0x3
+ 0x3041, 0x3094, /* ぁ - ゔ */ 0x3
+ 0x30a1, 0x30fa, /* ァ - ヺ */ 0x3
+ 0x3105, 0x312c, /* ㄅ - ㄬ */ 0x3
+ 0x3131, 0x318e, /* ㄱ - ㆎ */ 0x3
+ 0x3192, 0x319f, /* ㆒ - ㆟ */ 0x3
+ 0x3260, 0x327b, /* ㉠ - ㉻ */ 0x3
+ 0x328a, 0x32b0, /* ㊊ - ㊰ */ 0x3
+ 0x32d0, 0x32fe, /* ㋐ - ㋾ */ 0x3
+ 0x3300, 0x3357, /* ㌀ - ㍗ */ 0x3
+ 0x3371, 0x3376, /* ㍱ - ㍶ */ 0x3
+ 0x337b, 0x3394, /* ㍻ - ㎔ */ 0x3
+ 0x3399, 0x339e, /* ㎙ - ㎞ */ 0x3
+ 0x33a9, 0x33ad, /* ㎩ - ㎭ */ 0x3
+ 0x33b0, 0x33c1, /* ㎰ - ㏁ */ 0x3
+ 0x33c3, 0x33c5, /* ㏃ - ㏅ */ 0x3
+ 0x33c7, 0x33d7, /* ㏇ - ㏗ */ 0x3
+ 0x33d9, 0x33dd, /* ㏙ - ㏝ */ 0x4
+ 0x4e00, 0x9fff, /* 一 - 鿿 */ 0xa
+ 0xac00, 0xd7a3, /* 가 - 힣 */ 0xf
+ 0xf900, 0xfb06, /* 豈 - st */ 0xf
+ 0xfb13, 0xfb17, /* ﬓ - ﬗ */ 0xf
+ 0xfb1f, 0xfb28, /* ײַ - ﬨ */ 0xf
+ 0xfb2a, 0xfb36, /* שׁ - זּ */ 0xf
+ 0xfb38, 0xfb3c, /* טּ - לּ */ 0xf
+ 0xfb40, 0xfb41, /* נּ - סּ */ 0xf
+ 0xfb43, 0xfb44, /* ףּ - פּ */ 0xf
+ 0xfb46, 0xfbb1, /* צּ - ﮱ */ 0xf
+ 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */ 0xf
+ 0xfd50, 0xfd8f, /* ﵐ - ﶏ */ 0xf
+ 0xfd92, 0xfdc7, /* ﶒ - ﷇ */ 0xf
+ 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */ 0xf
+ 0xfe70, 0xfe72, /* ﹰ - ﹲ */ 0xf
+ 0xfe76, 0xfefc, /* ﹶ - ﻼ */ 0xf
+ 0xff66, 0xff6f, /* ヲ - ッ */ 0xf
+ 0xff71, 0xff9d, /* ア - ン */ 0xf
+ 0xffa0, 0xffbe, /* ᅠ - ᄒ */ 0xf
+ 0xffc2, 0xffc7, /* ᅡ - ᅦ */ 0xf
+ 0xffca, 0xffcf, /* ᅧ - ᅬ */ 0xf
+ 0xffd2, 0xffd7, /* ᅭ - ᅲ */ 0xf
+ 0xffda, 0xffdc, /* ᅳ - ᅵ */};
+};
+
+/*
+ * alpha singlets -
+ * only covers ranges not in lower||upper
+ */
+static Rune __alpha1[] = {
+ 0x00aa, /* ª */
+ 0x00b5, /* µ */
+ 0x00ba, /* º */
+ 0x03da, /* Ϛ */
+ 0x03dc, /* Ϝ */
+ 0x03de, /* Ϟ */
+ 0x03e0, /* Ϡ */
+ 0x06d5, /* ە */
+ 0x09b2, /* ল */ 0
+ 0x0a5e, /* ਫ਼ */ 0
+ 0x0a8d, /* ઍ */ 0
+ 0x0ae0, /* ૠ */ 0
+ 0x0b9c, /* ஜ */ 0
+ 0x0cde, /* ೞ */ 0
+ 0x0e4f, /* ๏ */ 0
+ 0x0e84, /* ຄ */ 0
+ 0x0e8a, /* ຊ */ 0
+ 0x0e8d, /* ຍ */ 0
+ 0x0ea5, /* ລ */ 0
+ 0x0ea7, /* ວ */ 0
+ 0x0eb0, /* ະ */ 0
+ 0x0ebd, /* ຽ */ 0
+ 0x1fbe, /* ι */ 0
+ 0x207f, /* ⁿ */ 0
+ 0x20a8, /* ₨ */ 0
+ 0x2102, /* ℂ */ 0
+ 0x2107, /* ℇ */ 0
+ 0x2124, /* ℤ */ 0
+ 0x2126, /* Ω */ 0
+ 0x2128, /* ℨ */ 0
+ 0xfb3e, /* מּ */ 0
+ 0xfe74, /* ﹴ */};
+};
+
+/*
+ * space ranges
+ */
+static Rune __space2[] = {
+ 0x0009, 0x000a, /* tab and newline */
+ 0x0020, 0x0020, /* space */
+ 0x00a0, 0x00a0, /* */
+ 0x2000, 0x200b, /* - */ 0x2
+ 0x2028, 0x2029, /*
-
*/ 0x3
+ 0x3000, 0x3000, /* */ 0
+ 0xfeff, 0xfeff, /* */};
+};
+
+/*
+ * lower case ranges
+ * 3rd col is conversion excess 500
+ */
+static Rune __toupper2[] = {
+ 0x0061, 0x007a, 468, /* a-z A-Z */
+ 0x00e0, 0x00f6, 468, /* à-ö À-Ö */ 0x0
+ 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */ 0x0
+ 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */ 0x0
+ 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */ 0x0
+ 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */ 0x0
+ 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */ 0x0
+ 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */ 0x0
+ 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */ 0x0
+ 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */ 0x0
+ 0x0430, 0x044f, 468, /* а-я А-Я */ 0x0
+ 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */ 0x0
+ 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */ 0x0
+ 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */ 0x1
+ 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */ 0x1f10,
+ 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */ 0x1f20,
+ 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */ 0x1f30,
+ 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */ 0x1f40,
+ 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */ 0x1f60,
+ 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */ 0x1f70,
+ 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */ 0x1f72,
+ 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */ 0x1f76,
+ 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */ 0x1f78,
+ 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */ 0x1f7a,
+ 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */ 0x1f7c,
+ 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */ 0x1f80,
+ 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */ 0x1f90,
+ 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */ 0x1fa0,
+ 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */ 0x1fb0,
+ 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */ 0x1fd0,
+ 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */ 0x1fe0,
+ 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */ 0x2170,
+ 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */ 0x24d0,
+ 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */ 0xff41,
+ 0xff41, 0xff5a, 468, /* a-z A-Z */};/*
+};
+
+/*
+ * lower case singlets
+ * 2nd col is conversion excess 500
+ */
+static Rune __toupper1[] = {
+ 0x00ff, 621, /* ÿ Ÿ */ 0
+ 0x0101, 499, /* ā Ā */ 0
+ 0x0103, 499, /* ă Ă */ 0
+ 0x0105, 499, /* ą Ą */ 0
+ 0x0107, 499, /* ć Ć */ 0
+ 0x0109, 499, /* ĉ Ĉ */ 0
+ 0x010b, 499, /* ċ Ċ */ 0
+ 0x010d, 499, /* č Č */ 0
+ 0x010f, 499, /* ď Ď */ 0
+ 0x0111, 499, /* đ Đ */ 0
+ 0x0113, 499, /* ē Ē */ 0
+ 0x0115, 499, /* ĕ Ĕ */ 0
+ 0x0117, 499, /* ė Ė */ 0
+ 0x0119, 499, /* ę Ę */ 0
+ 0x011b, 499, /* ě Ě */ 0
+ 0x011d, 499, /* ĝ Ĝ */ 0
+ 0x011f, 499, /* ğ Ğ */ 0
+ 0x0121, 499, /* ġ Ġ */ 0
+ 0x0123, 499, /* ģ Ģ */ 0
+ 0x0125, 499, /* ĥ Ĥ */ 0
+ 0x0127, 499, /* ħ Ħ */ 0
+ 0x0129, 499, /* ĩ Ĩ */ 0
+ 0x012b, 499, /* ī Ī */ 0
+ 0x012d, 499, /* ĭ Ĭ */ 0
+ 0x012f, 499, /* į Į */ 0
+ 0x0131, 268, /* ı I */
+ 0x0133, 499, /* ij IJ */ 0
+ 0x0135, 499, /* ĵ Ĵ */ 0
+ 0x0137, 499, /* ķ Ķ */ 0
+ 0x013a, 499, /* ĺ Ĺ */ 0
+ 0x013c, 499, /* ļ Ļ */ 0
+ 0x013e, 499, /* ľ Ľ */ 0
+ 0x0140, 499, /* ŀ Ŀ */ 0
+ 0x0142, 499, /* ł Ł */ 0
+ 0x0144, 499, /* ń Ń */ 0
+ 0x0146, 499, /* ņ Ņ */ 0
+ 0x0148, 499, /* ň Ň */ 0
+ 0x014b, 499, /* ŋ Ŋ */ 0
+ 0x014d, 499, /* ō Ō */ 0
+ 0x014f, 499, /* ŏ Ŏ */ 0
+ 0x0151, 499, /* ő Ő */ 0
+ 0x0153, 499, /* œ Œ */ 0
+ 0x0155, 499, /* ŕ Ŕ */ 0
+ 0x0157, 499, /* ŗ Ŗ */ 0
+ 0x0159, 499, /* ř Ř */ 0
+ 0x015b, 499, /* ś Ś */ 0
+ 0x015d, 499, /* ŝ Ŝ */ 0
+ 0x015f, 499, /* ş Ş */ 0
+ 0x0161, 499, /* š Š */ 0
+ 0x0163, 499, /* ţ Ţ */ 0
+ 0x0165, 499, /* ť Ť */ 0
+ 0x0167, 499, /* ŧ Ŧ */ 0
+ 0x0169, 499, /* ũ Ũ */ 0
+ 0x016b, 499, /* ū Ū */ 0
+ 0x016d, 499, /* ŭ Ŭ */ 0
+ 0x016f, 499, /* ů Ů */ 0
+ 0x0171, 499, /* ű Ű */ 0
+ 0x0173, 499, /* ų Ų */ 0
+ 0x0175, 499, /* ŵ Ŵ */ 0
+ 0x0177, 499, /* ŷ Ŷ */ 0
+ 0x017a, 499, /* ź Ź */ 0
+ 0x017c, 499, /* ż Ż */ 0
+ 0x017e, 499, /* ž Ž */ 0
+ 0x017f, 200, /* ſ S */
+ 0x0183, 499, /* ƃ Ƃ */ 0
+ 0x0185, 499, /* ƅ Ƅ */ 0
+ 0x0188, 499, /* ƈ Ƈ */ 0
+ 0x018c, 499, /* ƌ Ƌ */ 0
+ 0x0192, 499, /* ƒ Ƒ */ 0
+ 0x0199, 499, /* ƙ Ƙ */ 0
+ 0x01a1, 499, /* ơ Ơ */ 0
+ 0x01a3, 499, /* ƣ Ƣ */ 0
+ 0x01a5, 499, /* ƥ Ƥ */ 0
+ 0x01a8, 499, /* ƨ Ƨ */ 0
+ 0x01ad, 499, /* ƭ Ƭ */ 0
+ 0x01b0, 499, /* ư Ư */ 0
+ 0x01b4, 499, /* ƴ Ƴ */ 0
+ 0x01b6, 499, /* ƶ Ƶ */ 0
+ 0x01b9, 499, /* ƹ Ƹ */ 0
+ 0x01bd, 499, /* ƽ Ƽ */ 0
+ 0x01c5, 499, /* Dž DŽ */ 0
+ 0x01c6, 498, /* dž DŽ */ 0
+ 0x01c8, 499, /* Lj LJ */ 0
+ 0x01c9, 498, /* lj LJ */ 0
+ 0x01cb, 499, /* Nj NJ */ 0
+ 0x01cc, 498, /* nj NJ */ 0
+ 0x01ce, 499, /* ǎ Ǎ */ 0
+ 0x01d0, 499, /* ǐ Ǐ */ 0
+ 0x01d2, 499, /* ǒ Ǒ */ 0
+ 0x01d4, 499, /* ǔ Ǔ */ 0
+ 0x01d6, 499, /* ǖ Ǖ */ 0
+ 0x01d8, 499, /* ǘ Ǘ */ 0
+ 0x01da, 499, /* ǚ Ǚ */ 0
+ 0x01dc, 499, /* ǜ Ǜ */ 0
+ 0x01df, 499, /* ǟ Ǟ */ 0
+ 0x01e1, 499, /* ǡ Ǡ */ 0
+ 0x01e3, 499, /* ǣ Ǣ */ 0
+ 0x01e5, 499, /* ǥ Ǥ */ 0
+ 0x01e7, 499, /* ǧ Ǧ */ 0
+ 0x01e9, 499, /* ǩ Ǩ */ 0
+ 0x01eb, 499, /* ǫ Ǫ */ 0
+ 0x01ed, 499, /* ǭ Ǭ */ 0
+ 0x01ef, 499, /* ǯ Ǯ */ 0
+ 0x01f2, 499, /* Dz DZ */ 0
+ 0x01f3, 498, /* dz DZ */ 0
+ 0x01f5, 499, /* ǵ Ǵ */ 0
+ 0x01fb, 499, /* ǻ Ǻ */ 0
+ 0x01fd, 499, /* ǽ Ǽ */ 0
+ 0x01ff, 499, /* ǿ Ǿ */ 0
+ 0x0201, 499, /* ȁ Ȁ */ 0
+ 0x0203, 499, /* ȃ Ȃ */ 0
+ 0x0205, 499, /* ȅ Ȅ */ 0
+ 0x0207, 499, /* ȇ Ȇ */ 0
+ 0x0209, 499, /* ȉ Ȉ */ 0
+ 0x020b, 499, /* ȋ Ȋ */ 0
+ 0x020d, 499, /* ȍ Ȍ */ 0
+ 0x020f, 499, /* ȏ Ȏ */ 0
+ 0x0211, 499, /* ȑ Ȑ */ 0
+ 0x0213, 499, /* ȓ Ȓ */ 0
+ 0x0215, 499, /* ȕ Ȕ */ 0
+ 0x0217, 499, /* ȗ Ȗ */ 0
+ 0x0253, 290, /* ɓ Ɓ */ 0
+ 0x0254, 294, /* ɔ Ɔ */ 0
+ 0x025b, 297, /* ɛ Ɛ */ 0
+ 0x0260, 295, /* ɠ Ɠ */ 0
+ 0x0263, 293, /* ɣ Ɣ */ 0
+ 0x0268, 291, /* ɨ Ɨ */ 0
+ 0x0269, 289, /* ɩ Ɩ */ 0
+ 0x026f, 289, /* ɯ Ɯ */ 0
+ 0x0272, 287, /* ɲ Ɲ */ 0
+ 0x0283, 282, /* ʃ Ʃ */ 0
+ 0x0288, 282, /* ʈ Ʈ */ 0
+ 0x0292, 281, /* ʒ Ʒ */ 0
+ 0x03ac, 462, /* ά Ά */ 0
+ 0x03cc, 436, /* ό Ό */ 0
+ 0x03d0, 438, /* ϐ Β */ 0
+ 0x03d1, 443, /* ϑ Θ */ 0
+ 0x03d5, 453, /* ϕ Φ */ 0
+ 0x03d6, 446, /* ϖ Π */ 0
+ 0x03e3, 499, /* ϣ Ϣ */ 0
+ 0x03e5, 499, /* ϥ Ϥ */ 0
+ 0x03e7, 499, /* ϧ Ϧ */ 0
+ 0x03e9, 499, /* ϩ Ϩ */ 0
+ 0x03eb, 499, /* ϫ Ϫ */ 0
+ 0x03ed, 499, /* ϭ Ϭ */ 0
+ 0x03ef, 499, /* ϯ Ϯ */ 0
+ 0x03f0, 414, /* ϰ Κ */ 0
+ 0x03f1, 420, /* ϱ Ρ */ 0
+ 0x0461, 499, /* ѡ Ѡ */ 0
+ 0x0463, 499, /* ѣ Ѣ */ 0
+ 0x0465, 499, /* ѥ Ѥ */ 0
+ 0x0467, 499, /* ѧ Ѧ */ 0
+ 0x0469, 499, /* ѩ Ѩ */ 0
+ 0x046b, 499, /* ѫ Ѫ */ 0
+ 0x046d, 499, /* ѭ Ѭ */ 0
+ 0x046f, 499, /* ѯ Ѯ */ 0
+ 0x0471, 499, /* ѱ Ѱ */ 0
+ 0x0473, 499, /* ѳ Ѳ */ 0
+ 0x0475, 499, /* ѵ Ѵ */ 0
+ 0x0477, 499, /* ѷ Ѷ */ 0
+ 0x0479, 499, /* ѹ Ѹ */ 0
+ 0x047b, 499, /* ѻ Ѻ */ 0
+ 0x047d, 499, /* ѽ Ѽ */ 0
+ 0x047f, 499, /* ѿ Ѿ */ 0
+ 0x0481, 499, /* ҁ Ҁ */ 0
+ 0x0491, 499, /* ґ Ґ */ 0
+ 0x0493, 499, /* ғ Ғ */ 0
+ 0x0495, 499, /* ҕ Ҕ */ 0
+ 0x0497, 499, /* җ Җ */ 0
+ 0x0499, 499, /* ҙ Ҙ */ 0
+ 0x049b, 499, /* қ Қ */ 0
+ 0x049d, 499, /* ҝ Ҝ */ 0
+ 0x049f, 499, /* ҟ Ҟ */ 0
+ 0x04a1, 499, /* ҡ Ҡ */ 0
+ 0x04a3, 499, /* ң Ң */ 0
+ 0x04a5, 499, /* ҥ Ҥ */ 0
+ 0x04a7, 499, /* ҧ Ҧ */ 0
+ 0x04a9, 499, /* ҩ Ҩ */ 0
+ 0x04ab, 499, /* ҫ Ҫ */ 0
+ 0x04ad, 499, /* ҭ Ҭ */ 0
+ 0x04af, 499, /* ү Ү */ 0
+ 0x04b1, 499, /* ұ Ұ */ 0
+ 0x04b3, 499, /* ҳ Ҳ */ 0
+ 0x04b5, 499, /* ҵ Ҵ */ 0
+ 0x04b7, 499, /* ҷ Ҷ */ 0
+ 0x04b9, 499, /* ҹ Ҹ */ 0
+ 0x04bb, 499, /* һ Һ */ 0
+ 0x04bd, 499, /* ҽ Ҽ */ 0
+ 0x04bf, 499, /* ҿ Ҿ */ 0
+ 0x04c2, 499, /* ӂ Ӂ */ 0
+ 0x04c4, 499, /* ӄ Ӄ */ 0
+ 0x04c8, 499, /* ӈ Ӈ */ 0
+ 0x04cc, 499, /* ӌ Ӌ */ 0
+ 0x04d1, 499, /* ӑ Ӑ */ 0
+ 0x04d3, 499, /* ӓ Ӓ */ 0
+ 0x04d5, 499, /* ӕ Ӕ */ 0
+ 0x04d7, 499, /* ӗ Ӗ */ 0
+ 0x04d9, 499, /* ә Ә */ 0
+ 0x04db, 499, /* ӛ Ӛ */ 0
+ 0x04dd, 499, /* ӝ Ӝ */ 0
+ 0x04df, 499, /* ӟ Ӟ */ 0
+ 0x04e1, 499, /* ӡ Ӡ */ 0
+ 0x04e3, 499, /* ӣ Ӣ */ 0
+ 0x04e5, 499, /* ӥ Ӥ */ 0
+ 0x04e7, 499, /* ӧ Ӧ */ 0
+ 0x04e9, 499, /* ө Ө */ 0
+ 0x04eb, 499, /* ӫ Ӫ */ 0
+ 0x04ef, 499, /* ӯ Ӯ */ 0
+ 0x04f1, 499, /* ӱ Ӱ */ 0
+ 0x04f3, 499, /* ӳ Ӳ */ 0
+ 0x04f5, 499, /* ӵ Ӵ */ 0
+ 0x04f9, 499, /* ӹ Ӹ */ 0
+ 0x1e01, 499, /* ḁ Ḁ */ 0x1
+ 0x1e03, 499, /* ḃ Ḃ */ 0x1
+ 0x1e05, 499, /* ḅ Ḅ */ 0x1
+ 0x1e07, 499, /* ḇ Ḇ */ 0x1
+ 0x1e09, 499, /* ḉ Ḉ */ 0x1
+ 0x1e0b, 499, /* ḋ Ḋ */ 0x1
+ 0x1e0d, 499, /* ḍ Ḍ */ 0x1
+ 0x1e0f, 499, /* ḏ Ḏ */ 0x1
+ 0x1e11, 499, /* ḑ Ḑ */ 0x1
+ 0x1e13, 499, /* ḓ Ḓ */ 0x1
+ 0x1e15, 499, /* ḕ Ḕ */ 0x1
+ 0x1e17, 499, /* ḗ Ḗ */ 0x1
+ 0x1e19, 499, /* ḙ Ḙ */ 0x1
+ 0x1e1b, 499, /* ḛ Ḛ */ 0x1
+ 0x1e1d, 499, /* ḝ Ḝ */ 0x1
+ 0x1e1f, 499, /* ḟ Ḟ */ 0x1
+ 0x1e21, 499, /* ḡ Ḡ */ 0x1
+ 0x1e23, 499, /* ḣ Ḣ */ 0x1
+ 0x1e25, 499, /* ḥ Ḥ */ 0x1
+ 0x1e27, 499, /* ḧ Ḧ */ 0x1
+ 0x1e29, 499, /* ḩ Ḩ */ 0x1
+ 0x1e2b, 499, /* ḫ Ḫ */ 0x1
+ 0x1e2d, 499, /* ḭ Ḭ */ 0x1
+ 0x1e2f, 499, /* ḯ Ḯ */ 0x1
+ 0x1e31, 499, /* ḱ Ḱ */ 0x1
+ 0x1e33, 499, /* ḳ Ḳ */ 0x1
+ 0x1e35, 499, /* ḵ Ḵ */ 0x1
+ 0x1e37, 499, /* ḷ Ḷ */ 0x1
+ 0x1e39, 499, /* ḹ Ḹ */ 0x1
+ 0x1e3b, 499, /* ḻ Ḻ */ 0x1
+ 0x1e3d, 499, /* ḽ Ḽ */ 0x1
+ 0x1e3f, 499, /* ḿ Ḿ */ 0x1
+ 0x1e41, 499, /* ṁ Ṁ */ 0x1
+ 0x1e43, 499, /* ṃ Ṃ */ 0x1
+ 0x1e45, 499, /* ṅ Ṅ */ 0x1
+ 0x1e47, 499, /* ṇ Ṇ */ 0x1
+ 0x1e49, 499, /* ṉ Ṉ */ 0x1
+ 0x1e4b, 499, /* ṋ Ṋ */ 0x1
+ 0x1e4d, 499, /* ṍ Ṍ */ 0x1
+ 0x1e4f, 499, /* ṏ Ṏ */ 0x1
+ 0x1e51, 499, /* ṑ Ṑ */ 0x1
+ 0x1e53, 499, /* ṓ Ṓ */ 0x1
+ 0x1e55, 499, /* ṕ Ṕ */ 0x1
+ 0x1e57, 499, /* ṗ Ṗ */ 0x1
+ 0x1e59, 499, /* ṙ Ṙ */ 0x1
+ 0x1e5b, 499, /* ṛ Ṛ */ 0x1
+ 0x1e5d, 499, /* ṝ Ṝ */ 0x1
+ 0x1e5f, 499, /* ṟ Ṟ */ 0x1
+ 0x1e61, 499, /* ṡ Ṡ */ 0x1
+ 0x1e63, 499, /* ṣ Ṣ */ 0x1
+ 0x1e65, 499, /* ṥ Ṥ */ 0x1
+ 0x1e67, 499, /* ṧ Ṧ */ 0x1
+ 0x1e69, 499, /* ṩ Ṩ */ 0x1
+ 0x1e6b, 499, /* ṫ Ṫ */ 0x1
+ 0x1e6d, 499, /* ṭ Ṭ */ 0x1
+ 0x1e6f, 499, /* ṯ Ṯ */ 0x1
+ 0x1e71, 499, /* ṱ Ṱ */ 0x1
+ 0x1e73, 499, /* ṳ Ṳ */ 0x1
+ 0x1e75, 499, /* ṵ Ṵ */ 0x1
+ 0x1e77, 499, /* ṷ Ṷ */ 0x1
+ 0x1e79, 499, /* ṹ Ṹ */ 0x1
+ 0x1e7b, 499, /* ṻ Ṻ */ 0x1
+ 0x1e7d, 499, /* ṽ Ṽ */ 0x1
+ 0x1e7f, 499, /* ṿ Ṿ */ 0x1
+ 0x1e81, 499, /* ẁ Ẁ */ 0x1
+ 0x1e83, 499, /* ẃ Ẃ */ 0x1
+ 0x1e85, 499, /* ẅ Ẅ */ 0x1
+ 0x1e87, 499, /* ẇ Ẇ */ 0x1
+ 0x1e89, 499, /* ẉ Ẉ */ 0x1
+ 0x1e8b, 499, /* ẋ Ẋ */ 0x1
+ 0x1e8d, 499, /* ẍ Ẍ */ 0x1
+ 0x1e8f, 499, /* ẏ Ẏ */ 0x1
+ 0x1e91, 499, /* ẑ Ẑ */ 0x1
+ 0x1e93, 499, /* ẓ Ẓ */ 0x1
+ 0x1e95, 499, /* ẕ Ẕ */ 0x1
+ 0x1ea1, 499, /* ạ Ạ */ 0x1
+ 0x1ea3, 499, /* ả Ả */ 0x1
+ 0x1ea5, 499, /* ấ Ấ */ 0x1
+ 0x1ea7, 499, /* ầ Ầ */ 0x1
+ 0x1ea9, 499, /* ẩ Ẩ */ 0x1
+ 0x1eab, 499, /* ẫ Ẫ */ 0x1
+ 0x1ead, 499, /* ậ Ậ */ 0x1
+ 0x1eaf, 499, /* ắ Ắ */ 0x1
+ 0x1eb1, 499, /* ằ Ằ */ 0x1
+ 0x1eb3, 499, /* ẳ Ẳ */ 0x1
+ 0x1eb5, 499, /* ẵ Ẵ */ 0x1
+ 0x1eb7, 499, /* ặ Ặ */ 0x1
+ 0x1eb9, 499, /* ẹ Ẹ */ 0x1
+ 0x1ebb, 499, /* ẻ Ẻ */ 0x1
+ 0x1ebd, 499, /* ẽ Ẽ */ 0x1
+ 0x1ebf, 499, /* ế Ế */ 0x1
+ 0x1ec1, 499, /* ề Ề */ 0x1
+ 0x1ec3, 499, /* ể Ể */ 0x1
+ 0x1ec5, 499, /* ễ Ễ */ 0x1
+ 0x1ec7, 499, /* ệ Ệ */ 0x1
+ 0x1ec9, 499, /* ỉ Ỉ */ 0x1
+ 0x1ecb, 499, /* ị Ị */ 0x1
+ 0x1ecd, 499, /* ọ Ọ */ 0x1
+ 0x1ecf, 499, /* ỏ Ỏ */ 0x1
+ 0x1ed1, 499, /* ố Ố */ 0x1
+ 0x1ed3, 499, /* ồ Ồ */ 0x1
+ 0x1ed5, 499, /* ổ Ổ */ 0x1
+ 0x1ed7, 499, /* ỗ Ỗ */ 0x1
+ 0x1ed9, 499, /* ộ Ộ */ 0x1
+ 0x1edb, 499, /* ớ Ớ */ 0x1
+ 0x1edd, 499, /* ờ Ờ */ 0x1
+ 0x1edf, 499, /* ở Ở */ 0x1
+ 0x1ee1, 499, /* ỡ Ỡ */ 0x1
+ 0x1ee3, 499, /* ợ Ợ */ 0x1
+ 0x1ee5, 499, /* ụ Ụ */ 0x1
+ 0x1ee7, 499, /* ủ Ủ */ 0x1
+ 0x1ee9, 499, /* ứ Ứ */ 0x1
+ 0x1eeb, 499, /* ừ Ừ */ 0x1
+ 0x1eed, 499, /* ử Ử */ 0x1
+ 0x1eef, 499, /* ữ Ữ */ 0x1
+ 0x1ef1, 499, /* ự Ự */ 0x1
+ 0x1ef3, 499, /* ỳ Ỳ */ 0x1
+ 0x1ef5, 499, /* ỵ Ỵ */ 0x1
+ 0x1ef7, 499, /* ỷ Ỷ */ 0x1
+ 0x1ef9, 499, /* ỹ Ỹ */ 0x1
+ 0x1f51, 508, /* ὑ Ὑ */ 0x1
+ 0x1f53, 508, /* ὓ Ὓ */ 0x1
+ 0x1f55, 508, /* ὕ Ὕ */ 0x1
+ 0x1f57, 508, /* ὗ Ὗ */ 0x1
+ 0x1fb3, 509, /* ᾳ ᾼ */ 0x1
+ 0x1fc3, 509, /* ῃ ῌ */ 0x1
+ 0x1fe5, 507, /* ῥ Ῥ */ 0x1
+ 0x1ff3, 509, /* ῳ ῼ */};
+};
+
+/*
+ * upper case ranges
+ * 3rd col is conversion excess 500
+ */
+static Rune __tolower2[] = {
+ 0x0041, 0x005a, 532, /* A-Z a-z */
+ 0x00c0, 0x00d6, 532, /* À-Ö à-ö */ 0x0
+ 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */ 0x0
+ 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */ 0x0
+ 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */ 0x0
+ 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */ 0x0
+ 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */ 0x0
+ 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */ 0x0
+ 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */ 0x0
+ 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */ 0x0
+ 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */ 0x0
+ 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */ 0x0
+ 0x0410, 0x042f, 532, /* А-Я а-я */ 0x0
+ 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */ 0x1
+ 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */ 0x1f08,
+ 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */ 0x1f18,
+ 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */ 0x1f28,
+ 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */ 0x1f38,
+ 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */ 0x1f48,
+ 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */ 0x1f68,
+ 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */ 0x1f88,
+ 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */ 0x1f98,
+ 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */ 0x1fa8,
+ 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */ 0x1fb8,
+ 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */ 0x1fba,
+ 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */ 0x1fc8,
+ 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */ 0x1fd8,
+ 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */ 0x1fda,
+ 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */ 0x1fe8,
+ 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */ 0x1fea,
+ 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */ 0x1ff8,
+ 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */ 0x1ffa,
+ 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */ 0x2160,
+ 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */ 0x24b6,
+ 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */ 0xff21,
+ 0xff21, 0xff3a, 532, /* A-Z a-z */};/*
+};
+
+/*
+ * upper case singlets
+ * 2nd col is conversion excess 500
+ */
+static Rune __tolower1[] = {
+ 0x0100, 501, /* Ā ā */ 0
+ 0x0102, 501, /* Ă ă */ 0
+ 0x0104, 501, /* Ą ą */ 0
+ 0x0106, 501, /* Ć ć */ 0
+ 0x0108, 501, /* Ĉ ĉ */ 0
+ 0x010a, 501, /* Ċ ċ */ 0
+ 0x010c, 501, /* Č č */ 0
+ 0x010e, 501, /* Ď ď */ 0
+ 0x0110, 501, /* Đ đ */ 0
+ 0x0112, 501, /* Ē ē */ 0
+ 0x0114, 501, /* Ĕ ĕ */ 0
+ 0x0116, 501, /* Ė ė */ 0
+ 0x0118, 501, /* Ę ę */ 0
+ 0x011a, 501, /* Ě ě */ 0
+ 0x011c, 501, /* Ĝ ĝ */ 0
+ 0x011e, 501, /* Ğ ğ */ 0
+ 0x0120, 501, /* Ġ ġ */ 0
+ 0x0122, 501, /* Ģ ģ */ 0
+ 0x0124, 501, /* Ĥ ĥ */ 0
+ 0x0126, 501, /* Ħ ħ */ 0
+ 0x0128, 501, /* Ĩ ĩ */ 0
+ 0x012a, 501, /* Ī ī */ 0
+ 0x012c, 501, /* Ĭ ĭ */ 0
+ 0x012e, 501, /* Į į */ 0
+ 0x0130, 301, /* İ i */
+ 0x0132, 501, /* IJ ij */ 0
+ 0x0134, 501, /* Ĵ ĵ */ 0
+ 0x0136, 501, /* Ķ ķ */ 0
+ 0x0139, 501, /* Ĺ ĺ */ 0
+ 0x013b, 501, /* Ļ ļ */ 0
+ 0x013d, 501, /* Ľ ľ */ 0
+ 0x013f, 501, /* Ŀ ŀ */ 0
+ 0x0141, 501, /* Ł ł */ 0
+ 0x0143, 501, /* Ń ń */ 0
+ 0x0145, 501, /* Ņ ņ */ 0
+ 0x0147, 501, /* Ň ň */ 0
+ 0x014a, 501, /* Ŋ ŋ */ 0
+ 0x014c, 501, /* Ō ō */ 0
+ 0x014e, 501, /* Ŏ ŏ */ 0
+ 0x0150, 501, /* Ő ő */ 0
+ 0x0152, 501, /* Œ œ */ 0
+ 0x0154, 501, /* Ŕ ŕ */ 0
+ 0x0156, 501, /* Ŗ ŗ */ 0
+ 0x0158, 501, /* Ř ř */ 0
+ 0x015a, 501, /* Ś ś */ 0
+ 0x015c, 501, /* Ŝ ŝ */ 0
+ 0x015e, 501, /* Ş ş */ 0
+ 0x0160, 501, /* Š š */ 0
+ 0x0162, 501, /* Ţ ţ */ 0
+ 0x0164, 501, /* Ť ť */ 0
+ 0x0166, 501, /* Ŧ ŧ */ 0
+ 0x0168, 501, /* Ũ ũ */ 0
+ 0x016a, 501, /* Ū ū */ 0
+ 0x016c, 501, /* Ŭ ŭ */ 0
+ 0x016e, 501, /* Ů ů */ 0
+ 0x0170, 501, /* Ű ű */ 0
+ 0x0172, 501, /* Ų ų */ 0
+ 0x0174, 501, /* Ŵ ŵ */ 0
+ 0x0176, 501, /* Ŷ ŷ */ 0
+ 0x0178, 379, /* Ÿ ÿ */ 0
+ 0x0179, 501, /* Ź ź */ 0
+ 0x017b, 501, /* Ż ż */ 0
+ 0x017d, 501, /* Ž ž */ 0
+ 0x0181, 710, /* Ɓ ɓ */ 0
+ 0x0182, 501, /* Ƃ ƃ */ 0
+ 0x0184, 501, /* Ƅ ƅ */ 0
+ 0x0186, 706, /* Ɔ ɔ */ 0
+ 0x0187, 501, /* Ƈ ƈ */ 0
+ 0x018b, 501, /* Ƌ ƌ */ 0
+ 0x0190, 703, /* Ɛ ɛ */ 0
+ 0x0191, 501, /* Ƒ ƒ */ 0
+ 0x0193, 705, /* Ɠ ɠ */ 0
+ 0x0194, 707, /* Ɣ ɣ */ 0
+ 0x0196, 711, /* Ɩ ɩ */ 0
+ 0x0197, 709, /* Ɨ ɨ */ 0
+ 0x0198, 501, /* Ƙ ƙ */ 0
+ 0x019c, 711, /* Ɯ ɯ */ 0
+ 0x019d, 713, /* Ɲ ɲ */ 0
+ 0x01a0, 501, /* Ơ ơ */ 0
+ 0x01a2, 501, /* Ƣ ƣ */ 0
+ 0x01a4, 501, /* Ƥ ƥ */ 0
+ 0x01a7, 501, /* Ƨ ƨ */ 0
+ 0x01a9, 718, /* Ʃ ʃ */ 0
+ 0x01ac, 501, /* Ƭ ƭ */ 0
+ 0x01ae, 718, /* Ʈ ʈ */ 0
+ 0x01af, 501, /* Ư ư */ 0
+ 0x01b3, 501, /* Ƴ ƴ */ 0
+ 0x01b5, 501, /* Ƶ ƶ */ 0
+ 0x01b7, 719, /* Ʒ ʒ */ 0
+ 0x01b8, 501, /* Ƹ ƹ */ 0
+ 0x01bc, 501, /* Ƽ ƽ */ 0
+ 0x01c4, 502, /* DŽ dž */ 0
+ 0x01c5, 501, /* Dž dž */ 0
+ 0x01c7, 502, /* LJ lj */ 0
+ 0x01c8, 501, /* Lj lj */ 0
+ 0x01ca, 502, /* NJ nj */ 0
+ 0x01cb, 501, /* Nj nj */ 0
+ 0x01cd, 501, /* Ǎ ǎ */ 0
+ 0x01cf, 501, /* Ǐ ǐ */ 0
+ 0x01d1, 501, /* Ǒ ǒ */ 0
+ 0x01d3, 501, /* Ǔ ǔ */ 0
+ 0x01d5, 501, /* Ǖ ǖ */ 0
+ 0x01d7, 501, /* Ǘ ǘ */ 0
+ 0x01d9, 501, /* Ǚ ǚ */ 0
+ 0x01db, 501, /* Ǜ ǜ */ 0
+ 0x01de, 501, /* Ǟ ǟ */ 0
+ 0x01e0, 501, /* Ǡ ǡ */ 0
+ 0x01e2, 501, /* Ǣ ǣ */ 0
+ 0x01e4, 501, /* Ǥ ǥ */ 0
+ 0x01e6, 501, /* Ǧ ǧ */ 0
+ 0x01e8, 501, /* Ǩ ǩ */ 0
+ 0x01ea, 501, /* Ǫ ǫ */ 0
+ 0x01ec, 501, /* Ǭ ǭ */ 0
+ 0x01ee, 501, /* Ǯ ǯ */ 0
+ 0x01f1, 502, /* DZ dz */ 0
+ 0x01f2, 501, /* Dz dz */ 0
+ 0x01f4, 501, /* Ǵ ǵ */ 0
+ 0x01fa, 501, /* Ǻ ǻ */ 0
+ 0x01fc, 501, /* Ǽ ǽ */ 0
+ 0x01fe, 501, /* Ǿ ǿ */ 0
+ 0x0200, 501, /* Ȁ ȁ */ 0
+ 0x0202, 501, /* Ȃ ȃ */ 0
+ 0x0204, 501, /* Ȅ ȅ */ 0
+ 0x0206, 501, /* Ȇ ȇ */ 0
+ 0x0208, 501, /* Ȉ ȉ */ 0
+ 0x020a, 501, /* Ȋ ȋ */ 0
+ 0x020c, 501, /* Ȍ ȍ */ 0
+ 0x020e, 501, /* Ȏ ȏ */ 0
+ 0x0210, 501, /* Ȑ ȑ */ 0
+ 0x0212, 501, /* Ȓ ȓ */ 0
+ 0x0214, 501, /* Ȕ ȕ */ 0
+ 0x0216, 501, /* Ȗ ȗ */ 0
+ 0x0386, 538, /* Ά ά */ 0
+ 0x038c, 564, /* Ό ό */ 0
+ 0x03e2, 501, /* Ϣ ϣ */ 0
+ 0x03e4, 501, /* Ϥ ϥ */ 0
+ 0x03e6, 501, /* Ϧ ϧ */ 0
+ 0x03e8, 501, /* Ϩ ϩ */ 0
+ 0x03ea, 501, /* Ϫ ϫ */ 0
+ 0x03ec, 501, /* Ϭ ϭ */ 0
+ 0x03ee, 501, /* Ϯ ϯ */ 0
+ 0x0460, 501, /* Ѡ ѡ */ 0
+ 0x0462, 501, /* Ѣ ѣ */ 0
+ 0x0464, 501, /* Ѥ ѥ */ 0
+ 0x0466, 501, /* Ѧ ѧ */ 0
+ 0x0468, 501, /* Ѩ ѩ */ 0
+ 0x046a, 501, /* Ѫ ѫ */ 0
+ 0x046c, 501, /* Ѭ ѭ */ 0
+ 0x046e, 501, /* Ѯ ѯ */ 0
+ 0x0470, 501, /* Ѱ ѱ */ 0
+ 0x0472, 501, /* Ѳ ѳ */ 0
+ 0x0474, 501, /* Ѵ ѵ */ 0
+ 0x0476, 501, /* Ѷ ѷ */ 0
+ 0x0478, 501, /* Ѹ ѹ */ 0
+ 0x047a, 501, /* Ѻ ѻ */ 0
+ 0x047c, 501, /* Ѽ ѽ */ 0
+ 0x047e, 501, /* Ѿ ѿ */ 0
+ 0x0480, 501, /* Ҁ ҁ */ 0
+ 0x0490, 501, /* Ґ ґ */ 0
+ 0x0492, 501, /* Ғ ғ */ 0
+ 0x0494, 501, /* Ҕ ҕ */ 0
+ 0x0496, 501, /* Җ җ */ 0
+ 0x0498, 501, /* Ҙ ҙ */ 0
+ 0x049a, 501, /* Қ қ */ 0
+ 0x049c, 501, /* Ҝ ҝ */ 0
+ 0x049e, 501, /* Ҟ ҟ */ 0
+ 0x04a0, 501, /* Ҡ ҡ */ 0
+ 0x04a2, 501, /* Ң ң */ 0
+ 0x04a4, 501, /* Ҥ ҥ */ 0
+ 0x04a6, 501, /* Ҧ ҧ */ 0
+ 0x04a8, 501, /* Ҩ ҩ */ 0
+ 0x04aa, 501, /* Ҫ ҫ */ 0
+ 0x04ac, 501, /* Ҭ ҭ */ 0
+ 0x04ae, 501, /* Ү ү */ 0
+ 0x04b0, 501, /* Ұ ұ */ 0
+ 0x04b2, 501, /* Ҳ ҳ */ 0
+ 0x04b4, 501, /* Ҵ ҵ */ 0
+ 0x04b6, 501, /* Ҷ ҷ */ 0
+ 0x04b8, 501, /* Ҹ ҹ */ 0
+ 0x04ba, 501, /* Һ һ */ 0
+ 0x04bc, 501, /* Ҽ ҽ */ 0
+ 0x04be, 501, /* Ҿ ҿ */ 0
+ 0x04c1, 501, /* Ӂ ӂ */ 0
+ 0x04c3, 501, /* Ӄ ӄ */ 0
+ 0x04c7, 501, /* Ӈ ӈ */ 0
+ 0x04cb, 501, /* Ӌ ӌ */ 0
+ 0x04d0, 501, /* Ӑ ӑ */ 0
+ 0x04d2, 501, /* Ӓ ӓ */ 0
+ 0x04d4, 501, /* Ӕ ӕ */ 0
+ 0x04d6, 501, /* Ӗ ӗ */ 0
+ 0x04d8, 501, /* Ә ә */ 0
+ 0x04da, 501, /* Ӛ ӛ */ 0
+ 0x04dc, 501, /* Ӝ ӝ */ 0
+ 0x04de, 501, /* Ӟ ӟ */ 0
+ 0x04e0, 501, /* Ӡ ӡ */ 0
+ 0x04e2, 501, /* Ӣ ӣ */ 0
+ 0x04e4, 501, /* Ӥ ӥ */ 0
+ 0x04e6, 501, /* Ӧ ӧ */ 0
+ 0x04e8, 501, /* Ө ө */ 0
+ 0x04ea, 501, /* Ӫ ӫ */ 0
+ 0x04ee, 501, /* Ӯ ӯ */ 0
+ 0x04f0, 501, /* Ӱ ӱ */ 0
+ 0x04f2, 501, /* Ӳ ӳ */ 0
+ 0x04f4, 501, /* Ӵ ӵ */ 0
+ 0x04f8, 501, /* Ӹ ӹ */ 0
+ 0x1e00, 501, /* Ḁ ḁ */ 0x1
+ 0x1e02, 501, /* Ḃ ḃ */ 0x1
+ 0x1e04, 501, /* Ḅ ḅ */ 0x1
+ 0x1e06, 501, /* Ḇ ḇ */ 0x1
+ 0x1e08, 501, /* Ḉ ḉ */ 0x1
+ 0x1e0a, 501, /* Ḋ ḋ */ 0x1
+ 0x1e0c, 501, /* Ḍ ḍ */ 0x1
+ 0x1e0e, 501, /* Ḏ ḏ */ 0x1
+ 0x1e10, 501, /* Ḑ ḑ */ 0x1
+ 0x1e12, 501, /* Ḓ ḓ */ 0x1
+ 0x1e14, 501, /* Ḕ ḕ */ 0x1
+ 0x1e16, 501, /* Ḗ ḗ */ 0x1
+ 0x1e18, 501, /* Ḙ ḙ */ 0x1
+ 0x1e1a, 501, /* Ḛ ḛ */ 0x1
+ 0x1e1c, 501, /* Ḝ ḝ */ 0x1
+ 0x1e1e, 501, /* Ḟ ḟ */ 0x1
+ 0x1e20, 501, /* Ḡ ḡ */ 0x1
+ 0x1e22, 501, /* Ḣ ḣ */ 0x1
+ 0x1e24, 501, /* Ḥ ḥ */ 0x1
+ 0x1e26, 501, /* Ḧ ḧ */ 0x1
+ 0x1e28, 501, /* Ḩ ḩ */ 0x1
+ 0x1e2a, 501, /* Ḫ ḫ */ 0x1
+ 0x1e2c, 501, /* Ḭ ḭ */ 0x1
+ 0x1e2e, 501, /* Ḯ ḯ */ 0x1
+ 0x1e30, 501, /* Ḱ ḱ */ 0x1
+ 0x1e32, 501, /* Ḳ ḳ */ 0x1
+ 0x1e34, 501, /* Ḵ ḵ */ 0x1
+ 0x1e36, 501, /* Ḷ ḷ */ 0x1
+ 0x1e38, 501, /* Ḹ ḹ */ 0x1
+ 0x1e3a, 501, /* Ḻ ḻ */ 0x1
+ 0x1e3c, 501, /* Ḽ ḽ */ 0x1
+ 0x1e3e, 501, /* Ḿ ḿ */ 0x1
+ 0x1e40, 501, /* Ṁ ṁ */ 0x1
+ 0x1e42, 501, /* Ṃ ṃ */ 0x1
+ 0x1e44, 501, /* Ṅ ṅ */ 0x1
+ 0x1e46, 501, /* Ṇ ṇ */ 0x1
+ 0x1e48, 501, /* Ṉ ṉ */ 0x1
+ 0x1e4a, 501, /* Ṋ ṋ */ 0x1
+ 0x1e4c, 501, /* Ṍ ṍ */ 0x1
+ 0x1e4e, 501, /* Ṏ ṏ */ 0x1
+ 0x1e50, 501, /* Ṑ ṑ */ 0x1
+ 0x1e52, 501, /* Ṓ ṓ */ 0x1
+ 0x1e54, 501, /* Ṕ ṕ */ 0x1
+ 0x1e56, 501, /* Ṗ ṗ */ 0x1
+ 0x1e58, 501, /* Ṙ ṙ */ 0x1
+ 0x1e5a, 501, /* Ṛ ṛ */ 0x1
+ 0x1e5c, 501, /* Ṝ ṝ */ 0x1
+ 0x1e5e, 501, /* Ṟ ṟ */ 0x1
+ 0x1e60, 501, /* Ṡ ṡ */ 0x1
+ 0x1e62, 501, /* Ṣ ṣ */ 0x1
+ 0x1e64, 501, /* Ṥ ṥ */ 0x1
+ 0x1e66, 501, /* Ṧ ṧ */ 0x1
+ 0x1e68, 501, /* Ṩ ṩ */ 0x1
+ 0x1e6a, 501, /* Ṫ ṫ */ 0x1
+ 0x1e6c, 501, /* Ṭ ṭ */ 0x1
+ 0x1e6e, 501, /* Ṯ ṯ */ 0x1
+ 0x1e70, 501, /* Ṱ ṱ */ 0x1
+ 0x1e72, 501, /* Ṳ ṳ */ 0x1
+ 0x1e74, 501, /* Ṵ ṵ */ 0x1
+ 0x1e76, 501, /* Ṷ ṷ */ 0x1
+ 0x1e78, 501, /* Ṹ ṹ */ 0x1
+ 0x1e7a, 501, /* Ṻ ṻ */ 0x1
+ 0x1e7c, 501, /* Ṽ ṽ */ 0x1
+ 0x1e7e, 501, /* Ṿ ṿ */ 0x1
+ 0x1e80, 501, /* Ẁ ẁ */ 0x1
+ 0x1e82, 501, /* Ẃ ẃ */ 0x1
+ 0x1e84, 501, /* Ẅ ẅ */ 0x1
+ 0x1e86, 501, /* Ẇ ẇ */ 0x1
+ 0x1e88, 501, /* Ẉ ẉ */ 0x1
+ 0x1e8a, 501, /* Ẋ ẋ */ 0x1
+ 0x1e8c, 501, /* Ẍ ẍ */ 0x1
+ 0x1e8e, 501, /* Ẏ ẏ */ 0x1
+ 0x1e90, 501, /* Ẑ ẑ */ 0x1
+ 0x1e92, 501, /* Ẓ ẓ */ 0x1
+ 0x1e94, 501, /* Ẕ ẕ */ 0x1
+ 0x1ea0, 501, /* Ạ ạ */ 0x1
+ 0x1ea2, 501, /* Ả ả */ 0x1
+ 0x1ea4, 501, /* Ấ ấ */ 0x1
+ 0x1ea6, 501, /* Ầ ầ */ 0x1
+ 0x1ea8, 501, /* Ẩ ẩ */ 0x1
+ 0x1eaa, 501, /* Ẫ ẫ */ 0x1
+ 0x1eac, 501, /* Ậ ậ */ 0x1
+ 0x1eae, 501, /* Ắ ắ */ 0x1
+ 0x1eb0, 501, /* Ằ ằ */ 0x1
+ 0x1eb2, 501, /* Ẳ ẳ */ 0x1
+ 0x1eb4, 501, /* Ẵ ẵ */ 0x1
+ 0x1eb6, 501, /* Ặ ặ */ 0x1
+ 0x1eb8, 501, /* Ẹ ẹ */ 0x1
+ 0x1eba, 501, /* Ẻ ẻ */ 0x1
+ 0x1ebc, 501, /* Ẽ ẽ */ 0x1
+ 0x1ebe, 501, /* Ế ế */ 0x1
+ 0x1ec0, 501, /* Ề ề */ 0x1
+ 0x1ec2, 501, /* Ể ể */ 0x1
+ 0x1ec4, 501, /* Ễ ễ */ 0x1
+ 0x1ec6, 501, /* Ệ ệ */ 0x1
+ 0x1ec8, 501, /* Ỉ ỉ */ 0x1
+ 0x1eca, 501, /* Ị ị */ 0x1
+ 0x1ecc, 501, /* Ọ ọ */ 0x1
+ 0x1ece, 501, /* Ỏ ỏ */ 0x1
+ 0x1ed0, 501, /* Ố ố */ 0x1
+ 0x1ed2, 501, /* Ồ ồ */ 0x1
+ 0x1ed4, 501, /* Ổ ổ */ 0x1
+ 0x1ed6, 501, /* Ỗ ỗ */ 0x1
+ 0x1ed8, 501, /* Ộ ộ */ 0x1
+ 0x1eda, 501, /* Ớ ớ */ 0x1
+ 0x1edc, 501, /* Ờ ờ */ 0x1
+ 0x1ede, 501, /* Ở ở */ 0x1
+ 0x1ee0, 501, /* Ỡ ỡ */ 0x1
+ 0x1ee2, 501, /* Ợ ợ */ 0x1
+ 0x1ee4, 501, /* Ụ ụ */ 0x1
+ 0x1ee6, 501, /* Ủ ủ */ 0x1
+ 0x1ee8, 501, /* Ứ ứ */ 0x1
+ 0x1eea, 501, /* Ừ ừ */ 0x1
+ 0x1eec, 501, /* Ử ử */ 0x1
+ 0x1eee, 501, /* Ữ ữ */ 0x1
+ 0x1ef0, 501, /* Ự ự */ 0x1
+ 0x1ef2, 501, /* Ỳ ỳ */ 0x1
+ 0x1ef4, 501, /* Ỵ ỵ */ 0x1
+ 0x1ef6, 501, /* Ỷ ỷ */ 0x1
+ 0x1ef8, 501, /* Ỹ ỹ */ 0x1
+ 0x1f59, 492, /* Ὑ ὑ */ 0x1
+ 0x1f5b, 492, /* Ὓ ὓ */ 0x1
+ 0x1f5d, 492, /* Ὕ ὕ */ 0x1
+ 0x1f5f, 492, /* Ὗ ὗ */ 0x1
+ 0x1fbc, 491, /* ᾼ ᾳ */ 0x1
+ 0x1fcc, 491, /* ῌ ῃ */ 0x1
+ 0x1fec, 493, /* Ῥ ῥ */ 0x1
+ 0x1ffc, 491, /* ῼ ῳ */};
+};
+
+/*
+ * title characters are those between
+ * upper and lower case. ie DZ Dz dz
+ */
+static Rune __totitle1[] = {
+ 0x01c4, 501, /* DŽ Dž */ 0
+ 0x01c6, 499, /* dž Dž */ 0
+ 0x01c7, 501, /* LJ Lj */ 0
+ 0x01c9, 499, /* lj Lj */ 0
+ 0x01ca, 501, /* NJ Nj */ 0
+ 0x01cc, 499, /* nj Nj */ 0
+ 0x01f1, 501, /* DZ Dz */ 0
+ 0x01f3, 499, /* dz Dz */};
+};
+
+static Rune*
+bsearch(Rune c, Rune* t, int n, int ne) {
+ Rune* p;
+ int m;
+
+ while (n > 1) {
+ m = n / 2;
+ p = t + m * ne;
+ if (c >= p[0]) {
+ t = p;
+ n = n - m;
+ } else
+ n = m;
+ }
+ if (n && c >= t[0])
+ return t;
+ return 0;
+}
+
+Rune tolowerrune(Rune c) {
+ Rune* p;
+
+ p = bsearch(c, __tolower2, nelem(__tolower2) / 3, 3);
+ if (p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, __tolower1, nelem(__tolower1) / 2, 2);
+ if (p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune toupperrune(Rune c) {
+ Rune* p;
+
+ p = bsearch(c, __toupper2, nelem(__toupper2) / 3, 3);
+ if (p && c >= p[0] && c <= p[1])
+ return c + p[2] - 500;
+ p = bsearch(c, __toupper1, nelem(__toupper1) / 2, 2);
+ if (p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+Rune totitlerune(Rune c) {
+ Rune* p;
+
+ p = bsearch(c, __totitle1, nelem(__totitle1) / 2, 2);
+ if (p && c == p[0])
+ return c + p[1] - 500;
+ return c;
+}
+
+int islowerrune(Rune c) {
+ Rune* p;
+
+ p = bsearch(c, __toupper2, nelem(__toupper2) / 3, 3);
+ if (p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __toupper1, nelem(__toupper1) / 2, 2);
+ if (p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int isupperrune(Rune c) {
+ Rune* p;
+
+ p = bsearch(c, __tolower2, nelem(__tolower2) / 3, 3);
+ if (p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __tolower1, nelem(__tolower1) / 2, 2);
+ if (p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int isalpharune(Rune c) {
+ Rune* p;
+
+ if (isupperrune(c) || islowerrune(c))
+ return 1;
+ p = bsearch(c, __alpha2, nelem(__alpha2) / 2, 2);
+ if (p && c >= p[0] && c <= p[1])
+ return 1;
+ p = bsearch(c, __alpha1, nelem(__alpha1), 1);
+ if (p && c == p[0])
+ return 1;
+ return 0;
+}
+
+int istitlerune(Rune c) {
+ return isupperrune(c) && islowerrune(c);
+}
+
+int isspacerune(Rune c) {
+ Rune* p;
+
+ p = bsearch(c, __space2, nelem(__space2) / 2, 2);
+ if (p && c >= p[0] && c <= p[1])
+ return 1;
+ return 0;
+}
diff --git a/lib/libutf/utf.7 b/lib/libutf/utf.7
@@ -0,0 +1,99 @@
+.deEX
+.ift .ft5
+.nf
+..
+.deEE
+.ft1
+.fi
+..
+.TH UTF 7
+.SH NAME
+UTF, Unicode, ASCII, rune \- character set and format
+.SH DESCRIPTION
+The Plan 9 character set and representation are
+based on the Unicode Standard and on the ISO multibyte
+.SM UTF-8
+encoding (Universal Character
+Set Transformation Format, 8 bits wide).
+The Unicode Standard represents its characters in 16
+bits;
+.SM UTF-8
+represents such
+values in an 8-bit byte stream.
+Throughout this manual,
+.SM UTF-8
+is shortened to
+.SM UTF.
+.PP
+In Plan 9, a
+.I rune
+is a 16-bit quantity representing a Unicode character.
+Internally, programs may store characters as runes.
+However, any external manifestation of textual information,
+in files or at the interface between programs, uses a
+machine-independent, byte-stream encoding called
+.SM UTF.
+.PP
+.SM UTF
+is designed so the 7-bit
+.SM ASCII
+set (values hexadecimal 00 to 7F),
+appear only as themselves
+in the encoding.
+Runes with values above 7F appear as sequences of two or more
+bytes with values only from 80 to FF.
+.PP
+The
+.SM UTF
+encoding of the Unicode Standard is backward compatible with
+.SM ASCII\c
+:
+programs presented only with
+.SM ASCII
+work on Plan 9
+even if not written to deal with
+.SM UTF,
+as do
+programs that deal with uninterpreted byte streams.
+However, programs that perform semantic processing on
+.SM ASCII
+graphic
+characters must convert from
+.SM UTF
+to runes
+in order to work properly with non-\c
+.SM ASCII
+input.
+See
+.IR rune (3).
+.PP
+Letting numbers be binary,
+a rune x is converted to a multibyte
+.SM UTF
+sequence
+as follows:
+.PP
+01. x in [00000000.0bbbbbbb] → 0bbbbbbb.b
+.br
+10. x in [00000bbb.bbbbbbbb] → 110bbbbb, 10bbbbbb.b
+.br
+11. x in [bbbbbbbb.bbbbbbbb] → 1110bbbb, 10bbbbbb, 10bbbbbb.b
+.br
+.PP
+Conversion 01 provides a one-byte sequence that spans the
+.SM ASCII
+character set in a compatible way.
+Conversions 10 and 11 represent higher-valued characters
+as sequences of two or three bytes with the high bit set.
+Plan 9 does not support the 4, 5, and 6 byte sequences proposed by X-Open.
+When there are multiple ways to encode a value, for example rune 0,
+the shortest encoding is used.
+.PP
+In the inverse mapping,
+any sequence except those described above
+is incorrect and is converted to rune hexadecimal 0080.
+.SH "SEE ALSO"
+.IR ascii (1),
+.IR tcs (1),
+.IR rune (3),
+.IR "The Unicode Standard" .
diff --git a/lib/libutf/utf.h b/lib/libutf/utf.h
@@ -0,0 +1,53 @@
+#ifndef _UTF_H_
+#define _UTF_H_ 1
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef unsigned int Rune; /* 32 bits */
+
+enum {
+ UTFmax = 4, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
+ Runeself = 0x80, /* rune and UTF sequences are the same (<) */
+ Runeerror = 0xFFFD, /* decoding error in UTF */
+ Runemax = 0x10FFFF /* maximum rune value */
+};
+
+/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */
+int chartorune(Rune* rune, char* str);
+int fullrune(char* str, int n);
+int isalpharune(Rune c);
+int islowerrune(Rune c);
+int isspacerune(Rune c);
+int istitlerune(Rune c);
+int isupperrune(Rune c);
+int runelen(long c);
+int runenlen(Rune* r, int nrune);
+Rune* runestrcat(Rune* s1, Rune* s2);
+Rune* runestrchr(Rune* s, Rune c);
+int runestrcmp(Rune* s1, Rune* s2);
+Rune* runestrcpy(Rune* s1, Rune* s2);
+Rune* runestrdup(Rune* s);
+Rune* runestrecpy(Rune* s1, Rune* es1, Rune* s2);
+long runestrlen(Rune* s);
+Rune* runestrncat(Rune* s1, Rune* s2, long n);
+int runestrncmp(Rune* s1, Rune* s2, long n);
+Rune* runestrncpy(Rune* s1, Rune* s2, long n);
+Rune* runestrrchr(Rune* s, Rune c);
+Rune* runestrstr(Rune* s1, Rune* s2);
+int runetochar(char* str, Rune* rune);
+Rune tolowerrune(Rune c);
+Rune totitlerune(Rune c);
+Rune toupperrune(Rune c);
+char* utfecpy(char* to, char* e, char* from);
+int utflen(char* s);
+int utfnlen(char* s, long m);
+char* utfrrune(char* s, long c);
+char* utfrune(char* s, long c);
+char* utfutf(char* s1, char* s2);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif
diff --git a/lib/libutf/utfdef.h b/lib/libutf/utfdef.h
@@ -0,0 +1,35 @@
+/*
+ * compiler directive on Plan 9
+ */
+#ifndef USED
+# define USED(x) \
+ if (x) \
+ ; \
+ else
+#endif
+
+/*
+ * easiest way to make sure these are defined
+ */
+#define uchar _fmtuchar
+#define ushort _fmtushort
+#define uint _fmtuint
+#define ulong _fmtulong
+#define vlong _fmtvlong
+#define uvlong _fmtuvlong
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+typedef unsigned long long uvlong;
+typedef long long vlong;
+
+/*
+ * nil cannot be ((void*)0) on ANSI C,
+ * because it is used for function pointers
+ */
+#undef nil
+#define nil 0
+
+#undef nelem
+#define nelem ((void*) 0)
diff --git a/lib/libutf/utfecpy.c b/lib/libutf/utfecpy.c
@@ -0,0 +1,37 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#define _BSD_SOURCE 1 /* memccpy */
+#define _DEFAULT_SOURCE 1
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+char* utfecpy(char* to, char* e, char* from) {
+ char* end;
+
+ if (to >= e)
+ return to;
+ end = memccpy(to, from, '\0', e - to);
+ if (end == nil) {
+ end = e - 1;
+ while (end > to && (*--end & 0xC0) == 0x80)
+ ;
+ *end = '\0';
+ } else {
+ end--;
+ }
+ return end;
+}
diff --git a/lib/libutf/utflen.c b/lib/libutf/utflen.c
@@ -0,0 +1,36 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int utflen(char* s) {
+ int c;
+ long n;
+ Rune rune;
+
+ n = 0;
+ for (;;) {
+ c = *(uchar*) s;
+ if (c < Runeself) {
+ if (c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+}
diff --git a/lib/libutf/utfnlen.c b/lib/libutf/utfnlen.c
@@ -0,0 +1,40 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+int utfnlen(char* s, long m) {
+ int c;
+ long n;
+ Rune rune;
+ char* es;
+
+ es = s + m;
+ for (n = 0; s < es; n++) {
+ c = *(uchar*) s;
+ if (c < Runeself) {
+ if (c == '\0')
+ break;
+ s++;
+ continue;
+ }
+ if (!fullrune(s, es - s))
+ break;
+ s += chartorune(&rune, s);
+ }
+ return n;
+}
diff --git a/lib/libutf/utfrrune.c b/lib/libutf/utfrrune.c
@@ -0,0 +1,44 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+char* utfrrune(char* s, long c) {
+ long c1;
+ Rune r;
+ char* s1;
+
+ if (c < Runesync) /* not part of utf sequence */
+ return strrchr(s, c);
+
+ s1 = 0;
+ for (;;) {
+ c1 = *(uchar*) s;
+ if (c1 < Runeself) { /* one byte rune */
+ if (c1 == 0)
+ return s1;
+ if (c1 == c)
+ s1 = s;
+ s++;
+ continue;
+ }
+ c1 = chartorune(&r, s);
+ if (r == c)
+ s1 = s;
+ s += c1;
+ }
+}
diff --git a/lib/libutf/utfrune.c b/lib/libutf/utfrune.c
@@ -0,0 +1,43 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "plan9.h"
+#include "utf.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+char* utfrune(char* s, long c) {
+ long c1;
+ Rune r;
+ int n;
+
+ if (c < Runesync) /* not part of utf sequence */
+ return strchr(s, c);
+
+ for (;;) {
+ c1 = *(uchar*) s;
+ if (c1 < Runeself) { /* one byte rune */
+ if (c1 == 0)
+ return 0;
+ if (c1 == c)
+ return s;
+ s++;
+ continue;
+ }
+ n = chartorune(&r, s);
+ if (r == c)
+ return s;
+ s += n;
+ }
+}
diff --git a/lib/libutf/utfutf.c b/lib/libutf/utfutf.c
@@ -0,0 +1,41 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
+ * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "utf.h"
+
+#include "plan9.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+
+/*
+ * Return pointer to first occurrence of s2 in s1,
+ * 0 if none
+ */
+char* utfutf(char* s1, char* s2) {
+ char* p;
+ long f, n1, n2;
+ Rune r;
+
+ n1 = chartorune(&r, s2);
+ f = r;
+ if (f <= Runesync) /* represents self */
+ return strstr(s1, s2);
+
+ n2 = strlen(s2);
+ for (p = s1; (p = utfrune(p, f)); p += n1)
+ if (strncmp(p, s2, n2) == 0)
+ return p;
+ return 0;
+}
diff --git a/lib/libutil.old/Makefile b/lib/libutil.old/Makefile
@@ -0,0 +1,8 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = #openwrite.o util.o
+HEADERS = #arg.h common.h open.h types.h util.h
+LIBRARY = libutil.a
+
+include $(TOPDIR)/mk/lib.mk
+\ No newline at end of file
diff --git a/lib/libutil.old/common.h b/lib/libutil.old/common.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#define SHIFT(n) (argc -= (n), argv += (n))
+
+#define THROW(condition, message, exitcode, ...) \
+ { \
+ if (condition) { \
+ fprint(1, "%s: " message ": %s\n", \
+ current_prog(), ##__VA_ARGS__, strerror(errno)); \
+ if (exitcode != -1) \
+ exit(exitcode); \
+ } \
+ }
+
+#define THROW_MIN(func, ...) THROW((func) == -1, __VA_ARGS__)
+
+#define THROW_NULL(func, ...) THROW((func) == NULL, __VA_ARGS__)
+
+#define FALLTHROUGH __attribute__((fallthrough))
+
+#define EXIT_SUCCESS 0
+#define EXIT_USER 100
+#define EXIT_TEMP 101
+#define EXIT_PERM 102
diff --git a/lib/libutil.old/open.h b/lib/libutil.old/open.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "types.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+
+int openwrite(const char* path, i32 oflags, mode_t mode, const void* buffer, ssize_t size);
+ssize_t openread(const char* path, i32 oflags, void* buffer, ssize_t maxsize);
diff --git a/lib/libutil.old/openwrite.c b/lib/libutil.old/openwrite.c
@@ -0,0 +1,17 @@
+#include "open.h"
+
+#include <unistd.h>
+
+int openwrite(const char* path, i32 oflags, mode_t mode, const void* buffer, ssize_t size) {
+ int fd;
+
+ if ((fd = open(path, O_WRONLY | O_CREAT | oflags, mode)) == -1)
+ return -1;
+
+ if (write(fd, buffer, size) != size) {
+ close(fd);
+ return -1;
+ }
+
+ return close(fd);
+}
diff --git a/lib/libutil.old/types.h b/lib/libutil.old/types.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "common.h"
+
+#include <limits.h>
+
+#if __SIZEOF_INT__ != 4
+# error "int" is not 4 bytes wide
+#endif
+
+#if __SIZEOF_LONG_LONG__ != 8
+# error "long long" is not 8 bytes wide
+#endif
+
+typedef char i8;
+typedef unsigned char u8;
+typedef short i16;
+typedef unsigned short u16;
+typedef int i32;
+typedef unsigned int u32;
+typedef long long i64;
+typedef unsigned long long u64;
+
+typedef struct {
+ int read;
+ int write;
+} pipe_t;
diff --git a/lib/libutil.old/util.c b/lib/libutil.old/util.c
@@ -0,0 +1,169 @@
+#include "util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+ssize_t dgetline(int fd, char* line, size_t line_buffer) {
+ ssize_t line_size = 0;
+ ssize_t rc;
+ char c;
+
+ while (line_size < (ssize_t) line_buffer - 1 && (rc = read(fd, &c, 1)) == 1) {
+ if (c == '\r')
+ continue;
+ if (c == '\n')
+ break;
+ line[line_size++] = c;
+ }
+ line[line_size] = '\0';
+ if (rc == -1 && line_size == 0)
+ return -1;
+ return line_size;
+}
+
+ssize_t readstr(int fd, char* str) {
+ ssize_t len = 0;
+ int rc;
+
+ while ((rc = read(fd, &str[len], 1)) == 1 && str[len] != '\0')
+ len++;
+
+ str[len] = '\0';
+ return rc == -1 ? -1 : len;
+}
+
+ssize_t writestr(int fd, const char* str) {
+ if (str == NULL)
+ return write(fd, "", 1);
+ return write(fd, str, strlen(str) + 1);
+}
+
+unsigned int stat_mode(const char* format, ...) {
+ char path[PATH_MAX];
+ struct stat st;
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(path, PATH_MAX, format, args);
+ va_end(args);
+
+ if (stat(path, &st) == 0)
+ return st.st_mode;
+
+ return -1;
+}
+
+int fork_dup_cd_exec(int dir, const char* path, int fd0, int fd1, int fd2) {
+ pid_t pid;
+
+ if ((pid = fork()) == -1) {
+ fprint(1, "error: cannot fork process: %r\n");
+ return -1;
+ } else if (pid == 0) {
+ dup2(fd0, STDIN_FILENO);
+ dup2(fd1, STDOUT_FILENO);
+ dup2(fd2, STDERR_FILENO);
+
+ fchdir(dir);
+
+ execl(path, path, NULL);
+ fprint(1, "error: cannot execute stop process: %r\n");
+ _exit(1);
+ }
+ return pid;
+}
+
+int reclaim_console(void) {
+ int ttyfd;
+
+ if ((ttyfd = open("/dev/console", O_RDWR)) == -1)
+ return -1;
+
+ dup2(ttyfd, 0);
+ dup2(ttyfd, 1);
+ dup2(ttyfd, 2);
+ if (ttyfd > 2)
+ close(ttyfd);
+
+ return 0;
+}
+
+void sigblock_all(int unblock) {
+ sigset_t ss;
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGALRM);
+ sigaddset(&ss, SIGCHLD);
+ sigaddset(&ss, SIGCONT);
+ sigaddset(&ss, SIGHUP);
+ sigaddset(&ss, SIGINT);
+ sigaddset(&ss, SIGPIPE);
+ sigaddset(&ss, SIGTERM);
+
+ sigprocmask(unblock, &ss, NULL);
+}
+
+
+long parse_long(const char* str, const char* name) {
+ char* end;
+ long l = strtol(str, &end, 10);
+
+ if (*end != '\0') {
+ fprint(1, "error: invalid %s '%s'\n", name, optarg);
+ exit(1);
+ }
+ return l;
+}
+
+char* progname(char* path) {
+ char* match;
+
+ for (;;) {
+ if ((match = strrchr(path, '/')) == NULL)
+ return path;
+
+ if (match[1] != '\0')
+ return match + 1;
+
+ *match = '\0';
+ }
+ return path;
+}
+
+int fd_set_flag(int fd, int flags) {
+ int rc;
+
+ if ((rc = fcntl(fd, F_GETFL)) == -1)
+ return -1;
+
+ rc |= flags;
+
+ if (fcntl(fd, F_SETFL, rc) == -1)
+ return -1;
+
+ return rc;
+}
+
+void path_join(char* buffer, const char* component, ...) {
+ va_list va;
+ int index = 0;
+
+ va_start(va, component);
+
+ do {
+ if (!index)
+ buffer[index++] = '/';
+ while (*component)
+ buffer[index++] = *(component++);
+ } while ((component = va_arg(va, const char*)));
+ buffer[index] = '\0';
+}
diff --git a/lib/libutil.old/util.h b/lib/libutil.old/util.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <stdio.h>
+
+#define LEN(arr) (sizeof(arr) / sizeof(*arr))
+
+#define streq(a, b) (!strcmp((a), (b)))
+
+#define print_errno(msg, ...) (fprint(1, "%s: " msg, current_prog(), ##__VA_ARGS__, strerror(errno)))
+#define print_error(msg, ...) (fprint(1, "%s: " msg, current_prog(), ##__VA_ARGS__))
+
+// void errprint(const char* format, ...);
+
+// extern const char* prog; // has to be defined per program
+const char* current_prog(void);
+
+ssize_t dgetline(int fd, char* line, size_t line_buffer);
+ssize_t readstr(int fd, char* str);
+ssize_t writestr(int fd, const char* str);
+unsigned int stat_mode(const char* format, ...);
+int fork_dup_cd_exec(int dir, const char* path, int fd0, int fd1, int fd2);
+int reclaim_console(void);
+void sigblock_all(int unblock);
+long parse_long(const char* str, const char* name);
+char* progname(char* path);
+int fd_set_flag(int fd, int flags);
+void path_join(char* buffer, const char* component, ...);
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
@@ -0,0 +1,8 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = arg.o
+HEADERS = arg.h common.h
+LIBRARY = libutil.a
+
+include $(TOPDIR)/mk/lib.mk
+\ No newline at end of file
diff --git a/lib/libutil/arg.c b/lib/libutil/arg.c
@@ -0,0 +1,3 @@
+#include "arg.h"
+
+char* argv0;
diff --git a/lib/libutil/arg.h b/lib/libutil/arg.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <utf.h>
+
+/* command line */
+extern char* argv0;
+
+#define ARGBEGIN \
+ for ((argv0 ? 0 : (argv0 = *argv)), argv++, argc--; \
+ argv[0] && argv[0][0] == '-' && argv[0][1]; \
+ argc--, argv++) { \
+ char *_args, *_argt; \
+ Rune _argc; \
+ _args = &argv[0][1]; \
+ if (_args[0] == '-' && _args[1] == 0) { \
+ argc--; \
+ argv++; \
+ break; \
+ } \
+ _argc = 0; \
+ while (*_args && (_args += chartorune(&_argc, _args))) \
+ switch (_argc)
+#define ARGEND \
+ SET(_argt); \
+ USED(_argt); \
+ USED(_argc); \
+ USED(_args); \
+ } \
+ USED(argv); \
+ USED(argc);
+
+#define ARGF() (_argt = _args, _args = "", \
+ (*_argt ? _argt : argv[1] ? (argc--, *++argv) \
+ : 0))
+#define EARGF(x) (_argt = _args, _args = "", \
+ (*_argt ? _argt : argv[1] ? (argc--, *++argv) \
+ : ((x), abort(), (char*) 0)))
+
+#define ARGC() _argc
+
+#define SHIFT(by) (argc -= (by), argv += (by))
diff --git a/lib/libutil/common.h b/lib/libutil/common.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#define streq(a, b) (!strcmp(a, b))
+
+#define nil ((void*) 0)
+#define LEN(arr) (sizeof(arr) / sizeof(*arr))
+
+#define FALLTHROUGH __attribute__((fallthrough))
+
+#define FOREACH(iter, array) for (int(iter) = 0; (iter) < LEN(array); (iter)++)
+
+#define SET(x) ((x) = 0)
+#define USED(x) \
+ if (x) { \
+ } else { \
+ }
+#ifdef __GNUC__
+# if __GNUC__ >= 3
+# undef USED
+# define USED(x) ((void) (x))
+# endif
+#endif
diff --git a/mk/lib.mk b/mk/lib.mk
@@ -0,0 +1,16 @@
+include $(TOPDIR)/mk/phony.mk
+
+all: compile_flags.txt
+
+clean:
+ @echo "[ RM ] $(OBJS) $(LIBRARY) compile_flags.txt"
+ $(SILENT)-rm -f $(OBJS) $(LIBRARY) compile_flags.txt
+
+.PRECIOUS: $(OBJS)
+
+include $(TOPDIR)/mk/object.mk
+
+# Archives
+$(LIBRARY): $(OBJS)
+ @echo "[ AR ] $@"
+ $(SILENT)$(AR) $(ARFLAGS) $@ $^
+\ No newline at end of file
diff --git a/mk/object.mk b/mk/object.mk
@@ -0,0 +1,17 @@
+
+# only make compile_flags.txt if there are objects
+compile_flags.txt: Makefile
+ifneq "$(wildcard *.c)$(wildcard *.h)" ""
+ @echo "[ECHO] $@";
+ $(SILENT)echo $(CPPFLAGS) $(CFLAGS) | tr " " "\n" > $@
+endif
+
+# Object rules
+%.o: %.c | $(HEADERS)
+ @echo "[ CC ] $< -> $@"
+ $(SILENT)$(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+# Object rules
+%.o: %.cc | $(HEADERS)
+ @echo "[ CC ] $< -> $@"
+ $(SILENT)$(CXX) -c -o $@ $< $(CXXFLAGS) $(CPPFLAGS)
diff --git a/mk/phony.mk b/mk/phony.mk
@@ -0,0 +1,3 @@
+.PHONY: all binaries pages manuals clean
+.PHONY: install install-binaries install-manuals install-pages
+.PHONY: uninstall uninstall-binaries uninstall-manuals uninstall-pages
diff --git a/mk/prog.mk b/mk/prog.mk
@@ -1,10 +1,8 @@
--include $(TOPDIR)/config.mk
+include $(TOPDIR)/mk/phony.mk
-.PHONY: all binaries pages manuals clean
-.PHONY: install install-binaries install-manuals install-pages
-.PHONY: uninstall uninstall-binaries uninstall-manuals uninstall-pages
+CLEAN = $(OBJS) $(BINS:=.o) $(BINS) $(MANS) $(PAGES) compile_flags.txt
-all: binaries pages manuals
+all: binaries pages manuals compile_flags.txt
binaries: $(BINS)
@@ -13,8 +11,11 @@ manuals: $(MANS)
pages: $(PAGES)
clean:
- @echo "[ RM ] $(OBJS) $(BINS:=.o) $(BINS) $(MANS) $(PAGES)"
- $(SILENT)-rm -f $(OBJS) $(BINS:=.o) $(BINS) $(MANS) $(PAGES)
+ @echo "[ RM ] $(CLEAN)"
+ $(SILENT)-rm -f $(CLEAN)
+ @for lib in $(LIBS); do \
+ make -C `dirname $$lib` clean; \
+ done
install: install-binaries install-manuals install-pages
@@ -61,30 +62,36 @@ uninstall-pages:
@echo "[ RM ] $(PREFIX)/share/doc/fiss";
$(SILENT)rm -fr $(PREFIX)/share/doc/fiss;
-# Object rules
-%.o: %.c | $(HEADERS)
- @echo "[ CC ] $< -> $@"
- $(SILENT)$(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+.PRECIOUS: $(BINS:=.o)
+
+include $(TOPDIR)/mk/object.mk
# Executables
-%: %.o $(OBJS)
+%: %.o $(OBJS) $(LIBS)
@echo "[ LD ] $@"
$(SILENT)$(CC) -o $@ $^ $(LDFLAGS)
+# Shell Executables
%: %.sh
@echo "[COPY] $< -> $@"
$(SILENT)cp $< $@
$(SILENT)chmod +x $@
+# Symbolic Links
%: %.lnk
@echo "[LINK] $< -> $@"
$(SILENT)ln -sf $(shell cat $<) $@
-# Documentation and Manual
-%.html: %.txt
+# Documentation
+%.html: %.txt
@echo "[MDOC] $< -> $@"
- $(SILENT)$(SED) $(IN_REPLACE) $< | $(PYTHON) $(TOPDIR)/tools/make-docs.py > $@
+ $(SILENT)$(SED) $(IN_REPLACE) $< | $(PYTHON) $(TOPDIR)/tools/make-docs.py $(TOPDIR)/assets/template.html > $@
+# Manual
%: %.txt
@echo "[MMAN] $< -> $@"
- $(SILENT)$(SED) $(IN_REPLACE) $< | $(PYTHON) $(TOPDIR)/tools/make-man.py | $(AWK) '/./ { print }' > $@
-\ No newline at end of file
+ $(SILENT)$(SED) $(IN_REPLACE) $< | $(PYTHON) $(TOPDIR)/tools/make-man.py | $(AWK) '/./ { print }' > $@
+
+# Libraries
+$(LIBDIR)/%:
+ make -C $(dir $@) $(notdir $@)
+\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
@@ -1,6 +1,7 @@
TOPDIR = ..
-include $(TOPDIR)/config.mk
-SUBDIRS += chpst finit fsvc halt modules-load seedrng vlogger zzz
+#SUBDIRS += chpst fsvc halt modules-load seedrng serdo vlogger zzz test
+SUBDIRS = chpst finit modules-load seedrng serdo
include $(TOPDIR)/mk/subdir.mk
diff --git a/src/chpst/Makefile b/src/chpst/Makefile
@@ -1,11 +1,12 @@
TOPDIR=../..
-include $(TOPDIR)/config.mk
-OBJS = parse.o ../common/util.o
+OBJS = parse.o
BINS = chpst envuidgid pgrphack setlock setuidgid softlimit
INTERM = chpst.8.txt
MANS = chpst.8
PAGES = chpst.8.html
-HEADERS = parse.h ../common/util.h
+HEADERS = parse.h
+LIBS = $(LIBDIR)/libutil/libutil.a $(LIBDIR)/libfmt/libfmt.a $(LIBDIR)/libutf/libutf.a
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/chpst/chpst.c b/src/chpst/chpst.c
@@ -1,28 +1,36 @@
-#include "../common/util.h"
#include "parse.h"
-#include <errno.h>
+#include <arg.h>
+#include <common.h>
#include <fcntl.h>
+#include <fmt.h>
#include <grp.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/resource.h>
+#include <unistd.h>
-#define SHIFT(argc, argv, by) ((argc) -= (by), (argv) += (by))
-
-
-const char* current_prog(void) {
- return "chpst";
+void usage(void) {
+ print("Usage:\n"
+ " chpst [-vP012] [-u user[:group]] [-U user[:group]] [-b argv0] [-e dir]\n"
+ " [-/ root] [-C pwd] [-n nice] [-l|-L lock] [-m n] [-d n] [-o n] [-p n] [-f n]\n"
+ " [-c n] command ...\n"
+ " softlimit [-m n] [-a n] [-d n] [-o n] [-p n] [-f n] [-c n] [-r n] [-t n]\n"
+ " [-l n] [-s n] command ...\n"
+ " setuidgid user[:group] command ...\n"
+ " envuidgid user[:group] command ...\n"
+ " pgrphack command ...\n"
+ " setlock [-nNxX] lock command ...\n");
}
void limit(int what, long l) {
struct rlimit r;
if (getrlimit(what, &r) == -1)
- fprintf(stderr, "error: unable to getrlimit\n");
+ fprint(1, "error: unable to getrlimit: %r\n");
if (l < 0) {
r.rlim_cur = 0;
@@ -32,16 +40,18 @@ void limit(int what, long l) {
r.rlim_cur = l;
if (setrlimit(what, &r) == -1)
- fprintf(stderr, "error: unable to setrlimit\n");
+ fprint(1, "error: unable to setrlimit: %r\n");
}
int main(int argc, char** argv) {
- int opt, lockfd, lockflags, gid_len = 0;
- char *arg0 = NULL, *root = NULL, *cd = NULL, *lock = NULL, *exec = NULL;
+ int lockfd, lockflags = 0, gid_len = 0;
+ char *arg0 = NULL, *root = NULL, *cd = NULL, *lock = NULL;
uid_t uid = 0;
gid_t gid[61];
+ argv0 = argv[0];
+
long limitd = -2,
limits = -2,
limitl = -2,
@@ -56,160 +66,152 @@ int main(int argc, char** argv) {
bool ssid = false;
bool closestd[3] = { false, false, false };
- if (streq(argv[0], "setuidgid") || streq(argv[0], "envuidgid")) {
+ if (streq(argv0, "setuidgid") || streq(argv0, "envuidgid")) {
if (argc < 2) {
- fprintf(stderr, "%s <uid-gid> command...", argv[0]);
+ fprint(1, "%s <uid-gid> command...", argv0);
return 1;
}
gid_len = parse_ugid(argv[1], &uid, gid);
- SHIFT(argc, argv, 2);
- } else if (streq(argv[0], "pgrphack")) {
+ SHIFT(2);
+ } else if (streq(argv0, "pgrphack")) {
ssid = true;
- SHIFT(argc, argv, 1);
- } else if (streq(argv[0], "setlock")) {
- while ((opt = getopt(argc, argv, "+xXnN")) != -1) {
- switch (opt) {
- case 'n':
- lockflags = LOCK_EX | LOCK_NB;
- break;
- case 'N':
- lockflags = LOCK_EX;
- break;
- case 'x':
- case 'X':
- fprintf(stderr, "warning: '-%c' is ignored\n", optopt);
- break;
- case '?':
- fprintf(stderr, "%s [-xXnN] command...", argv[0]);
- return 1;
- }
+ SHIFT(1);
+ } else if (streq(argv0, "setlock")) {
+ ARGBEGIN {
+ case 'n':
+ lockflags = LOCK_EX | LOCK_NB;
+ break;
+ case 'N':
+ lockflags = LOCK_EX;
+ break;
+ case 'x':
+ case 'X':
+ fprint(1, "warning: '-%c' is ignored\n", ARGC());
+ break;
+ default:
+ usage();
+ return 1;
}
- SHIFT(argc, argv, optind);
+ ARGEND;
+
if (argc < 1) {
- fprintf(stderr, "%s [-xXnN] command...", argv[0]);
+ fprint(1, "%s [-xXnN] command...", argv0);
return 1;
}
- lock = argv[0];
- SHIFT(argc, argv, 1);
- } else if (streq(argv[0], "softlimit")) {
- while ((opt = getopt(argc, argv, "+a:c:d:f:l:m:o:p:r:s:t:")) != -1) {
- switch (opt) {
- case 'm':
- limits = limitl = limita = limitd = parse_long(optarg, "limit");
- break;
- case 'a':
- limita = parse_long(optarg, "limit");
- break;
- case 'd':
- limitd = parse_long(optarg, "limit");
- break;
- case 'o':
- limito = parse_long(optarg, "limit");
- break;
- case 'p':
- limitp = parse_long(optarg, "limit");
- break;
- case 'f':
- limitf = parse_long(optarg, "limit");
- break;
- case 'c':
- limitc = parse_long(optarg, "limit");
- break;
- case 'r':
- limitr = parse_long(optarg, "limit");
- break;
- case 't':
- limitt = parse_long(optarg, "limit");
- break;
- case 'l':
- limitl = parse_long(optarg, "limit");
- break;
- case 's':
- limits = parse_long(optarg, "limit");
- break;
- case '?':
- fprintf(stderr, "softlimit command...");
- return 1;
- }
+ lock = argv[1];
+ SHIFT(1);
+ } else if (streq(argv0, "softlimit")) {
+ ARGBEGIN {
+ case 'm':
+ limits = limitl = limita = limitd = atol(EARGF(usage()));
+ break;
+ case 'a':
+ limita = atol(EARGF(usage()));
+ break;
+ case 'd':
+ limitd = atol(EARGF(usage()));
+ break;
+ case 'o':
+ limito = atol(EARGF(usage()));
+ break;
+ case 'p':
+ limitp = atol(EARGF(usage()));
+ break;
+ case 'f':
+ limitf = atol(EARGF(usage()));
+ break;
+ case 'c':
+ limitc = atol(EARGF(usage()));
+ break;
+ case 'r':
+ limitr = atol(EARGF(usage()));
+ break;
+ case 't':
+ limitt = atol(EARGF(usage()));
+ break;
+ case 'l':
+ limitl = atol(EARGF(usage()));
+ break;
+ case 's':
+ limits = atol(EARGF(usage()));
+ break;
+ default:
+ usage();
+ return 1;
}
- SHIFT(argc, argv, optind);
+ ARGEND;
} else {
- if (!streq(argv[0], "chpst"))
- fprintf(stderr, "warning: program-name unsupported, asuming `chpst`\n");
-
- while ((opt = getopt(argc, argv, "+u:U:b:e:m:d:o:p:f:c:r:t:/:C:n:l:L:vP012V")) != -1) {
- switch (opt) {
- case 'u':
- case 'U':
- gid_len = parse_ugid(optarg, &uid, gid);
- break;
- case 'b':
- arg0 = optarg;
- break;
- case '/':
- root = optarg;
- break;
- case 'C':
- cd = optarg;
- break;
- case 'n':
- nicelevel = parse_long(optarg, "nice-level");
- break;
- case 'l':
- lock = optarg;
- lockflags = LOCK_EX | LOCK_NB;
- break;
- case 'L':
- lock = optarg;
- lockflags = LOCK_EX;
- break;
- case 'v': // ignored
- break;
- case 'P':
- ssid = true;
- break;
- case '0':
- case '1':
- case '2':
- closestd[opt - '0'] = true;
- break;
- case 'm':
- limits = limitl = limita = limitd = parse_long(optarg, "limit");
- break;
- case 'd':
- limitd = parse_long(optarg, "limit");
- break;
- case 'o':
- limito = parse_long(optarg, "limit");
- break;
- case 'p':
- limitp = parse_long(optarg, "limit");
- break;
- case 'f':
- limitf = parse_long(optarg, "limit");
- break;
- case 'c':
- limitc = parse_long(optarg, "limit");
- break;
- case 'r':
- limitr = parse_long(optarg, "limit");
- break;
- case 't':
- limitt = parse_long(optarg, "limit");
- break;
- case 'e':
- fprintf(stderr, "warning: '-%c' is ignored\n", optopt);
- break;
- case '?':
- fprintf(stderr, "usage\n");
- return 1;
- }
+ ARGBEGIN {
+ case 'u':
+ case 'U':
+ gid_len = parse_ugid(EARGF(usage()), &uid, gid);
+ break;
+ case 'b':
+ arg0 = EARGF(usage());
+ break;
+ case '/':
+ root = EARGF(usage());
+ break;
+ case 'C':
+ cd = EARGF(usage());
+ break;
+ case 'n':
+ nicelevel = atol(EARGF(usage()));
+ break;
+ case 'l':
+ lock = EARGF(usage());
+ lockflags = LOCK_EX | LOCK_NB;
+ break;
+ case 'L':
+ lock = EARGF(usage());
+ lockflags = LOCK_EX;
+ break;
+ case 'v': // ignored
+ break;
+ case 'P':
+ ssid = true;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ closestd[ARGC() - '0'] = true;
+ break;
+ case 'm':
+ limits = limitl = limita = limitd = atol(EARGF(usage()));
+ break;
+ case 'd':
+ limitd = atol(EARGF(usage()));
+ break;
+ case 'o':
+ limito = atol(EARGF(usage()));
+ break;
+ case 'p':
+ limitp = atol(EARGF(usage()));
+ break;
+ case 'f':
+ limitf = atol(EARGF(usage()));
+ break;
+ case 'c':
+ limitc = atol(EARGF(usage()));
+ break;
+ case 'r':
+ limitr = atol(EARGF(usage()));
+ break;
+ case 't':
+ limitt = atol(EARGF(usage()));
+ break;
+ case 'e':
+ fprint(1, "warning: '-%c' is ignored\n", ARGC());
+ break;
+ default:
+ usage();
+ return 1;
}
- SHIFT(argc, argv, optind);
+ ARGEND;
}
if (argc == 0) {
- fprintf(stderr, "%s: command required\n", argv[0]);
+ fprint(1, "%s: command required\n", argv0);
return 1;
}
@@ -226,7 +228,7 @@ int main(int argc, char** argv) {
if (root) {
if (chroot(root) == -1)
- print_errno("unable to change root directory: %s\n");
+ fprint(1, "unable to change root directory: %r\n");
// chdir to '/', otherwise the next command will complain 'directory not found'
chdir("/");
@@ -238,7 +240,7 @@ int main(int argc, char** argv) {
if (nicelevel != 0) {
if (nice(nicelevel) == -1)
- print_errno("unable to set nice level: %s\n");
+ fprint(1, "unable to set nice level: %r\n");
}
if (limitd >= -1) {
@@ -246,7 +248,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_DATA, limitd);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_DATA\n");
+ fprint(1, "system does not support RLIMIT_DATA\n");
#endif
}
if (limits >= -1) {
@@ -254,7 +256,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_STACK, limits);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_STACK\n");
+ fprint(1, "system does not support RLIMIT_STACK\n");
#endif
}
if (limitl >= -1) {
@@ -262,7 +264,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_MEMLOCK, limitl);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_MEMLOCK\n");
+ fprint(1, "system does not support RLIMIT_MEMLOCK\n");
#endif
}
if (limita >= -1) {
@@ -273,7 +275,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_AS, limita);
# else
if (verbose)
- fprintf(stderr, "system does neither support RLIMIT_VMEM nor RLIMIT_AS\n");
+ fprint(1, "system does neither support RLIMIT_VMEM nor RLIMIT_AS\n");
# endif
#endif
}
@@ -285,7 +287,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_OFILE, limito);
# else
if (verbose)
- fprintf(stderr, "system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE\n");
+ fprint(1, "system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE\n");
# endif
#endif
}
@@ -294,7 +296,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_NPROC, limitp);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_NPROC\n");
+ fprint(1, "system does not support RLIMIT_NPROC\n");
#endif
}
if (limitf >= -1) {
@@ -302,7 +304,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_FSIZE, limitf);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_FSIZE\n");
+ fprint(1, "system does not support RLIMIT_FSIZE\n");
#endif
}
if (limitc >= -1) {
@@ -310,7 +312,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_CORE, limitc);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_CORE\n");
+ fprint(1, "system does not support RLIMIT_CORE\n");
#endif
}
if (limitr >= -1) {
@@ -318,7 +320,7 @@ int main(int argc, char** argv) {
limit(RLIMIT_RSS, limitr);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_RSS\n");
+ fprint(1, "system does not support RLIMIT_RSS\n");
#endif
}
if (limitt >= -1) {
@@ -326,29 +328,22 @@ int main(int argc, char** argv) {
limit(RLIMIT_CPU, limitt);
#else
if (verbose)
- fprintf(stderr, "system does not support RLIMIT_CPU\n");
+ fprint(1, "system does not support RLIMIT_CPU\n");
#endif
}
if (lock) {
if ((lockfd = open(lock, O_WRONLY | O_APPEND)) == -1)
- print_errno("unable to open lock: %s\n");
+ fprint(1, "unable to open lock: %r\n");
if (flock(lockfd, lockflags) == -1)
- print_errno("unable to lock: %s\n");
+ fprint(1, "unable to lock: %r\n");
}
- if (closestd[0] && close(0) == -1)
- print_errno("unable to close stdin: %s\n");
- if (closestd[1] && close(1) == -1)
- print_errno("unable to close stdout: %s\n");
- if (closestd[2] && close(2) == -1)
- print_errno("unable to close stderr: %s\n");
-
- exec = argv[0];
- if (arg0)
- argv[0] = arg0;
+ for (int i = 0; i < 3; i++)
+ if (closestd[i] && close(i) == -1)
+ fprint(1, "unable to close std-%d: %r\n", i);
- execvp(exec, argv);
- print_errno("cannot execute: %s\n");
+ execvp(arg0 ? arg0 : *argv, argv);
+ fprint(1, "cannot execute: %r\n");
}
diff --git a/src/chpst/parse.c b/src/chpst/parse.c
@@ -2,7 +2,6 @@
#include <fcntl.h>
#include <grp.h>
-#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/src/common/util.c b/src/common/util.c
@@ -1,153 +0,0 @@
-#include "util.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-
-ssize_t dgetline(int fd, char* line, size_t line_buffer) {
- ssize_t line_size = 0;
- ssize_t rc;
- char c;
-
- while (line_size < (ssize_t) line_buffer - 1 && (rc = read(fd, &c, 1)) == 1) {
- if (c == '\r')
- continue;
- if (c == '\n')
- break;
- line[line_size++] = c;
- }
- line[line_size] = '\0';
- if (rc == -1 && line_size == 0)
- return -1;
- return line_size;
-}
-
-ssize_t readstr(int fd, char* str) {
- ssize_t len = 0;
- int rc;
-
- while ((rc = read(fd, &str[len], 1)) == 1 && str[len] != '\0')
- len++;
-
- str[len] = '\0';
- return rc == -1 ? -1 : len;
-}
-
-ssize_t writestr(int fd, const char* str) {
- if (str == NULL)
- return write(fd, "", 1);
- return write(fd, str, strlen(str) + 1);
-}
-
-unsigned int stat_mode(const char* format, ...) {
- char path[PATH_MAX];
- struct stat st;
- va_list args;
-
- va_start(args, format);
- vsnprintf(path, PATH_MAX, format, args);
- va_end(args);
-
- if (stat(path, &st) == 0)
- return st.st_mode;
-
- return 0;
-}
-
-int fork_dup_cd_exec(int dir, const char* path, int fd0, int fd1, int fd2) {
- pid_t pid;
-
- if ((pid = fork()) == -1) {
- print_errno("error: cannot fork process: %s\n");
- return -1;
- } else if (pid == 0) {
- dup2(fd0, STDIN_FILENO);
- dup2(fd1, STDOUT_FILENO);
- dup2(fd2, STDERR_FILENO);
-
- fchdir(dir);
-
- execl(path, path, NULL);
- print_errno("error: cannot execute stop process: %s\n");
- _exit(1);
- }
- return pid;
-}
-
-int reclaim_console(void) {
- int ttyfd;
-
- if ((ttyfd = open("/dev/console", O_RDWR)) == -1)
- return -1;
-
- dup2(ttyfd, 0);
- dup2(ttyfd, 1);
- dup2(ttyfd, 2);
- if (ttyfd > 2)
- close(ttyfd);
-
- return 0;
-}
-
-void sigblock_all(int unblock) {
- sigset_t ss;
-
- sigemptyset(&ss);
- sigaddset(&ss, SIGALRM);
- sigaddset(&ss, SIGCHLD);
- sigaddset(&ss, SIGCONT);
- sigaddset(&ss, SIGHUP);
- sigaddset(&ss, SIGINT);
- sigaddset(&ss, SIGPIPE);
- sigaddset(&ss, SIGTERM);
-
- sigprocmask(unblock, &ss, NULL);
-}
-
-
-long parse_long(const char* str, const char* name) {
- char* end;
- long l = strtol(str, &end, 10);
-
- if (*end != '\0') {
- fprintf(stderr, "error: invalid %s '%s'\n", name, optarg);
- exit(1);
- }
- return l;
-}
-
-char* progname(char* path) {
- char* match;
-
- for (;;) {
- if ((match = strrchr(path, '/')) == NULL)
- return path;
-
- if (match[1] != '\0')
- return match + 1;
-
- *match = '\0';
- }
- return path;
-}
-
-int fd_set_flag(int fd, int flags) {
- int rc;
-
- if ((rc = fcntl(fd, F_GETFL)) == -1)
- return -1;
-
- rc |= flags;
-
- if (fcntl(fd, F_SETFL, rc) == -1)
- return -1;
-
- return rc;
-}
diff --git a/src/common/util.h b/src/common/util.h
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <stdio.h>
-#include <time.h>
-
-#define streq(a, b) (!strcmp((a), (b)))
-
-#define print_errno(msg, ...) (fprintf(stderr, "%s: " msg, current_prog(), ##__VA_ARGS__, strerror(errno)))
-#define print_error(msg, ...) (fprintf(stderr, "%s: " msg, current_prog(), ##__VA_ARGS__))
-
-typedef struct {
- int read;
- int write;
-} pipe_t;
-
-const char* current_prog(void); // has to be defined per program
-
-ssize_t dgetline(int fd, char* line, size_t line_buffer);
-ssize_t readstr(int fd, char* str);
-ssize_t writestr(int fd, const char* str);
-unsigned int stat_mode(const char* format, ...);
-int fork_dup_cd_exec(int dir, const char* path, int fd0, int fd1, int fd2);
-int reclaim_console(void);
-void sigblock_all(int unblock);
-long parse_long(const char* str, const char* name);
-char* progname(char* path);
-int fd_set_flag(int fd, int flags);
diff --git a/src/finit/LICENSE b/src/finit/LICENSE
@@ -0,0 +1,21 @@
+MIT/X Consortium License
+
+© 2014-2015 Dimitris Papastamos <[email protected]>
+
+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 substantial 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.
diff --git a/src/finit/Makefile b/src/finit/Makefile
@@ -1,15 +1,14 @@
-TOPDIR = ../..
+TOPDIR=../..
-include $(TOPDIR)/config.mk
-BINS += fsvs finit
-OBJS += message.o supervise.o service.o start.o \
- stop.o register.o handle_exit.o handle_command.o \
- encode.o dependency.o status.o stage.o ../common/util.o
+OBJS =
+BINS = finit
+INTERM =
+MANS =
+PAGES =
+HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a $(LIBDIR)/libfmt/libfmt.a $(LIBDIR)/libutf/libutf.a
-HEADERS += ../common/util.h message.h service.h stage.h
-MANS += finit.8 fsvs.8
-PAGES += finit.8.html fsvs.8.html
-
-finit: LDFLAGS += -static
+finit_FLAGS = -static
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/finit/README b/src/finit/README
@@ -0,0 +1,26 @@
+sinit - suckless init
+=====================
+
+sinit is a simple init. It was initially based on
+Rich Felker's minimal init[1].
+
+Why?
+----
+
+I wanted to get rid of Busybox init on my toy distro[2].
+
+How?
+----
+
+There are 3 signals that sinit will act on.
+
+ SIGUSR1: powers off the machine.
+ SIGINT: reboots the machine (or alternatively via ctrl-alt-del).
+ SIGCHLD: reap children
+
+To see how sinit integrates with the init scripts, then have
+a look at [3].
+
+[1] https://gist.github.com/rofl0r/6168719
+[2] http://git.2f30.org/morpheus/
+[3] http://git.2f30.org/ports/tree/fs/
diff --git a/src/finit/dependency.c b/src/finit/dependency.c
@@ -1,60 +0,0 @@
-#include "../common/util.h"
-#include "config.h"
-#include "service.h"
-
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-
-static bool circular_dependency(struct service* s, struct service* d) {
- if (s == d)
- return true;
-
- for (int i = 0; i < s->parents_size; i++) {
- if (circular_dependency(s->parents[i], d))
- return true;
- }
-
- return false;
-}
-
-void service_add_dependency(struct service* s, struct service* d) {
- if (circular_dependency(s, d)) {
- print_error("warning: detected circular dependency while adding %s to %s\n", d->name, s->name);
- return;
- }
-
- s->children[s->children_size++] = d;
- d->parents[s->parents_size++] = s;
-}
-
-void service_update_dependency(struct service* s) {
- struct service* dep;
- int depends_file;
- char line[SV_NAME_MAX];
-
- if (s->log_service) { // aka keep first entry (the log service) if a log service is used
- service_add_dependency(s, s->log_service);
- }
-
- if ((depends_file = openat(s->dir, "depends", O_RDONLY)) == -1)
- return;
-
- while (dgetline(depends_file, line, sizeof(line)) > 0) {
- if (streq(s->name, line)) {
- fprintf(stderr, "warning: %s depends on itself\n", s->name);
- continue;
- }
-
- if ((dep = service_get(line)) == NULL) {
- fprintf(stderr, "warning: %s depends on %s: dependency not found\n", s->name, line);
- continue;
- }
- service_add_dependency(s, dep);
- }
-
- close(depends_file);
-}
diff --git a/src/finit/finit.8.txt b/src/finit/finit.8.txt
@@ -1,69 +0,0 @@
-@man finit 8 "MAY 2023" "%VERSION%" "fiss man page"
-@header finit(8) %VERSION%
-
-@title name Name
-
-*fiss* - a UNIX process no 1
-
-
-@title synopsis Synopsis
-@list
-*fsvc*
-when running as PID 0 (as init)
-
-*fsvc* <0|6>
-when running regulary to controll init
-@endlist
-
-@title controlling Controlling
-
-If *finit* is invoked by any other user than _root_, it failes.
-
-@list
-*finit* 0
-halts the system
-
-*finit* 6
-reboots the system
-@endlist
-
-@title description Description
-
-*fiss* must be run as Unix process no 1 if invoked without arguments and handles the boot process in user-land.
-
-This happens in three stages:
-
-
-@title stage Stage 1
-
-*fiss* runs _/etc/fiss/start_ and waits for it to terminate. The system's one time tasks are done here. _/etc/fiss/start_ has full control of _/dev/console_ to be able to start an emergency shell if the one time initialization tasks fail. If _/etc/fiss/start_ crashes, *finit* will skip stage 2 and enter stage 3.
-
-
-@title stage Stage 2
-
-*fiss* starts all services in _/etc/fiss/service.d_ which should not return until system shutdown; if it crashes, it will be restarted.
-
-
-@title stage Stage 3
-
-If *fiss* is told to shutdown the system, it terminates stage 2 if it is running, and runs _/etc/fiss/stop_. The systems tasks to shutdown and possibly halt or reboot the system are done here. If stage 3 returns, *finit* checks if the file
-
-
-@title signals Signals
-
-*finit* only accepts signals in stage 2.
-
-If *finit* receives a CONT signal and the file *finit*
-is told to shutdown the system.
-
-if *finit* receives an INT signal, *finit* restarts the system.
-
-
-@title see See Also
-
-fiss-init(8), runsvdir(8), runsvchdir(8), sv(8), runsv(8), chpst(8), utmpset(8), svlogd(8)
-
-
-@title author Author
-
-Friedel Schön <[email protected]>
diff --git a/src/finit/finit.c b/src/finit/finit.c
@@ -1,137 +1,88 @@
-
-#include "../common/util.h"
-#include "config.h"
-#include "message.h"
-#include "service.h"
-#include "stage.h"
-
-#include <errno.h>
-#include <fcntl.h>
+/* See LICENSE file for copyright and license details. */
+#include <common.h>
+#include <fmt.h>
#include <signal.h>
#include <stdio.h>
-#include <string.h>
-#include <sys/reboot.h>
+#include <stdlib.h>
+#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#define TIMEO 30
-const char* current_prog(void) {
- return "finit";
-}
+static void sighalt(void);
+static void sigreap(void);
+static void sigreboot(void);
-static bool do_reboot;
-
-static int handle_initctl(int argc, const char** argv) {
+static const struct {
int sig;
+ void (*handler)(void);
+} sigmap[] = {
+ { SIGTERM, sighalt },
+ { SIGINT, sigreboot },
+ { SIGCHLD, sigreap },
+ { SIGALRM, sigreap },
+};
- if (argc != 2 || argv[1][1] != '\0' || (argv[1][0] != '0' && argv[1][0] != '6')) {
- print_usage_exit(PROG_FINIT, 1);
- }
- if (getuid() != 0) {
- fprintf(stderr, "error: can only be run as root...\n");
- return 1;
- }
- sig = argv[1][0] == '0' ? SIGTERM : SIGINT;
- if (kill(1, sig) == -1) {
- print_errno("error: unable to kill init: %s\n");
- return 1;
- }
- return 0;
-}
+static sigset_t set;
+static pid_t run_pid;
-static void signal_interrupt(int signum) {
- daemon_running = false;
- do_reboot = signum == SIGINT;
-}
+static char start_command[] = "/etc/fiss/run";
+static char stop_command[] = "/etc/fiss/halt";
+int main(void) {
+ int signal;
-int main(int argc, const char** argv) {
- sigset_t ss;
- pid_t pid;
+ if (getpid() != 1)
+ return 1;
- if (getpid() != 1) {
- return handle_initctl(argc, argv);
+ chdir("/");
+ sigfillset(&set);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ switch (run_pid = fork()) {
+ case 0:
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ setsid();
+ execl(start_command, start_command, NULL);
+ perror("execvp");
+ _exit(1);
+ case -1:
+ perror("fork");
}
- setsid();
-
- sigblock_all(false);
-
- reclaim_console();
-
- // disable ctrl-alt-delete
- reboot(0);
-
- printf("booting...\n");
- daemon_running = true;
-
- // stage 1
- handle_stage(0);
-
-
- // stage 2
- if (daemon_running) { // stage1 succeed
- struct sigaction sigact = { 0 };
-
- sigblock_all(true);
-
- sigact.sa_handler = signal_interrupt;
- sigaction(SIGTERM, &sigact, NULL);
- sigaction(SIGINT, &sigact, NULL);
-
- service_supervise(SV_SERVICE_DIR, SV_BOOT_SERVICE, false);
- sigblock_all(false);
+ while (1) {
+ alarm(TIMEO);
+ sigwait(&set, &signal);
+ for (int i = 0; i < (int) LEN(sigmap); i++) {
+ if (sigmap[i].sig == signal) {
+ sigmap[i].handler();
+ break;
+ }
+ }
}
- // stage 3
- handle_stage(2);
-
-#ifdef RB_AUTOBOOT
- /* fallthrough stage 3 */
- printf("sending KILL signal to all processes...\n");
- kill(-1, SIGKILL);
-
- if ((pid = fork()) <= 0) {
- if (do_reboot) {
- printf("system reboot\n");
- sync();
- reboot(RB_AUTOBOOT);
- } else {
-# if defined(RB_POWER_OFF)
- printf("system power off\n");
- sync();
- reboot(RB_POWER_OFF);
- sleep(2);
-# elif defined(RB_HALT_SYSTEM)
- printf("system halt\n");
- sync();
- reboot(RB_HALT_SYSTEM);
-# elif define(RB_HALT)
- printf("system halt\n");
- sync();
- reboot(RB_HALT);
-# else
- printf("system reboot\n");
- sync();
- reboot(RB_AUTOBOOT);
-# endif
- }
- if (pid == 0)
- _exit(0);
- } else {
- sigemptyset(&ss);
- sigaddset(&ss, SIGCHLD);
- sigprocmask(SIG_UNBLOCK, &ss, NULL);
+ /* not reachable */
+ return 0;
+}
- while (waitpid(pid, NULL, 0) != -1) {}
- }
-#endif
+static void
+sighalt(void) {
+ execl(stop_command, stop_command, "halt", NULL);
+}
- sigfillset(&ss);
- for (;;)
- sigsuspend(&ss);
+static void
+sigreboot(void) {
+ execl(stop_command, stop_command, "reboot", NULL);
+}
- /* not reached */
- printf("exit.\n");
- return 0;
+static void
+sigreap(void) {
+ pid_t pid;
+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
+ if (pid == run_pid) {
+ fprint(1, "run stopped, halting\n");
+ sighalt();
+ }
+ alarm(TIMEO);
}
diff --git a/src/finit/fsvs.c b/src/finit/fsvs.c
@@ -1,69 +0,0 @@
-
-#include "../common/util.h"
-#include "config.h"
-#include "message.h"
-#include "service.h"
-
-#include <getopt.h>
-#include <stdio.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-
-const char* current_prog(void) {
- return "fsvs";
-}
-
-static const struct option long_options[] = {
- { "version", no_argument, 0, 'V' },
- { "once", no_argument, 0, 'o' },
- { 0 }
-};
-
-static void signal_interrupt(int signum) {
- (void) signum;
-
- daemon_running = false;
-}
-
-int main(int argc, char** argv) {
- int c;
- bool once = false;
- while ((c = getopt_long(argc, argv, ":Vo", long_options, NULL)) > 0) {
- switch (c) {
- case 'V':
- print_version_exit();
- break;
- case 'o':
- once = true;
- break;
- default:
- case '?':
- if (optopt)
- fprintf(stderr, "error: invalid option -%c\n", optopt);
- else
- fprintf(stderr, "error: invalid option %s\n", argv[optind - 1]);
- print_usage_exit(PROG_FSVS, 1);
- }
- }
-
- argv += optind;
- argc -= optind;
- if (argc == 0) {
- fprintf(stderr, "error: missing <service-dir>\n");
- print_usage_exit(PROG_FSVS, 1);
- } else if (argc == 1) {
- fprintf(stderr, "error: missing <runlevel>\n");
- print_usage_exit(PROG_FSVS, 1);
- } else if (argc > 2) {
- fprintf(stderr, "error: too many arguments\n");
- print_usage_exit(PROG_FSVS, 1);
- }
-
- struct sigaction sa = { 0 };
- sa.sa_handler = signal_interrupt;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
-
- return service_supervise(argv[0], argv[1], once);
-}
diff --git a/src/finit/handle_exit.c b/src/finit/handle_exit.c
@@ -1,104 +0,0 @@
-#include "service.h"
-
-#include <errno.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-
-static void do_finish(struct service* s) {
- struct stat st;
-
- if (fstatat(s->dir, "finish", &st, 0) != -1 && st.st_mode & S_IXUSR) {
- if ((s->pid = fork_dup_cd_exec(s->dir, "./finish", null_fd, null_fd, null_fd)) == -1) {
- print_errno("error: cannot execute ./finish: %s\n");
- service_update_state(s, STATE_INACTIVE);
- } else {
- service_update_state(s, STATE_FINISHING);
- }
- } else if (s->fail_count == SV_FAIL_MAX) {
- service_update_state(s, STATE_ERROR);
- printf("%s died\n", s->name);
- } else {
- service_update_state(s, s->restart == S_ONCE ? STATE_DONE : STATE_INACTIVE);
- }
-}
-
-
-void service_handle_exit(struct service* s, bool signaled, int return_code) {
- struct stat st;
-
- s->pid = 0;
- s->stop_timeout = 0;
-
- if (s->restart == S_ONCE)
- s->restart = S_DOWN;
-
- switch (s->state) {
- case STATE_SETUP:
- service_run(s);
- break;
- case STATE_ACTIVE_FOREGROUND:
- if (signaled) {
- s->last_exit = EXIT_SIGNALED;
- s->return_code = return_code;
- s->fail_count++;
-
- printf("%s killed thought signal %d\n", s->name, s->return_code);
- } else {
- s->last_exit = EXIT_NORMAL;
- s->return_code = return_code;
- if (s->return_code > 0)
- s->fail_count++;
- else
- s->fail_count = 0;
-
- printf("%s exited with code %d\n", s->name, s->return_code);
- }
-
- do_finish(s);
-
- break;
- case STATE_ACTIVE_DUMMY:
- case STATE_ACTIVE_BACKGROUND:
- case STATE_STOPPING:
- do_finish(s);
- break;
-
- case STATE_FINISHING:
- if (s->fail_count == SV_FAIL_MAX) {
- service_update_state(s, STATE_ERROR);
- printf("%s died\n", s->name);
- } else {
- service_update_state(s, s->restart == S_ONCE ? STATE_DONE : STATE_INACTIVE);
- }
- break;
- case STATE_STARTING:
- if (!signaled && return_code == 0) {
- if (fstatat(s->dir, "stop", &st, 0) != -1 && st.st_mode & S_IXUSR) {
- service_update_state(s, STATE_ACTIVE_BACKGROUND);
- } else {
- do_finish(s);
- }
- } else if (!signaled) {
- s->last_exit = EXIT_NORMAL;
- s->return_code = return_code;
-
- do_finish(s);
- } else { // signaled
- s->last_exit = EXIT_SIGNALED;
- s->return_code = return_code;
-
- do_finish(s);
- }
- break;
-
- case STATE_ERROR:
- case STATE_INACTIVE:
- case STATE_DONE:
- printf("unexpected error: %s died but it's inactive\n", s->name);
- }
-}
diff --git a/src/finit/init.lnk b/src/finit/init.lnk
@@ -1 +0,0 @@
-finit
-\ No newline at end of file
diff --git a/src/finit/message.c b/src/finit/message.c
@@ -1,52 +0,0 @@
-#include "message.h"
-
-#include "config.h"
-
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-static const char* prog_usage[] = {
- [PROG_FINIT] = "init <0|6>",
- [PROG_FSVC] = "fsvc <command> [-v --verbose] [-V --version] [-r --runlevel <level>] [-s --service-dir <path>]\n"
- " fsvc start [-p --pin] <service>\n"
- " fsvc stop [-p --pin] <service>\n"
- " fsvc enable [-o --once] <service>\n"
- " fsvc disable [-o --once] <service>\n"
- " fsvc kill <service> <signal|signum>\n"
- " fsvc status [-c --check] <service>\n"
- " fsvc pause <service>\n"
- " fsvc resume <service>\n"
- " fsvc switch [-f --reset] <runlevel>",
- [PROG_FSVS] = "fsvs [-V --version] [-v --verbose] [-f --force] <service-dir> <runlevel>",
- [PROG_HALT] = "halt [-n] [-f] [-d] [-w] [-B]",
- [PROG_POWEROFF] = "poweroff [-n] [-f] [-d] [-w] [-B]",
- [PROG_REBOOT] = "reboot [-n] [-f] [-d] [-w] [-B]",
- [PROG_SEEDRNG] = "seedrng",
- [PROG_SIGREMAP] = "sigremap [-s --single] [-v --verbose] [-V --version] <old-signal=new-signal...> <command> [args...]",
- [PROG_VLOGGER] = "vlogger [-isS] [-f file] [-p pri] [-t tag] [message ...]",
- [PROG_ZZZ] = "zzz [-n --noop] [-S --freeze] [-z --suspend] [-Z --hibernate] [-R --reboot] [-H --hybrid]"
-};
-
-static const char* prog_manual[] = {
- [PROG_FINIT] = "finit 8",
- [PROG_FSVC] = "fsvc 8",
- [PROG_FSVS] = "fsvs 8",
- [PROG_HALT] = "halt 8",
- [PROG_POWEROFF] = "poweroff 8",
- [PROG_REBOOT] = "reboot 8",
- [PROG_SEEDRNG] = "seedrng 8",
- [PROG_SIGREMAP] = "sigremap 8",
- [PROG_VLOGGER] = "vlogger 1",
- [PROG_ZZZ] = "zzz 8"
-};
-
-void print_usage_exit(enum prog prog, int status) {
- fprintf(status ? stderr : stdout, "Usage: %s\n\nCheck manual '%s' for more information.\n", prog_usage[prog], prog_manual[prog]);
- exit(status);
-}
-
-void print_version_exit(void) {
- printf(SV_VERSION);
- exit(0);
-}
diff --git a/src/finit/register.c b/src/finit/register.c
@@ -1,96 +0,0 @@
-#include "../common/util.h"
-#include "config.h"
-#include "service.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-
-static int init_supervise(struct service* s) {
- int fd;
- struct stat st;
-
- if (fstatat(s->dir, "supervise", &st, 0) == -1 && mkdirat(s->dir, "supervise", 0755) == -1) {
- return -1;
- }
-
- if (fstatat(s->dir, "supervise/ok", &st, 0) == -1 && mkfifoat(s->dir, "supervise/ok", 0666) == -1) {
- print_errno("cannot create fifo at supervise/ok: %s\n");
- return -1;
- }
-
- if (fstatat(s->dir, "supervise/control", &st, 0) == -1 && mkfifoat(s->dir, "supervise/control", 0644) == -1) {
- print_errno("cannot create fifo at supervise/control: %s\n");
- return -1;
- }
-
- if (openat(s->dir, "supervise/ok", O_RDONLY | O_NONBLOCK) == -1) {
- print_errno("cannot open supervise/ok: %s\n");
- return -1;
- }
-
- if ((s->control = openat(s->dir, "supervise/control", O_RDONLY | O_NONBLOCK)) == -1) {
- print_errno("cannot open supervise/ok: %s\n");
- return -1;
- }
-
- if ((fd = openat(s->dir, "supervise/lock", O_CREAT | O_WRONLY, 0644)) == -1) {
- print_errno("cannot create supervise/lock: %s\n");
- return -1;
- }
- close(fd);
-
- return 0;
-}
-
-struct service* service_register(int dir, const char* name, bool is_log_service) {
- struct service* s;
- struct stat st;
-
- if ((s = service_get(name)) == NULL) {
- s = &services[services_size++];
- s->state = STATE_INACTIVE;
- s->restart = S_DOWN;
- s->last_exit = EXIT_NONE;
- s->return_code = 0;
- s->fail_count = 0;
- s->log_service = NULL;
- s->paused = false;
- s->log_pipe.read = 0;
- s->log_pipe.write = 0;
- s->is_log_service = is_log_service;
- s->stop_timeout = 0;
-
- if ((s->dir = openat(dir, name, O_DIRECTORY)) == -1) {
- print_errno("error: cannot open '%s': %s\n", name);
- services_size--;
- return NULL;
- }
-
- if (init_supervise(s) == -1) {
- services_size--;
- return NULL;
- }
-
- strncpy(s->name, name, sizeof(s->name));
-
- service_update_state(s, -1);
- }
-
- if (s->is_log_service) {
- if (s->log_pipe.read == 0 || s->log_pipe.write == 0)
- pipe((int*) &s->log_pipe);
-
- } else if (!s->log_service && fstatat(s->dir, "log", &st, 0) != -1 && S_ISDIR(st.st_mode)) {
- s->log_service = service_register(s->dir, "log", true);
- }
-
- service_write(s);
-
- return s;
-}
diff --git a/src/finit/service.c b/src/finit/service.c
@@ -1,86 +0,0 @@
-#include "service.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-
-struct service services[SV_SERVICE_MAX];
-int services_size = 0;
-char runlevel[SV_NAME_MAX];
-int service_dir;
-const char* service_dir_path;
-int null_fd;
-bool daemon_running;
-
-struct service* service_get(const char* name) {
- for (int i = 0; i < services_size; i++) {
- if (streq(services[i].name, name))
- return &services[i];
- }
- return NULL;
-}
-
-int service_refresh_directory(void) {
- DIR* dp;
- struct dirent* ep;
- struct stat st;
- struct service* s;
-
- if ((dp = opendir(service_dir_path)) == NULL) {
- print_errno("error: cannot open service directory: %s\n");
- return -1;
- }
-
- for (int i = 0; i < services_size; i++) {
- s = &services[i];
- if (fstat(s->dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
- service_stop(s);
- close(s->dir);
- close(s->control);
- }
- }
-
- while ((ep = readdir(dp)) != NULL) {
- if (ep->d_name[0] == '.')
- continue;
-
- if (fstatat(service_dir, ep->d_name, &st, 0) == -1 || !S_ISDIR(st.st_mode))
- continue;
-
- service_register(service_dir, ep->d_name, false);
- }
-
- closedir(dp);
-
- for (int i = 0; i < services_size; i++) {
- services[i].children_size = 0;
- services[i].parents_size = 0;
- }
-
- for (int i = 0; i < services_size; i++)
- service_update_dependency(&services[i]);
-
- return 0;
-}
-
-
-bool service_need_restart(struct service* s) {
- if (!daemon_running)
- return false;
-
- if (s->restart == S_RESTART)
- return true;
-
- for (int i = 0; i < s->parents_size; i++) {
- if (service_need_restart(s->parents[i]))
- return true;
- }
-
- return false;
-}
diff --git a/src/finit/service.h b/src/finit/service.h
@@ -1,114 +0,0 @@
-#pragma once
-
-#include "../common/util.h"
-#include "config.h"
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <time.h>
-
-
-enum service_command {
- X_UP = 'u', // starts the services, pin as started
- X_DOWN = 'd', // stops the service, pin as stopped
- X_ONCE = 'o', // starts the service, pin as once
- X_TERM = 't', // same as down
- X_KILL = 'k', // sends kill, pin as stopped
- X_PAUSE = 'p', // pauses the service
- X_CONT = 'c', // resumes the service
- X_RESET = 'r', // resets the service
- X_ALARM = 'a', // sends alarm
- X_HUP = 'h', // sends hup
- X_INT = 'i', // sends interrupt
- X_QUIT = 'q', // sends quit
- X_USR1 = '1', // sends usr1
- X_USR2 = '2', // sends usr2
- X_EXIT = 'x', // does nothing
-};
-
-enum service_state {
- STATE_INACTIVE, // not started
- STATE_SETUP, // ./setup running
- STATE_STARTING, // ./start running
- STATE_ACTIVE_FOREGROUND, // ./run running
- STATE_ACTIVE_BACKGROUND, // ./start finished, ./stop not called yet
- STATE_ACTIVE_DUMMY, // dependencies started
- STATE_STOPPING, // ./stop running
- STATE_FINISHING, // ./finish running
- STATE_DONE, // ./stop finished
- STATE_ERROR, // something went wrong
-};
-
-enum service_exit {
- EXIT_NONE, // never exited
- EXIT_NORMAL, // exited
- EXIT_SIGNALED, // crashed
-};
-
-enum service_restart {
- S_DOWN, // service should not be started
- S_ONCE, // service should
- S_RESTART, // service should be started
-};
-
-struct service_serial {
- uint8_t status_change[8];
- uint8_t state;
- uint8_t return_code;
- uint8_t fail_count;
- uint8_t flags;
- uint8_t pid[4];
- uint8_t paused;
- uint8_t restart;
- uint8_t force_down;
- uint8_t state_runit;
-};
-
-struct service {
- char name[SV_NAME_MAX]; // name of service
- enum service_state state; // current state
- pid_t pid; // pid of run
- int dir; // dirfd
- int control; // fd to supervise/control
- time_t state_change; // last status change
- enum service_restart restart; // should restart on exit
- enum service_exit last_exit; // stopped signaled or exited
- int return_code; // return code or signal
- uint8_t fail_count; // current fail cound
- bool is_log_service; // is a log service
- bool paused; // is paused
- time_t stop_timeout; // stop start-time
- pipe_t log_pipe; // pipe for logging
- struct service* log_service; // has a log_server otherwise NULL
- int parents_size; // count of service depending on
- struct service* parents[SV_DEPENDENCY_MAX]; // service depending on
- int children_size; // count of dependencies
- struct service* children[SV_DEPENDENCY_MAX]; // dependencies
-};
-
-extern struct service services[];
-extern int services_size;
-extern int null_fd;
-extern bool daemon_running;
-extern const char* service_dir_path;
-extern int service_dir;
-
-
-void service_encode(struct service* s, struct service_serial* buffer);
-struct service* service_get(const char* name);
-void service_handle_command(struct service* s, char command);
-void service_handle_exit(struct service* s, bool signaled, int return_code);
-void service_kill(struct service* s, int signal);
-bool service_need_restart(struct service* s);
-int service_refresh_directory(void);
-struct service* service_register(int dir, const char* name, bool is_log_service);
-void service_run(struct service* s);
-int service_send_command(char command, char extra, const char* service, struct service* response, int response_max);
-void service_start(struct service* s);
-const char* service_status_name(struct service* s);
-void service_stop(struct service* s);
-int service_supervise(const char* service_dir_, const char* service, bool once);
-void service_update_dependency(struct service* s);
-bool service_is_dependency(struct service* s);
-void service_update_state(struct service* s, int state);
-void service_write(struct service* s);
diff --git a/src/finit/stage.c b/src/finit/stage.c
@@ -1,82 +0,0 @@
-#include "stage.h"
-
-#include "../common/util.h"
-#include "config.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-
-static char* stage_exec[][4] = {
- [0] = { SV_START_EXEC, NULL },
- [2] = { SV_STOP_EXEC, NULL },
-};
-
-
-bool handle_stage(int stage) {
- int pid, ttyfd, exitstat, sig = 0;
- sigset_t ss;
- bool cont = true;
-
- while ((pid = fork()) == -1) {
- print_errno("error: unable to fork for stage1: %s\n");
- sleep(5);
- }
- if (pid == 0) {
- /* child */
-
- if (stage == 0) {
- /* stage 1 gets full control of console */
- if ((ttyfd = open("/dev/console", O_RDWR)) == -1) {
- print_errno("error: unable to open /dev/console: %s\n");
- } else {
- ioctl(ttyfd, TIOCSCTTY, NULL); // make the controlling process
- dup2(ttyfd, 0);
- if (ttyfd > 2) close(ttyfd);
- }
- }
-
- sigblock_all(true);
-
- printf("enter stage %d\n", stage);
- execv(stage_exec[stage][0], stage_exec[stage]);
- print_errno("error: unable to exec stage %d: %s\n", stage);
- _exit(1);
- }
-
- sigemptyset(&ss);
- sigaddset(&ss, SIGCHLD);
- sigaddset(&ss, SIGUSR1);
- sigaddset(&ss, SIGCONT);
-
- sigwait(&ss, &sig);
-
- if (stage == 1 && sig != SIGCHLD)
- kill(pid, SIGTERM);
-
- if (waitpid(pid, &exitstat, 0) == -1) {
- print_errno("warn: waitpid failed: %s");
- sleep(5);
- }
-
- reclaim_console();
-
- if (stage == 0) {
- if (!WIFEXITED(exitstat) || WEXITSTATUS(exitstat) != 0) {
- if (WIFSIGNALED(exitstat)) {
- /* this is stage 1 */
- fprintf(stderr, "stage 1 failed: skip stage 2\n");
- cont = false;
- }
- }
- printf("leave stage 1\n");
- }
-
- return cont;
-}
diff --git a/src/finit/stage.h b/src/finit/stage.h
@@ -1,6 +0,0 @@
-#pragma once
-
-#include <stdbool.h>
-
-
-bool handle_stage(int stage);
diff --git a/src/finit/start.c b/src/finit/start.c
@@ -1,112 +0,0 @@
-#include "service.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-
-static void set_pipes(struct service* s) {
- if (s->is_log_service) {
- close(s->log_pipe.write);
- dup2(s->log_pipe.read, STDIN_FILENO);
- close(s->log_pipe.read);
- dup2(null_fd, STDOUT_FILENO);
- dup2(null_fd, STDERR_FILENO);
- } else if (s->log_service) { // aka has_log_service
- close(s->log_service->log_pipe.read);
- dup2(s->log_service->log_pipe.write, STDOUT_FILENO);
- dup2(s->log_service->log_pipe.write, STDERR_FILENO);
- close(s->log_service->log_pipe.write);
- dup2(null_fd, STDIN_FILENO);
- } else if (stat_mode("log") & S_IWRITE) { // is not
- int log_fd;
- if ((log_fd = open("log", O_WRONLY | O_TRUNC)) == -1)
- log_fd = null_fd;
-
- dup2(null_fd, STDIN_FILENO);
- dup2(log_fd, STDOUT_FILENO);
- dup2(log_fd, STDERR_FILENO);
- } else if (S_ISREG(stat_mode("nolog"))) {
- dup2(null_fd, STDIN_FILENO);
- dup2(null_fd, STDOUT_FILENO);
- dup2(null_fd, STDERR_FILENO);
- } else {
- char service_log[PATH_MAX];
- int log_fd;
-
- snprintf(service_log, PATH_MAX, "%s/%s.log", SV_LOG_DIR, s->name);
-
- if ((log_fd = open(service_log, O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1)
- log_fd = null_fd;
-
- dup2(null_fd, STDIN_FILENO);
- dup2(log_fd, STDOUT_FILENO);
- dup2(log_fd, STDERR_FILENO);
- }
-}
-
-void service_run(struct service* s) {
- struct stat st;
-
- if (fstatat(s->dir, "run", &st, 0) != -1 && st.st_mode & S_IXUSR) {
- service_update_state(s, STATE_ACTIVE_FOREGROUND);
- } else if (fstatat(s->dir, "start", &st, 0) != -1 && st.st_mode & S_IXUSR) {
- service_update_state(s, STATE_STARTING);
- } else if (fstatat(s->dir, "depends", &st, 0) != -1 && st.st_mode & S_IREAD) {
- service_update_state(s, STATE_ACTIVE_DUMMY);
- } else {
- // fprintf(stderr, "warn: %s: `run`, `start` or `depends` not found\n", s->name);
- service_update_state(s, STATE_INACTIVE);
- }
-
- if (s->state != STATE_ACTIVE_DUMMY) {
- if ((s->pid = fork()) == -1) {
- print_errno("error: cannot fork process: %s\n");
- exit(1);
- } else if (s->pid == 0) { // child
- if (setsid() == -1)
- print_errno("error: cannot setsid: %s\n");
-
- fchdir(s->dir);
- set_pipes(s);
-
- if (s->state == STATE_STARTING) {
- execl("./start", "./start", NULL);
- } else {
- execl("./run", "./run", NULL);
- }
- print_errno("error: cannot execute service: %s\n");
- _exit(1);
- }
- }
-}
-
-void service_start(struct service* s) {
- struct stat st;
-
- if (!daemon_running || s->state != STATE_INACTIVE)
- return;
-
- printf("starting %s\n", s->name);
- for (int i = 0; i < s->children_size; i++) {
- service_start(s->children[i]);
- }
-
- if (fstatat(s->dir, "setup", &st, 0) != -1 && st.st_mode & S_IXUSR) {
- if ((s->pid = fork_dup_cd_exec(s->dir, "./setup", null_fd, null_fd, null_fd)) == -1) {
- print_errno("error: cannot execute ./setup: %s\n");
- service_update_state(s, STATE_INACTIVE);
- } else {
- service_update_state(s, STATE_SETUP);
- }
- } else {
- service_run(s);
- }
- printf("started %s \n", s->name);
-}
diff --git a/src/finit/status.c b/src/finit/status.c
@@ -1,94 +0,0 @@
-#include "../common/util.h"
-#include "service.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-
-void service_update_state(struct service* s, int state) {
- if (state != -1)
- s->state = state;
-
- s->state_change = time(NULL);
-
- service_write(s);
-}
-
-void service_write(struct service* s) {
- int fd;
- const char* stat_human;
- struct service_serial stat_runit;
-
- if ((fd = openat(s->dir, "supervise/status.new", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
- print_errno("cannot open supervise/status: %s\n");
- return;
- }
-
- service_encode(s, &stat_runit);
-
- if (write(fd, &stat_runit, sizeof(stat_runit)) == -1) {
- print_errno("cannot write to supervise/status: %s\n");
- return;
- }
-
- close(fd);
-
- if ((fd = openat(s->dir, "supervise/stat.new", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
- print_errno("cannot create supervise/stat: %s\n");
- return;
- }
-
- stat_human = service_status_name(s);
- if (write(fd, stat_human, strlen(stat_human)) == -1) {
- print_errno("cannot write to supervise/stat: %s\n");
- return;
- }
-
- close(fd);
-
- if ((fd = openat(s->dir, "supervise/pid.new", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
- print_errno("cannot create supervise/stat: %s\n");
- return;
- }
-
- dprintf(fd, "%d", s->pid);
-
- close(fd);
-
- renameat(s->dir, "supervise/status.new", s->dir, "supervise/status");
- renameat(s->dir, "supervise/stat.new", s->dir, "supervise/stat");
- renameat(s->dir, "supervise/pid.new", s->dir, "supervise/pid");
-}
-
-const char* service_status_name(struct service* s) {
- switch (s->state) {
- case STATE_SETUP:
- return "setup";
- case STATE_STARTING:
- return "starting";
- case STATE_ACTIVE_FOREGROUND:
- return "run";
- case STATE_ACTIVE_BACKGROUND:
- return "run-background";
- case STATE_ACTIVE_DUMMY:
- return "run-dummy";
- case STATE_FINISHING:
- return "finishing";
- case STATE_STOPPING:
- return "stopping";
- case STATE_INACTIVE:
- return "down";
- case STATE_DONE:
- return "done";
- case STATE_ERROR:
- return "dead (error)";
- default:
- return NULL;
- }
-}
diff --git a/src/finit/stop.c b/src/finit/stop.c
@@ -1,48 +0,0 @@
-#include "../common/util.h"
-#include "service.h"
-
-#include <errno.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-
-void service_stop(struct service* s) {
- switch (s->state) {
- case STATE_ACTIVE_DUMMY:
- service_handle_exit(s, false, 0);
- break;
- case STATE_ACTIVE_BACKGROUND:
- if ((s->pid = fork_dup_cd_exec(s->dir, "./stop", null_fd, null_fd, null_fd)) == -1) {
- print_errno("error: cannot execute ./stop: %s\n");
- service_update_state(s, STATE_INACTIVE);
- } else {
- service_update_state(s, STATE_STOPPING);
- }
- break;
- case STATE_ACTIVE_FOREGROUND:
- case STATE_SETUP:
- case STATE_STARTING:
- case STATE_STOPPING:
- case STATE_FINISHING:
- s->stop_timeout = time(NULL);
- kill(s->pid, SIGTERM);
- service_update_state(s, -1);
- break;
- case STATE_DONE:
- s->state = STATE_INACTIVE;
- case STATE_INACTIVE:
- case STATE_ERROR:
- break;
- }
-}
-
-void service_kill(struct service* s, int signal) {
- if (!s->pid)
- return;
-
- if (s->state == STATE_ACTIVE_FOREGROUND)
- kill(s->pid, signal);
-}
diff --git a/src/finit/supervise.c b/src/finit/supervise.c
@@ -1,170 +0,0 @@
-#include "../common/util.h"
-#include "config.h"
-#include "service.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <setjmp.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-
-static void signal_child(int unused) {
- (void) unused;
-
- int status;
- pid_t died_pid;
- struct service* s = NULL;
-
- if ((died_pid = wait(&status)) == -1) {
- print_errno("error: cannot wait for process: %s\n");
- return;
- }
-
- if (!WIFEXITED(status) && !WIFSIGNALED(status))
- return;
-
- for (int i = 0; i < services_size; i++) {
- if (services[i].pid == died_pid) {
- s = &services[i];
- break;
- }
- }
- if (s == NULL)
- return;
-
- service_handle_exit(s, WIFSIGNALED(status), WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status));
-}
-
-static void update_services(void) {
- struct service* s;
-
- for (int i = 0; i < services_size; i++) {
- s = &services[i];
- if (s->state == STATE_INACTIVE || s->state == STATE_ERROR)
- s->stop_timeout = 0;
-
- if (s->state == STATE_ERROR)
- continue;
-
- if (s->stop_timeout != 0) {
- if (time(NULL) - s->stop_timeout >= SV_STOP_TIMEOUT) {
- printf(":: service '%s' doesn't terminate, killing...\n", s->name);
- service_kill(s, SIGKILL);
- s->stop_timeout = 0;
- }
- } else if (s->state == STATE_INACTIVE && service_need_restart(s)) {
- service_start(s);
- }
- }
-}
-
-static void control_sockets(void) {
- struct service* s;
- char cmd;
-
- for (int i = 0; i < services_size; i++) {
- s = &services[i];
- while (read(s->control, &cmd, 1) == 1) {
- printf("handling '%c' from %s\n", cmd, s->name);
- service_handle_command(s, cmd);
- }
- }
-}
-
-void stop_dummies(void) {
- for (int i = 0; i < services_size; i++) {
- if (services[i].state != STATE_ACTIVE_DUMMY || services[i].restart == S_RESTART)
- continue;
-
- for (int j = 0; j < services[i].children_size; j++) {
- struct service* dep = services[i].children[j];
- if (dep->state != STATE_INACTIVE && dep->state != STATE_ERROR)
- goto dont_stop;
- }
-
- service_stop(&services[i]);
-
- dont_stop:;
- }
-}
-
-int service_supervise(const char* service_dir_, const char* service, bool once) {
- struct sigaction sigact = { 0 };
- struct service* s;
-
- daemon_running = true;
-
- sigact.sa_handler = signal_child;
- sigaction(SIGCHLD, &sigact, NULL);
- sigact.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sigact, NULL);
-
- service_dir_path = service_dir_;
- if ((service_dir = open(service_dir_, O_DIRECTORY)) == -1) {
- print_errno("error: cannot open directory %s: %s\n", service_dir_);
- return 1;
- }
-
- if ((null_fd = open("/dev/null", O_RDWR)) == -1) {
- print_errno("error: cannot open /dev/null: %s\n");
- null_fd = 1;
- }
-
- printf(":: starting services\n");
-
- service_refresh_directory();
-
- if ((s = service_get(service)) == NULL) {
- fprintf(stderr, "error: cannot start '%s': not found\n", service);
- goto cleanup;
- }
-
- s->restart = once ? S_ONCE : S_RESTART;
- service_start(s);
-
-
- bool cont;
- // accept connections and handle requests
- do {
- if (!daemon_running) {
- for (int i = 0; i < services_size; i++) {
- s = &services[i];
- service_stop(s);
- }
- }
-
- service_refresh_directory();
- stop_dummies();
- control_sockets();
- update_services();
-
- sleep(SV_CHECK_INTERVAL);
-
- cont = false;
- for (int i = 0; i < services_size; i++) {
- if (services[i].state != STATE_INACTIVE && services[i].state != STATE_ERROR)
- cont = true;
- }
- } while (cont);
-
- printf(":: terminating\n");
-
- printf(":: all services stopped\n");
-
-cleanup:
-
- close(service_dir);
- close(null_fd);
-
- signal(SIGPIPE, SIG_DFL);
- signal(SIGCHLD, SIG_DFL);
- return 0;
-}
diff --git a/src/fsvc/Makefile b/src/fsvc/Makefile
@@ -1,11 +1,12 @@
TOPDIR=../..
-include $(TOPDIR)/config.mk
-OBJS += ../finit/message.o ../common/util.o signame.o
+OBJS += ../fsvs/message.o signame.o
BINS = fsvc
INTERM = fsvc.8.txt
MANS = fsvc.8
PAGES = fsvc.8.html
-HEADERS = ../common/util.h ../finit/message.h ../finit/service.h signame.h
+HEADERS = ../fsvs/message.h ../fsvs/service.h signame.h
+LIBS = $(LIBDIR)/libutil/libutil.a
include $(TOPDIR)/mk/prog.mk
diff --git a/src/fsvc/fsvc.c b/src/fsvc/fsvc.c
@@ -1,15 +1,15 @@
-#include "../common/util.h"
-#include "../finit/message.h"
-#include "../finit/service.h"
+#include "../fsvs/message.h"
+#include "../fsvs/service.h"
#include "config.h"
#include "signame.h"
+#include "util.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
-#include <limits.h>
#include <stdbool.h>
+#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -170,56 +170,56 @@ int status(int dir) {
switch (s.state) {
case STATE_SETUP:
- printf("setting up");
+ print("setting up");
break;
case STATE_STARTING:
- printf("starting as %d", s.pid);
+ print("starting as %d", s.pid);
break;
case STATE_ACTIVE_FOREGROUND:
- printf("active as %d", s.pid);
+ print("active as %d", s.pid);
break;
case STATE_ACTIVE_BACKGROUND:
case STATE_ACTIVE_DUMMY:
- printf("active");
+ print("active");
break;
case STATE_FINISHING:
- printf("finishing as %d", s.pid);
+ print("finishing as %d", s.pid);
break;
case STATE_STOPPING:
- printf("stopping as %d", s.pid);
+ print("stopping as %d", s.pid);
break;
case STATE_INACTIVE:
- printf("inactive");
+ print("inactive");
break;
case STATE_ERROR:
- printf("dead (error)");
+ print("dead (error)");
break;
}
if (s.paused)
- printf(" & paused");
+ print(" & paused");
- printf(" since %lu%s", timeval, timeunit);
+ print(" since %lu%s", timeval, timeunit);
if (s.once == S_ONCE)
- printf(", started once");
+ print(", started once");
if (s.restart)
- printf(", should restart");
+ print(", should restart");
if (s.is_depends)
- printf(", started as dependency");
+ print(", started as dependency");
if (s.return_code > 0 && s.last_exit == EXIT_NORMAL)
- printf(", exited with %d", s.return_code);
+ print(", exited with %d", s.return_code);
if (s.return_code > 0 && s.last_exit == EXIT_SIGNALED)
- printf(", crashed with SIG%s", sigabbr(s.return_code));
+ print(", crashed with SIG%s", sigabbr(s.return_code));
if (s.fail_count > 0)
- printf(", failed %d times", s.fail_count);
+ print(", failed %d times", s.fail_count);
- printf("\n");
+ print("\n");
return 0;
}
@@ -243,9 +243,9 @@ int main(int argc, char** argv) {
default:
case '?':
if (optopt)
- fprintf(stderr, "error: invalid option -%c\n", optopt);
+ fprint(1, "error: invalid option -%c\n", optopt);
else
- fprintf(stderr, "error: invalid option %s\n", argv[optind - 1]);
+ fprint(1, "error: invalid option %s\n", argv[optind - 1]);
print_usage_exit(PROG_FSVC, 1);
}
}
@@ -253,7 +253,7 @@ int main(int argc, char** argv) {
argc -= optind, argv += optind;
if (argc == 0) {
- fprintf(stderr, "error: command omitted\n");
+ fprint(1, "error: command omitted\n");
print_usage_exit(PROG_FSVC, 1);
}
for (const char** ident = (void*) command_names; ident[0] != NULL; ident++) {
@@ -263,14 +263,14 @@ int main(int argc, char** argv) {
}
}
if (command == NULL) {
- fprintf(stderr, "error: unknown command '%s'\n", argv[0]);
+ fprint(1, "error: unknown command '%s'\n", argv[0]);
print_usage_exit(PROG_FSVC, 1);
}
argc--, argv++;
if (argc == 0) {
- fprintf(stderr, "error: at least one service must be specified\n");
+ fprint(1, "error: at least one service must be specified\n");
print_usage_exit(PROG_FSVC, 1);
}
@@ -285,24 +285,24 @@ int main(int argc, char** argv) {
service = progname(argv[i]);
if ((dir = open(argv[i], O_DIRECTORY)) == -1) {
- fprintf(stderr, "error: %s: cannot open directory: %s\n", argv[i], strerror(errno));
+ fprint(1, "error: %s: cannot open directory: %s\n", argv[i], strerror(errno));
continue;
}
if ((fd = openat(dir, "supervise/ok", O_WRONLY | O_NONBLOCK)) == -1) {
- fprintf(stderr, "error: %s: cannot open supervise/control: %s\n", argv[i], strerror(errno));
+ fprint(1, "error: %s: cannot open supervise/control: %s\n", argv[i], strerror(errno));
continue;
}
close(fd);
if ((mod = get_mtime(dir)) == -1) {
- fprintf(stderr, "error: %s: cannot get modify-time\n", argv[i]);
+ fprint(1, "error: %s: cannot get modify-time\n", argv[i]);
continue;
}
if (command[0] != '\0') {
if (send_command(dir, command) == -1) {
- fprintf(stderr, "error: %s: unable to send command\n", argv[i]);
+ fprint(1, "error: %s: unable to send command\n", argv[i]);
continue;
}
} else {
@@ -315,12 +315,12 @@ int main(int argc, char** argv) {
usleep(500); // sleep half a secound
if (get_mtime(dir) == mod)
- printf("timeout: ");
+ print("timeout: ");
- printf("%s: ", service);
+ print("%s: ", service);
if (status(dir) == -1)
- printf("unable to access supervise/status\n");
+ print("unable to access supervise/status\n");
}
}
}
diff --git a/src/fsvc/signame.c b/src/fsvc/signame.c
@@ -1,21 +1,18 @@
#include "signame.h"
-#include "../common/util.h"
+#include "util.h"
#include <signal.h>
#include <stdlib.h>
#include <string.h>
-
#define SIGNUM_NAME(name) \
{ SIG##name, #name }
-struct signal_name {
+static struct {
int num;
const char* name;
-};
-
-static struct signal_name signal_names[] = {
+} signals[] = {
/* Signals required by POSIX 1003.1-2001 base, listed in
traditional numeric order where possible. */
#ifdef SIGHUP
@@ -169,8 +166,6 @@ static struct signal_name signal_names[] = {
#ifdef SIGTHR
SIGNUM_NAME(THR),
#endif
-
- { 0, NULL },
};
int signame(char const* name) {
@@ -186,18 +181,18 @@ int signame(char const* name) {
}
// search for name
- for (struct signal_name* sigpair = signal_names; sigpair->num != 0; sigpair++)
- if (streq(sigpair->name, name))
- return sigpair->num;
+ for (int i = 0; i < (int) LEN(signals); i++)
+ if (streq(signals[i].name, name))
+ return signals[i].num;
return -1;
}
const char* sigabbr(int signal) {
// search for name
- for (struct signal_name* sigpair = signal_names; sigpair->num != 0; sigpair++)
- if (sigpair->num == signal)
- return sigpair->name;
+ for (int i = 0; i < (int) LEN(signals); i++)
+ if (signals[i].num == signal)
+ return signals[i].name;
return "UNKNOWN";
}
diff --git a/src/fsvs/Makefile b/src/fsvs/Makefile
@@ -0,0 +1,14 @@
+TOPDIR = ../..
+-include $(TOPDIR)/config.mk
+
+BINS += fsvs
+OBJS += message.o supervise.o service.o start.o \
+ stop.o register.o handle_exit.o handle_command.o \
+ encode.o dependency.o status.o
+
+HEADERS += message.h service.h stage.h
+LIBS = $(LIBDIR)/libutil/libutil.a
+MANS += fsvs.8
+PAGES += fsvs.8.html
+
+include $(TOPDIR)/mk/prog.mk
+\ No newline at end of file
diff --git a/src/fsvs/dependency.c b/src/fsvs/dependency.c
@@ -0,0 +1,60 @@
+#include "config.h"
+#include "service.h"
+#include "util.h"
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+
+static bool circular_dependency(struct service* s, struct service* d) {
+ if (s == d)
+ return true;
+
+ for (int i = 0; i < s->parents_size; i++) {
+ if (circular_dependency(s->parents[i], d))
+ return true;
+ }
+
+ return false;
+}
+
+void service_add_dependency(struct service* s, struct service* d) {
+ if (circular_dependency(s, d)) {
+ print_error("warning: detected circular dependency while adding %s to %s\n", d->name, s->name);
+ return;
+ }
+
+ s->children[s->children_size++] = d;
+ d->parents[s->parents_size++] = s;
+}
+
+void service_update_dependency(struct service* s) {
+ struct service* dep;
+ int depends_file;
+ char line[SV_NAME_MAX];
+
+ if (s->log_service) { // aka keep first entry (the log service) if a log service is used
+ service_add_dependency(s, s->log_service);
+ }
+
+ if ((depends_file = openat(s->dir, "depends", O_RDONLY)) == -1)
+ return;
+
+ while (dgetline(depends_file, line, sizeof(line)) > 0) {
+ if (streq(s->name, line)) {
+ fprint(1, "warning: %s depends on itself\n", s->name);
+ continue;
+ }
+
+ if ((dep = service_get(line)) == NULL) {
+ fprint(1, "warning: %s depends on %s: dependency not found\n", s->name, line);
+ continue;
+ }
+ service_add_dependency(s, dep);
+ }
+
+ close(depends_file);
+}
diff --git a/src/finit/encode.c b/src/fsvs/encode.c
diff --git a/src/finit/fsvs.8.txt b/src/fsvs/fsvs.8.txt
diff --git a/src/fsvs/fsvs.c b/src/fsvs/fsvs.c
@@ -0,0 +1,69 @@
+
+#include "config.h"
+#include "message.h"
+#include "service.h"
+#include "util.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+
+const char* current_prog(void) {
+ return "fsvs";
+}
+
+static const struct option long_options[] = {
+ { "version", no_argument, 0, 'V' },
+ { "once", no_argument, 0, 'o' },
+ { 0 }
+};
+
+static void signal_interrupt(int signum) {
+ (void) signum;
+
+ daemon_running = false;
+}
+
+int main(int argc, char** argv) {
+ int c;
+ bool once = false;
+ while ((c = getopt_long(argc, argv, ":Vo", long_options, NULL)) > 0) {
+ switch (c) {
+ case 'V':
+ print_version_exit();
+ break;
+ case 'o':
+ once = true;
+ break;
+ default:
+ case '?':
+ if (optopt)
+ fprint(1, "error: invalid option -%c\n", optopt);
+ else
+ fprint(1, "error: invalid option %s\n", argv[optind - 1]);
+ print_usage_exit(PROG_FSVS, 1);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+ if (argc == 0) {
+ fprint(1, "error: missing <service-dir>\n");
+ print_usage_exit(PROG_FSVS, 1);
+ } else if (argc == 1) {
+ fprint(1, "error: missing <runlevel>\n");
+ print_usage_exit(PROG_FSVS, 1);
+ } else if (argc > 2) {
+ fprint(1, "error: too many arguments\n");
+ print_usage_exit(PROG_FSVS, 1);
+ }
+
+ struct sigaction sa = { 0 };
+ sa.sa_handler = signal_interrupt;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ return service_supervise(argv[0], argv[1], once);
+}
diff --git a/src/finit/handle_command.c b/src/fsvs/handle_command.c
diff --git a/src/fsvs/handle_exit.c b/src/fsvs/handle_exit.c
@@ -0,0 +1,104 @@
+#include "service.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+static void do_finish(struct service* s) {
+ struct stat st;
+
+ if (fstatat(s->dir, "finish", &st, 0) != -1 && st.st_mode & S_IXUSR) {
+ if ((s->pid = fork_dup_cd_exec(s->dir, "./finish", null_fd, null_fd, null_fd)) == -1) {
+ fprint(1, "error: cannot execute ./finish: %r\n");
+ service_update_state(s, STATE_INACTIVE);
+ } else {
+ service_update_state(s, STATE_FINISHING);
+ }
+ } else if (s->fail_count == SV_FAIL_MAX) {
+ service_update_state(s, STATE_ERROR);
+ print("%s died\n", s->name);
+ } else {
+ service_update_state(s, s->restart == S_ONCE ? STATE_DONE : STATE_INACTIVE);
+ }
+}
+
+
+void service_handle_exit(struct service* s, bool signaled, int return_code) {
+ struct stat st;
+
+ s->pid = 0;
+ s->stop_timeout = 0;
+
+ if (s->restart == S_ONCE)
+ s->restart = S_DOWN;
+
+ switch (s->state) {
+ case STATE_SETUP:
+ service_run(s);
+ break;
+ case STATE_ACTIVE_FOREGROUND:
+ if (signaled) {
+ s->last_exit = EXIT_SIGNALED;
+ s->return_code = return_code;
+ s->fail_count++;
+
+ print("%s killed thought signal %d\n", s->name, s->return_code);
+ } else {
+ s->last_exit = EXIT_NORMAL;
+ s->return_code = return_code;
+ if (s->return_code > 0)
+ s->fail_count++;
+ else
+ s->fail_count = 0;
+
+ print("%s exited with code %d\n", s->name, s->return_code);
+ }
+
+ do_finish(s);
+
+ break;
+ case STATE_ACTIVE_DUMMY:
+ case STATE_ACTIVE_BACKGROUND:
+ case STATE_STOPPING:
+ do_finish(s);
+ break;
+
+ case STATE_FINISHING:
+ if (s->fail_count == SV_FAIL_MAX) {
+ service_update_state(s, STATE_ERROR);
+ print("%s died\n", s->name);
+ } else {
+ service_update_state(s, s->restart == S_ONCE ? STATE_DONE : STATE_INACTIVE);
+ }
+ break;
+ case STATE_STARTING:
+ if (!signaled && return_code == 0) {
+ if (fstatat(s->dir, "stop", &st, 0) != -1 && st.st_mode & S_IXUSR) {
+ service_update_state(s, STATE_ACTIVE_BACKGROUND);
+ } else {
+ do_finish(s);
+ }
+ } else if (!signaled) {
+ s->last_exit = EXIT_NORMAL;
+ s->return_code = return_code;
+
+ do_finish(s);
+ } else { // signaled
+ s->last_exit = EXIT_SIGNALED;
+ s->return_code = return_code;
+
+ do_finish(s);
+ }
+ break;
+
+ case STATE_ERROR:
+ case STATE_INACTIVE:
+ case STATE_DONE:
+ print("unexpected error: %s died but it's inactive\n", s->name);
+ }
+}
diff --git a/src/fsvs/message.c b/src/fsvs/message.c
@@ -0,0 +1,52 @@
+#include "message.h"
+
+#include "config.h"
+
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const char* prog_usage[] = {
+ [PROG_FINIT] = "init <0|6>",
+ [PROG_FSVC] = "fsvc <command> [-v --verbose] [-V --version] [-r --runlevel <level>] [-s --service-dir <path>]\n"
+ " fsvc start [-p --pin] <service>\n"
+ " fsvc stop [-p --pin] <service>\n"
+ " fsvc enable [-o --once] <service>\n"
+ " fsvc disable [-o --once] <service>\n"
+ " fsvc kill <service> <signal|signum>\n"
+ " fsvc status [-c --check] <service>\n"
+ " fsvc pause <service>\n"
+ " fsvc resume <service>\n"
+ " fsvc switch [-f --reset] <runlevel>",
+ [PROG_FSVS] = "fsvs [-V --version] [-v --verbose] [-f --force] <service-dir> <runlevel>",
+ [PROG_HALT] = "halt [-n] [-f] [-d] [-w] [-B]",
+ [PROG_POWEROFF] = "poweroff [-n] [-f] [-d] [-w] [-B]",
+ [PROG_REBOOT] = "reboot [-n] [-f] [-d] [-w] [-B]",
+ [PROG_SEEDRNG] = "seedrng",
+ [PROG_SIGREMAP] = "sigremap [-s --single] [-v --verbose] [-V --version] <old-signal=new-signal...> <command> [args...]",
+ [PROG_VLOGGER] = "vlogger [-isS] [-f file] [-p pri] [-t tag] [message ...]",
+ [PROG_ZZZ] = "zzz [-n --noop] [-S --freeze] [-z --suspend] [-Z --hibernate] [-R --reboot] [-H --hybrid]"
+};
+
+static const char* prog_manual[] = {
+ [PROG_FINIT] = "finit 8",
+ [PROG_FSVC] = "fsvc 8",
+ [PROG_FSVS] = "fsvs 8",
+ [PROG_HALT] = "halt 8",
+ [PROG_POWEROFF] = "poweroff 8",
+ [PROG_REBOOT] = "reboot 8",
+ [PROG_SEEDRNG] = "seedrng 8",
+ [PROG_SIGREMAP] = "sigremap 8",
+ [PROG_VLOGGER] = "vlogger 1",
+ [PROG_ZZZ] = "zzz 8"
+};
+
+void print_usage_exit(enum prog prog, int status) {
+ fprintf(status ? stderr : stdout, "Usage: %s\n\nCheck manual '%s' for more information.\n", prog_usage[prog], prog_manual[prog]);
+ exit(status);
+}
+
+void print_version_exit(void) {
+ print(SV_VERSION);
+ exit(0);
+}
diff --git a/src/finit/message.h b/src/fsvs/message.h
diff --git a/src/fsvs/register.c b/src/fsvs/register.c
@@ -0,0 +1,96 @@
+#include "config.h"
+#include "service.h"
+#include "util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+static int init_supervise(struct service* s) {
+ int fd;
+ struct stat st;
+
+ if (fstatat(s->dir, "supervise", &st, 0) == -1 && mkdirat(s->dir, "supervise", 0755) == -1) {
+ return -1;
+ }
+
+ if (fstatat(s->dir, "supervise/ok", &st, 0) == -1 && mkfifoat(s->dir, "supervise/ok", 0666) == -1) {
+ fprint(1, "cannot create fifo at supervise/ok: %r\n");
+ return -1;
+ }
+
+ if (fstatat(s->dir, "supervise/control", &st, 0) == -1 && mkfifoat(s->dir, "supervise/control", 0644) == -1) {
+ fprint(1, "cannot create fifo at supervise/control: %r\n");
+ return -1;
+ }
+
+ if (openat(s->dir, "supervise/ok", O_RDONLY | O_NONBLOCK) == -1) {
+ fprint(1, "cannot open supervise/ok: %r\n");
+ return -1;
+ }
+
+ if ((s->control = openat(s->dir, "supervise/control", O_RDONLY | O_NONBLOCK)) == -1) {
+ fprint(1, "cannot open supervise/ok: %r\n");
+ return -1;
+ }
+
+ if ((fd = openat(s->dir, "supervise/lock", O_CREAT | O_WRONLY, 0644)) == -1) {
+ fprint(1, "cannot create supervise/lock: %r\n");
+ return -1;
+ }
+ close(fd);
+
+ return 0;
+}
+
+struct service* service_register(int dir, const char* name, bool is_log_service) {
+ struct service* s;
+ struct stat st;
+
+ if ((s = service_get(name)) == NULL) {
+ s = &services[services_size++];
+ s->state = STATE_INACTIVE;
+ s->restart = S_DOWN;
+ s->last_exit = EXIT_NONE;
+ s->return_code = 0;
+ s->fail_count = 0;
+ s->log_service = NULL;
+ s->paused = false;
+ s->log_pipe.read = 0;
+ s->log_pipe.write = 0;
+ s->is_log_service = is_log_service;
+ s->stop_timeout = 0;
+
+ if ((s->dir = openat(dir, name, O_DIRECTORY)) == -1) {
+ print_errno("error: cannot open '%s': %s\n", name);
+ services_size--;
+ return NULL;
+ }
+
+ if (init_supervise(s) == -1) {
+ services_size--;
+ return NULL;
+ }
+
+ strncpy(s->name, name, sizeof(s->name));
+
+ service_update_state(s, -1);
+ }
+
+ if (s->is_log_service) {
+ if (s->log_pipe.read == 0 || s->log_pipe.write == 0)
+ pipe((int*) &s->log_pipe);
+
+ } else if (!s->log_service && fstatat(s->dir, "log", &st, 0) != -1 && S_ISDIR(st.st_mode)) {
+ s->log_service = service_register(s->dir, "log", true);
+ }
+
+ service_write(s);
+
+ return s;
+}
diff --git a/src/fsvs/service.c b/src/fsvs/service.c
@@ -0,0 +1,86 @@
+#include "service.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+
+struct service services[SV_SERVICE_MAX];
+int services_size = 0;
+char runlevel[SV_NAME_MAX];
+int service_dir;
+const char* service_dir_path;
+int null_fd;
+bool daemon_running;
+
+struct service* service_get(const char* name) {
+ for (int i = 0; i < services_size; i++) {
+ if (streq(services[i].name, name))
+ return &services[i];
+ }
+ return NULL;
+}
+
+int service_refresh_directory(void) {
+ DIR* dp;
+ struct dirent* ep;
+ struct stat st;
+ struct service* s;
+
+ if ((dp = opendir(service_dir_path)) == NULL) {
+ fprint(1, "error: cannot open service directory: %r\n");
+ return -1;
+ }
+
+ for (int i = 0; i < services_size; i++) {
+ s = &services[i];
+ if (fstat(s->dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
+ service_stop(s);
+ close(s->dir);
+ close(s->control);
+ }
+ }
+
+ while ((ep = readdir(dp)) != NULL) {
+ if (ep->d_name[0] == '.')
+ continue;
+
+ if (fstatat(service_dir, ep->d_name, &st, 0) == -1 || !S_ISDIR(st.st_mode))
+ continue;
+
+ service_register(service_dir, ep->d_name, false);
+ }
+
+ closedir(dp);
+
+ for (int i = 0; i < services_size; i++) {
+ services[i].children_size = 0;
+ services[i].parents_size = 0;
+ }
+
+ for (int i = 0; i < services_size; i++)
+ service_update_dependency(&services[i]);
+
+ return 0;
+}
+
+
+bool service_need_restart(struct service* s) {
+ if (!daemon_running)
+ return false;
+
+ if (s->restart == S_RESTART)
+ return true;
+
+ for (int i = 0; i < s->parents_size; i++) {
+ if (service_need_restart(s->parents[i]))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/fsvs/service.h b/src/fsvs/service.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include "../../config.h"
+#include "types.h"
+#include "util.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+
+
+enum service_command {
+ X_UP = 'u', // starts the services, pin as started
+ X_DOWN = 'd', // stops the service, pin as stopped
+ X_ONCE = 'o', // starts the service, pin as once
+ X_TERM = 't', // same as down
+ X_KILL = 'k', // sends kill, pin as stopped
+ X_PAUSE = 'p', // pauses the service
+ X_CONT = 'c', // resumes the service
+ X_RESET = 'r', // resets the service
+ X_ALARM = 'a', // sends alarm
+ X_HUP = 'h', // sends hup
+ X_INT = 'i', // sends interrupt
+ X_QUIT = 'q', // sends quit
+ X_USR1 = '1', // sends usr1
+ X_USR2 = '2', // sends usr2
+ X_EXIT = 'x', // does nothing
+};
+
+enum service_state {
+ STATE_INACTIVE, // not started
+ STATE_SETUP, // ./setup running
+ STATE_STARTING, // ./start running
+ STATE_ACTIVE_FOREGROUND, // ./run running
+ STATE_ACTIVE_BACKGROUND, // ./start finished, ./stop not called yet
+ STATE_ACTIVE_DUMMY, // dependencies started
+ STATE_STOPPING, // ./stop running
+ STATE_FINISHING, // ./finish running
+ STATE_DONE, // ./stop finished
+ STATE_ERROR, // something went wrong
+};
+
+enum service_exit {
+ EXIT_NONE, // never exited
+ EXIT_NORMAL, // exited
+ EXIT_SIGNALED, // crashed
+};
+
+enum service_restart {
+ S_DOWN, // service should not be started
+ S_ONCE, // service should
+ S_RESTART, // service should be started
+};
+
+struct service_serial {
+ uint8_t status_change[8];
+ uint8_t state;
+ uint8_t return_code;
+ uint8_t fail_count;
+ uint8_t flags;
+ uint8_t pid[4];
+ uint8_t paused;
+ uint8_t restart;
+ uint8_t force_down;
+ uint8_t state_runit;
+};
+
+struct service {
+ char name[NAME_MAX]; // name of service
+ enum service_state state; // current state
+ pid_t pid; // pid of run
+ int dir; // dirfd
+ int control; // fd to supervise/control
+ time_t state_change; // last status change
+ enum service_restart restart; // should restart on exit
+ enum service_exit last_exit; // stopped signaled or exited
+ int return_code; // return code or signal
+ uint8_t fail_count; // current fail cound
+ bool is_log_service; // is a log service
+ bool paused; // is paused
+ time_t stop_timeout; // stop start-time
+ pipe_t log_pipe; // pipe for logging
+ struct service* log_service; // has a log_server otherwise NULL
+ int parents_size; // count of service depending on
+ struct service* parents[10]; // service depending on
+ int children_size; // count of dependencies
+ struct service* children[10]; // dependencies
+};
+
+extern struct service services[];
+extern int services_size;
+extern int null_fd;
+extern bool daemon_running;
+extern const char* service_dir_path;
+extern int service_dir;
+
+
+void service_encode(struct service* s, struct service_serial* buffer);
+struct service* service_get(const char* name);
+void service_handle_command(struct service* s, char command);
+void service_handle_exit(struct service* s, bool signaled, int return_code);
+void service_kill(struct service* s, int signal);
+bool service_need_restart(struct service* s);
+int service_refresh_directory(void);
+struct service* service_register(int dir, const char* name, bool is_log_service);
+void service_run(struct service* s);
+int service_send_command(char command, char extra, const char* service, struct service* response, int response_max);
+void service_start(struct service* s);
+const char* service_status_name(struct service* s);
+void service_stop(struct service* s);
+int service_supervise(const char* service_dir_, const char* service, bool once);
+void service_update_dependency(struct service* s);
+bool service_is_dependency(struct service* s);
+void service_update_state(struct service* s, int state);
+void service_write(struct service* s);
diff --git a/src/fsvs/start.c b/src/fsvs/start.c
@@ -0,0 +1,112 @@
+#include "service.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+static void set_pipes(struct service* s) {
+ if (s->is_log_service) {
+ close(s->log_pipe.write);
+ dup2(s->log_pipe.read, STDIN_FILENO);
+ close(s->log_pipe.read);
+ dup2(null_fd, STDOUT_FILENO);
+ dup2(null_fd, STDERR_FILENO);
+ } else if (s->log_service) { // aka has_log_service
+ close(s->log_service->log_pipe.read);
+ dup2(s->log_service->log_pipe.write, STDOUT_FILENO);
+ dup2(s->log_service->log_pipe.write, STDERR_FILENO);
+ close(s->log_service->log_pipe.write);
+ dup2(null_fd, STDIN_FILENO);
+ } else if (stat_mode("log") & S_IWRITE) { // is not
+ int log_fd;
+ if ((log_fd = open("log", O_WRONLY | O_TRUNC)) == -1)
+ log_fd = null_fd;
+
+ dup2(null_fd, STDIN_FILENO);
+ dup2(log_fd, STDOUT_FILENO);
+ dup2(log_fd, STDERR_FILENO);
+ } else if (S_ISREG(stat_mode("nolog"))) {
+ dup2(null_fd, STDIN_FILENO);
+ dup2(null_fd, STDOUT_FILENO);
+ dup2(null_fd, STDERR_FILENO);
+ } else {
+ char service_log[PATH_MAX];
+ int log_fd;
+
+ snprintf(service_log, PATH_MAX, "%s/%s.log", SV_LOG_DIR, s->name);
+
+ if ((log_fd = open(service_log, O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1)
+ log_fd = null_fd;
+
+ dup2(null_fd, STDIN_FILENO);
+ dup2(log_fd, STDOUT_FILENO);
+ dup2(log_fd, STDERR_FILENO);
+ }
+}
+
+void service_run(struct service* s) {
+ struct stat st;
+
+ if (fstatat(s->dir, "run", &st, 0) != -1 && st.st_mode & S_IXUSR) {
+ service_update_state(s, STATE_ACTIVE_FOREGROUND);
+ } else if (fstatat(s->dir, "start", &st, 0) != -1 && st.st_mode & S_IXUSR) {
+ service_update_state(s, STATE_STARTING);
+ } else if (fstatat(s->dir, "depends", &st, 0) != -1 && st.st_mode & S_IREAD) {
+ service_update_state(s, STATE_ACTIVE_DUMMY);
+ } else {
+ // fprint(1, "warn: %s: `run`, `start` or `depends` not found\n", s->name);
+ service_update_state(s, STATE_INACTIVE);
+ }
+
+ if (s->state != STATE_ACTIVE_DUMMY) {
+ if ((s->pid = fork()) == -1) {
+ fprint(1, "error: cannot fork process: %r\n");
+ exit(1);
+ } else if (s->pid == 0) { // child
+ if (setsid() == -1)
+ fprint(1, "error: cannot setsid: %r\n");
+
+ fchdir(s->dir);
+ set_pipes(s);
+
+ if (s->state == STATE_STARTING) {
+ execl("./start", "./start", NULL);
+ } else {
+ execl("./run", "./run", NULL);
+ }
+ fprint(1, "error: cannot execute service: %r\n");
+ _exit(1);
+ }
+ }
+}
+
+void service_start(struct service* s) {
+ struct stat st;
+
+ if (!daemon_running || s->state != STATE_INACTIVE)
+ return;
+
+ print("starting %s\n", s->name);
+ for (int i = 0; i < s->children_size; i++) {
+ service_start(s->children[i]);
+ }
+
+ if (fstatat(s->dir, "setup", &st, 0) != -1 && st.st_mode & S_IXUSR) {
+ if ((s->pid = fork_dup_cd_exec(s->dir, "./setup", null_fd, null_fd, null_fd)) == -1) {
+ fprint(1, "error: cannot execute ./setup: %r\n");
+ service_update_state(s, STATE_INACTIVE);
+ } else {
+ service_update_state(s, STATE_SETUP);
+ }
+ } else {
+ service_run(s);
+ }
+ print("started %s \n", s->name);
+}
diff --git a/src/fsvs/status.c b/src/fsvs/status.c
@@ -0,0 +1,94 @@
+#include "service.h"
+#include "util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+void service_update_state(struct service* s, int state) {
+ if (state != -1)
+ s->state = state;
+
+ s->state_change = time(NULL);
+
+ service_write(s);
+}
+
+void service_write(struct service* s) {
+ int fd;
+ const char* stat_human;
+ struct service_serial stat_runit;
+
+ if ((fd = openat(s->dir, "supervise/status.new", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+ fprint(1, "cannot open supervise/status: %r\n");
+ return;
+ }
+
+ service_encode(s, &stat_runit);
+
+ if (write(fd, &stat_runit, sizeof(stat_runit)) == -1) {
+ fprint(1, "cannot write to supervise/status: %r\n");
+ return;
+ }
+
+ close(fd);
+
+ if ((fd = openat(s->dir, "supervise/stat.new", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+ fprint(1, "cannot create supervise/stat: %r\n");
+ return;
+ }
+
+ stat_human = service_status_name(s);
+ if (write(fd, stat_human, strlen(stat_human)) == -1) {
+ fprint(1, "cannot write to supervise/stat: %r\n");
+ return;
+ }
+
+ close(fd);
+
+ if ((fd = openat(s->dir, "supervise/pid.new", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+ fprint(1, "cannot create supervise/stat: %r\n");
+ return;
+ }
+
+ dprintf(fd, "%d", s->pid);
+
+ close(fd);
+
+ renameat(s->dir, "supervise/status.new", s->dir, "supervise/status");
+ renameat(s->dir, "supervise/stat.new", s->dir, "supervise/stat");
+ renameat(s->dir, "supervise/pid.new", s->dir, "supervise/pid");
+}
+
+const char* service_status_name(struct service* s) {
+ switch (s->state) {
+ case STATE_SETUP:
+ return "setup";
+ case STATE_STARTING:
+ return "starting";
+ case STATE_ACTIVE_FOREGROUND:
+ return "run";
+ case STATE_ACTIVE_BACKGROUND:
+ return "run-background";
+ case STATE_ACTIVE_DUMMY:
+ return "run-dummy";
+ case STATE_FINISHING:
+ return "finishing";
+ case STATE_STOPPING:
+ return "stopping";
+ case STATE_INACTIVE:
+ return "down";
+ case STATE_DONE:
+ return "done";
+ case STATE_ERROR:
+ return "dead (error)";
+ default:
+ return NULL;
+ }
+}
diff --git a/src/fsvs/stop.c b/src/fsvs/stop.c
@@ -0,0 +1,48 @@
+#include "service.h"
+#include "util.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+
+void service_stop(struct service* s) {
+ switch (s->state) {
+ case STATE_ACTIVE_DUMMY:
+ service_handle_exit(s, false, 0);
+ break;
+ case STATE_ACTIVE_BACKGROUND:
+ if ((s->pid = fork_dup_cd_exec(s->dir, "./stop", null_fd, null_fd, null_fd)) == -1) {
+ fprint(1, "error: cannot execute ./stop: %r\n");
+ service_update_state(s, STATE_INACTIVE);
+ } else {
+ service_update_state(s, STATE_STOPPING);
+ }
+ break;
+ case STATE_ACTIVE_FOREGROUND:
+ case STATE_SETUP:
+ case STATE_STARTING:
+ case STATE_STOPPING:
+ case STATE_FINISHING:
+ s->stop_timeout = time(NULL);
+ kill(s->pid, SIGTERM);
+ service_update_state(s, -1);
+ break;
+ case STATE_DONE:
+ s->state = STATE_INACTIVE;
+ case STATE_INACTIVE:
+ case STATE_ERROR:
+ break;
+ }
+}
+
+void service_kill(struct service* s, int signal) {
+ if (!s->pid)
+ return;
+
+ if (s->state == STATE_ACTIVE_FOREGROUND)
+ kill(s->pid, signal);
+}
diff --git a/src/fsvs/supervise.c b/src/fsvs/supervise.c
@@ -0,0 +1,170 @@
+#include "config.h"
+#include "service.h"
+#include "util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+
+static void signal_child(int unused) {
+ (void) unused;
+
+ int status;
+ pid_t died_pid;
+ struct service* s = NULL;
+
+ if ((died_pid = wait(&status)) == -1) {
+ fprint(1, "error: cannot wait for process: %r\n");
+ return;
+ }
+
+ if (!WIFEXITED(status) && !WIFSIGNALED(status))
+ return;
+
+ for (int i = 0; i < services_size; i++) {
+ if (services[i].pid == died_pid) {
+ s = &services[i];
+ break;
+ }
+ }
+ if (s == NULL)
+ return;
+
+ service_handle_exit(s, WIFSIGNALED(status), WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status));
+}
+
+static void update_services(void) {
+ struct service* s;
+
+ for (int i = 0; i < services_size; i++) {
+ s = &services[i];
+ if (s->state == STATE_INACTIVE || s->state == STATE_ERROR)
+ s->stop_timeout = 0;
+
+ if (s->state == STATE_ERROR)
+ continue;
+
+ if (s->stop_timeout != 0) {
+ if (time(NULL) - s->stop_timeout >= SV_STOP_TIMEOUT) {
+ print(":: service '%s' doesn't terminate, killing...\n", s->name);
+ service_kill(s, SIGKILL);
+ s->stop_timeout = 0;
+ }
+ } else if (s->state == STATE_INACTIVE && service_need_restart(s)) {
+ service_start(s);
+ }
+ }
+}
+
+static void control_sockets(void) {
+ struct service* s;
+ char cmd;
+
+ for (int i = 0; i < services_size; i++) {
+ s = &services[i];
+ while (read(s->control, &cmd, 1) == 1) {
+ print("handling '%c' from %s\n", cmd, s->name);
+ service_handle_command(s, cmd);
+ }
+ }
+}
+
+void stop_dummies(void) {
+ for (int i = 0; i < services_size; i++) {
+ if (services[i].state != STATE_ACTIVE_DUMMY || services[i].restart == S_RESTART)
+ continue;
+
+ for (int j = 0; j < services[i].children_size; j++) {
+ struct service* dep = services[i].children[j];
+ if (dep->state != STATE_INACTIVE && dep->state != STATE_ERROR)
+ goto dont_stop;
+ }
+
+ service_stop(&services[i]);
+
+ dont_stop:;
+ }
+}
+
+int service_supervise(const char* service_dir_, const char* service, bool once) {
+ struct sigaction sigact = { 0 };
+ struct service* s;
+
+ daemon_running = true;
+
+ sigact.sa_handler = signal_child;
+ sigaction(SIGCHLD, &sigact, NULL);
+ sigact.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sigact, NULL);
+
+ service_dir_path = service_dir_;
+ if ((service_dir = open(service_dir_, O_DIRECTORY)) == -1) {
+ print_errno("error: cannot open directory %s: %s\n", service_dir_);
+ return 1;
+ }
+
+ if ((null_fd = open("/dev/null", O_RDWR)) == -1) {
+ fprint(1, "error: cannot open /dev/null: %r\n");
+ null_fd = 1;
+ }
+
+ print(":: starting services\n");
+
+ service_refresh_directory();
+
+ if ((s = service_get(service)) == NULL) {
+ fprint(1, "error: cannot start '%s': not found\n", service);
+ goto cleanup;
+ }
+
+ s->restart = once ? S_ONCE : S_RESTART;
+ service_start(s);
+
+
+ bool cont;
+ // accept connections and handle requests
+ do {
+ if (!daemon_running) {
+ for (int i = 0; i < services_size; i++) {
+ s = &services[i];
+ service_stop(s);
+ }
+ }
+
+ service_refresh_directory();
+ stop_dummies();
+ control_sockets();
+ update_services();
+
+ sleep(SV_CHECK_INTERVAL);
+
+ cont = false;
+ for (int i = 0; i < services_size; i++) {
+ if (services[i].state != STATE_INACTIVE && services[i].state != STATE_ERROR)
+ cont = true;
+ }
+ } while (cont);
+
+ print(":: terminating\n");
+
+ print(":: all services stopped\n");
+
+cleanup:
+
+ close(service_dir);
+ close(null_fd);
+
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+ return 0;
+}
diff --git a/src/halt/Makefile b/src/halt/Makefile
@@ -1,11 +1,12 @@
TOPDIR=../..
-include $(TOPDIR)/config.mk
-OBJS += wtmp.o ../common/util.o
+OBJS += wtmp.o
BINS = halt poweroff reboot shutdown
INTERM = halt.8 shutdown.8.txt
MANS = halt.8 shutdown.8
PAGES = halt.8.html shutdown.8.html
-HEADERS = ../common/util.h wtmp.h
+HEADERS = wtmp.h
+LIBS = $(LIBDIR)/libutil/libutil.a
include $(TOPDIR)/mk/prog.mk
diff --git a/src/halt/halt.c b/src/halt/halt.c
@@ -1,4 +1,4 @@
-#include "../common/util.h"
+#include "util.h"
#include "wtmp.h"
#include <errno.h>
@@ -33,7 +33,7 @@ int main(int argc, char* argv[]) {
rebootnum = RB_AUTOBOOT;
initarg = "6";
} else {
- fprintf(stderr, "invalid mode: %s\n", prog);
+ fprint(1, "invalid mode: %s\n", prog);
return 1;
}
@@ -60,7 +60,7 @@ int main(int argc, char* argv[]) {
write_wtmp(1);
return 0;
default:
- fprintf(stderr, "Usage: %s [-n] [-f] [-d] [-w] [-B]", prog);
+ fprint(1, "Usage: %s [-n] [-f] [-d] [-w] [-B]", prog);
return 1;
}
@@ -76,7 +76,7 @@ int main(int argc, char* argv[]) {
} else {
execl("/sbin/init", "init", initarg, NULL);
}
- print_errno("reboot failed: %s\n");
+ fprint(1, "reboot failed: %r\n");
}
return 0;
diff --git a/src/modules-load/Makefile b/src/modules-load/Makefile
@@ -1,11 +1,12 @@
TOPDIR=../..
-include $(TOPDIR)/config.mk
-OBJS = ../common/util.o
+OBJS =
BINS = modules-load
INTERM = modules-load.8.txt
MANS = modules-load.8
PAGES = modules-load.8.html
-HEADERS = ../common/util.h
+HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a $(LIBDIR)/libfmt/libfmt.a $(LIBDIR)/libutf/libutf.a
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
@@ -1,10 +1,9 @@
+#include "common.h"
-#include "../common/util.h"
-
+#include <bio.h>
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
+#include <fmt.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,7 +32,7 @@ static void read_cmdline(void) {
return;
if ((size = read(fd, kernel_cmdline, sizeof(kernel_cmdline))) == -1) {
- print_errno("cannot read /proc/cmdline: %s\n");
+ fprint(1, "cannot read /proc/cmdline: %r\n");
close(fd);
return;
}
@@ -62,23 +61,28 @@ static void read_cmdline(void) {
}
}
-static void read_file(const char* path) {
- int fd;
+static const char trim_chars[] = "#;\n\r";
+
+static const char* search_dirs[] = {
+ "/etc/modules-load.d/",
+ "/run/modules-load.d/",
+ "/usr/lib/modules-load.d/",
+};
+
+static void read_file(char* path) {
+ FILE* fd;
char line[MAX_MODULE_SIZE];
- char* comment;
+ char* ending;
- if ((fd = open(path, O_RDONLY)) == -1) {
- print_errno("unable to open %s: %s\n", path);
+ if (!(fd = fopen(path, "r"))) {
+ fprint(1, "unable to open %s: %r\n", path);
return;
}
- while (dgetline(fd, line, sizeof(line)) > 0) {
- if ((comment = strchr(line, '#')) != NULL) {
- *comment = '\0';
- }
- if ((comment = strchr(line, ';')) != NULL) {
- *comment = '\0';
- }
+ while (fgets(line, sizeof(line), fd)) {
+ for (const char* chr = trim_chars; *chr; chr++)
+ if ((ending = strchr(line, *chr)) != NULL)
+ *ending = '\0';
if (line[0] != '\0')
strcpy(modules[modules_size++], line);
@@ -95,7 +99,7 @@ static void read_dir(const char* path) {
char filepath[1024];
while ((de = readdir(dir)) != NULL) {
- if (de->d_name[0] == '.')
+ if (*de->d_name == '.')
continue;
strcpy(filepath, path);
@@ -110,16 +114,14 @@ static void read_dir(const char* path) {
int main(int argc, char** argv) {
read_cmdline();
- read_dir("/etc/modules-load.d/");
- read_dir("/run/modules-load.d/");
- read_dir("/usr/lib/modules-load.d/");
+ for (int i = 0; i < (int) (sizeof(search_dirs) / sizeof(*search_dirs)); i++)
+ read_dir(search_dirs[i]);
if (modules_size == 0)
return 0;
- for (int i = 0; i < modules_size; i++) {
- printf("%s\n", modules[i]);
- }
+ for (int i = 0; i < modules_size; i++)
+ print("%s\n", modules[i]);
char* args[modules_size + argc - 1 + 2 + 1];
int argi = 0;
@@ -127,18 +129,16 @@ int main(int argc, char** argv) {
args[argi++] = "modprobe";
args[argi++] = "-ab";
- for (int i = 1; i < argc; i++) {
+ for (int i = 1; i < argc; i++)
args[argi++] = argv[i];
- }
- for (int i = 0; i < modules_size; i++) {
+ for (int i = 0; i < modules_size; i++)
args[argi++] = modules[i];
- }
args[argi++] = NULL;
execvp("modprobe", args);
- print_errno("cannot exec modprobe: %s");
+ fprint(1, "cannot exec modprobe: %r\n");
return 1;
}
diff --git a/src/seedrng/Makefile b/src/seedrng/Makefile
@@ -7,5 +7,6 @@ INTERM =
MANS =
PAGES =
HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a $(LIBDIR)/libfmt/libfmt.a $(LIBDIR)/libutf/libutf.a
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/seedrng/seedrng.c b/src/seedrng/seedrng.c
@@ -3,6 +3,7 @@
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
+#include <fmt.h>
#include <linux/random.h>
#include <poll.h>
#include <stdbool.h>
@@ -376,7 +377,7 @@ static int seed_from_file_if_exists(const char* filename, int dfd, bool credit,
blake2s_update(hash, &seed_len, sizeof(seed_len));
blake2s_update(hash, seed, seed_len);
- printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
+ print("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
if (seed_rng(seed, seed_len, credit) < 0) {
ret = -errno;
perror("Unable to seed");
@@ -447,7 +448,7 @@ int main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
blake2s_update(&hash, new_seed, new_seed_len);
blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
- printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
+ print("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
fd = openat(dfd, NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
if (fd < 0) {
perror("Unable to open seed file for writing");
diff --git a/src/serdo/Makefile b/src/serdo/Makefile
@@ -0,0 +1,12 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = builtins.o
+BINS = serdo
+INTERM =
+MANS = serdo.8
+PAGES = serdo.8.html
+HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a $(LIBDIR)/libfmt/libfmt.a $(LIBDIR)/libutf/libutf.a
+
+include $(TOPDIR)/mk/prog.mk
+\ No newline at end of file
diff --git a/src/serdo/builtins.c b/src/serdo/builtins.c
@@ -0,0 +1,81 @@
+#include <errno.h>
+#include <fmt.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXENV 256
+
+
+extern char* envp[];
+extern int envc;
+
+int envset(char* s) {
+ char* l;
+
+ if (!(l = strchr(s, '='))) return -1;
+
+ for (int i = 0; i < envc && envp[i]; i++)
+ if (strncmp(envp[i], s, l - s) == 0) {
+ envp[i] = s;
+ return 0;
+ }
+
+ if (envc < MAXENV) {
+ envp[envc++] = s;
+ envp[envc] = 0;
+ return 0;
+ }
+ return -1;
+}
+
+
+int chdir_builtin(int argc, char** argv) {
+ if (argc != 2) {
+ fprint(1, "usage: %s <path>\n", argv[0]);
+ return 1;
+ }
+
+ if (chdir(argv[1]) == -1) {
+ fprint(1, "%s: unable to change dir to '%s': %s\n", argv[0], argv[1], strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+int export_builtin(int argc, char** argv) {
+ if (argc == 1) {
+ for (int i = 0; i < envc; i++)
+ print("%s\n", envp[i]);
+ return 0;
+ }
+
+ for (int i = 1; i < argc; i++) {
+ if (envset(argv[i]) == -1)
+ return 1;
+ }
+ return 0;
+}
+
+int execute(FILE*);
+
+int source_builtin(int argc, char** argv) {
+ FILE* f;
+ if (argc != 2) {
+ fprint(1, "source <file>\n");
+ return 1;
+ }
+ THROW_NULL(f = fopen(argv[1], "r"), "unable to open %s", 1, argv[1]);
+ return execute(f);
+}
+
+const struct builtin {
+ const char* name;
+ int (*func)(int argv, char** argc);
+} builtins[] = {
+ { "cd", chdir_builtin },
+ { "export", export_builtin },
+ { ".", source_builtin },
+ { "source", source_builtin },
+ { 0 }
+};
diff --git a/src/serdo/serdo.8.txt b/src/serdo/serdo.8.txt
@@ -0,0 +1,31 @@
+@man serdo 8 "JAN 2024" "%VERSION$" "fiss man page"
+@header serdo(8) %VERSION%
+
+@title name Name
+
+*serdo* - runs programs serially
+
+@title synopsis Synopsis
+
+*serdo* [*-c*] [filename]
+
+@title description Description
+
+*serdo* will open the file given by the command line argument and serially execute all the commands in it. If a command fails, the whole batch job is aborted (unless _-c_ is given as first paramter on the serdo command line). For testing purposes you can omit the filename and it will read from standard-input. *serdo* isn't supposed to be run as an interactive shell, thus there is no prompt or whatsoever. You can exit the session by running the *false* command or closing standard input (with Ctrl-D).
+
+serdo understands the *cd* and *export* sh(1) built-ins (no loops, no ~/home expansion, no $FOO expansion, no backticks).
+
+serdo is very limited by design, but it is nice to have if you just want to run a few ifconfig, ip, route commands in sequence. serdo will return the exit code of the last command it ran, 0 if none were given.
+
+@title built-in-commands Built-in Commands
+@list
+*cd* <*path*>~
+changes the directory to the given path.
+
+*export* [*variable=value* ...]~
+exports given valiables of overrides if already set. Executed without arguments will print current environment valiables. Remember that variable-expension isn't available, thus _export PATH="$PATH:/commands"_ will not work.
+@endlist
+
+@title author Author
+
+*serdo* is part of [*minit*](http://www.fefe.de/minit/) was originally written by Felix von Leitner and later modified by Friedel Schon
+\ No newline at end of file
diff --git a/src/serdo/serdo.c b/src/serdo/serdo.c
@@ -0,0 +1,144 @@
+#include "arg.h"
+#define _GNU_SOURCE
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <fmt.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define MAXENV 256
+
+const char* current_prog(void) {
+ return "serdo";
+}
+
+char* envp[MAXENV + 2];
+int envc;
+
+int continueonerror;
+
+extern const struct builtin {
+ const char* name;
+ int (*func)(int argv, char** argc);
+} builtins[];
+
+static int spawn(int argc, char** argv) {
+ pid_t pid;
+ int status;
+
+ for (const struct builtin* bi = builtins; bi->name; bi++) {
+ if (!strcmp(argv[0], bi->name))
+ return bi->func(argc, argv);
+ }
+
+ switch (pid = fork()) {
+ case -1:
+ fprint(1, "unable to fork: %r\n");
+ exit(1);
+ case 0:
+ execvpe(argv[0], argv, envp);
+ fprint(1, "unable to exec: %r\n");
+ _exit(1);
+ }
+
+ if (waitpid(pid, &status, 0) == -1)
+ fprint(1, "unable to waid for process: %r\n");
+
+ if (!WIFEXITED(status))
+ return -1;
+
+ return WEXITSTATUS(status);
+}
+
+static int doline(char* line, ssize_t line_size) {
+ int rc;
+ int i = 0;
+ int spacecount = 0;
+ int argc = 0;
+ char** argv;
+
+ while (isspace(line[i])) i++;
+
+ if (line[i] == '#')
+ return 0;
+
+ for (int j = i; j < line_size; j++)
+ if (isspace(line[j])) spacecount++;
+
+ argv = malloc((spacecount + 1) * sizeof(char*));
+
+ while (i < line_size && line[i] != '\n') {
+ if (line[i] == '"') {
+ i++;
+ argv[argc++] = &line[i];
+ while (i < line_size && line[i - 1] == '\\' && line[i] != '"') i++;
+ line[i++] = '\0';
+ } else if (line[i] == '\'') {
+ i++;
+ argv[argc++] = &line[i];
+ while (i < line_size && line[i - 1] == '\\' && line[i] != '\'') i++;
+ line[i++] = '\0';
+ } else {
+ argv[argc++] = &line[i];
+ while (i < line_size && !isspace(line[i])) i++;
+ line[i++] = '\0';
+ }
+
+ while (i < line_size && isspace(line[i])) i++;
+ }
+
+ argv[argc] = NULL;
+
+ rc = spawn(argc, argv);
+ free(argv);
+ return rc;
+}
+
+int execute(FILE* file) {
+ int rc = 0;
+ char* line = NULL;
+ size_t line_alloc = 0;
+ ssize_t line_size;
+
+ while ((line_size = getline(&line, &line_alloc, file)) > 0) {
+ if ((rc = doline(line, line_size)) && !continueonerror)
+ break;
+ }
+ if (line)
+ free(line);
+
+ return rc;
+}
+
+int main(int argc, char* argv[], char* env[]) {
+ FILE* f;
+ (void) argc;
+
+ for (envc = 0; envc < MAXENV && env[envc]; ++envc)
+ envp[envc] = env[envc];
+
+ envp[envc] = 0;
+
+ SHIFT(1);
+
+ if (argc > 1 && !strcmp(*argv, "-c")) {
+ continueonerror = 1;
+ SHIFT(1);
+ }
+
+ if (argc > 1) {
+ if (!(f = fopen(*argv, "r"))) {
+ fprint(1, "unable to open %s: %r\n", *argv);
+ return 1;
+ }
+ } else
+ f = stdin;
+
+ return execute(f);
+}
diff --git a/src/serdo/test.serdo b/src/serdo/test.serdo
diff --git a/src/sigremap/Makefile b/src/sigremap/Makefile
@@ -2,10 +2,11 @@ TOPDIR=../..
-include $(TOPDIR)/config.mk
OBJS =
-BINS = zzz ZZZ
-INTERM = zzz.8.txt
-MANS = zzz.8
-PAGES = zzz.8.html
+BINS = sigremap
+INTERM =
+MANS = sigremap.8
+PAGES = sigremap.8.html
HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/sigremap/sigremap.c b/src/sigremap/sigremap.c
@@ -21,9 +21,9 @@
*/
-#include "../common/util.h"
#include "message.h"
#include "signame.h"
+#include "util.h"
#include <assert.h>
#include <errno.h>
@@ -41,10 +41,10 @@ const char* current_prog(void) {
return "sigremap";
}
-#define DEBUG(...) \
- do { \
- if (debug) \
- fprintf(stderr, __VA_ARGS__); \
+#define DEBUG(...) \
+ do { \
+ if (debug) \
+ fprint(1, __VA_ARGS__); \
} while (0)
#define set_signal_undefined(old, new) \
@@ -166,11 +166,11 @@ static char** parse_command(int argc, char* argv[]) {
new ++;
if ((oldsig = signame(old)) == -1) {
- fprintf(stderr, "error: invalid old signal '%s'\n", old);
+ fprint(1, "error: invalid old signal '%s'\n", old);
exit(1);
}
if ((newsig = signame(new)) == -1) {
- fprintf(stderr, "error: invalid new signal '%s'\n", new);
+ fprint(1, "error: invalid new signal '%s'\n", new);
exit(1);
}
signal_remap[oldsig] = newsig;
@@ -247,14 +247,14 @@ int main(int argc, char* argv[]) {
child_pid = fork();
if (child_pid < 0) {
- print_errno("error: unable to fork: %s\n");
+ fprint(1, "error: unable to fork: %r\n");
return 1;
} else if (child_pid == 0) {
/* child */
sigprocmask(SIG_UNBLOCK, &all_signals, NULL);
if (use_setsid) {
if (setsid() == -1) {
- print_errno("error: unable to setsid: %s\n");
+ fprint(1, "error: unable to setsid: %r\n");
exit(1);
}
diff --git a/src/supervise/Makefile b/src/supervise/Makefile
@@ -0,0 +1,28 @@
+TOPDIR=../..
+-include $(TOPDIR)/config.mk
+
+OBJS = \
+ write_status.o \
+ encode.o \
+ control_loop.o \
+ need_restart.o \
+ rotate_state.o \
+ state/inactive.o \
+ state/dependency.o \
+ state/setup.o \
+ state/starting.o \
+ state/active_foreground.o \
+ state/active_background.o \
+ state/active_dummy.o \
+ state/stopping.o \
+ state/finishing.o \
+ state/active_pid.o \
+ state_functions.o
+BINS = supervise
+INTERM =
+MANS =
+PAGES =
+HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a
+
+include $(TOPDIR)/mk/prog.mk
+\ No newline at end of file
diff --git a/src/supervise/defs.h b/src/supervise/defs.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "types.h"
+
+#include <stdbool.h>
+#include <time.h>
+
+#define PID_BUFFER_MAX 16
+#define STAT_BUFFER_MAX 128
+
+#define WANT_ROTATE(current, next) \
+ (current) != (next) && IS_ACTIVE(current) != IS_ACTIVE(next)
+
+
+typedef enum command {
+ X_UP = 'u', // starts the services, pin as started
+ X_DOWN = 'd', // stops the service, pin as stopped
+ X_ONCE = 'o', // starts the service, pin as once
+ X_TERM = 't', // same as down
+ X_KILL = 'k', // sends kill, pin as stopped
+ X_PAUSE = 'p', // pauses the service
+ X_CONT = 'c', // resumes the service
+ X_RESET = 'r', // resets the service
+ X_ALARM = 'a', // sends alarm
+ X_HUP = 'h', // sends hup
+ X_INT = 'i', // sends interrupt
+ X_QUIT = 'q', // sends quit
+ X_USR1 = '1', // sends usr1
+ X_USR2 = '2', // sends usr2
+ X_EXIT = 'x', // does nothing
+} command_t;
+
+
+typedef enum state {
+ STATE_INACTIVE, // not started
+ STATE_DEPENDENCY, // waiting for dependencies
+ STATE_SETUP, // ./setup running
+ STATE_STARTING, // ./start running
+ STATE_READY, // ./ready running, waiting for finish
+ STATE_ACTIVE_FOREGROUND, // ./run running
+ STATE_ACTIVE_BACKGROUND, // ./start finished, ./stop not called yet
+ STATE_ACTIVE_PID, // ./start finished, waiting for ./pid
+ STATE_ACTIVE_DUMMY, // dependencies started
+ STATE_STOPPING, // ./stop running
+ STATE_FINISHING, // ./finish running
+} state_t;
+
+#define STATE_MAX STATE_FINISHING
+
+typedef struct serial {
+ u8 status_change[8];
+ u8 state;
+ u8 return_code;
+ u8 fail_count;
+ u8 flags;
+ u8 pid[4];
+ u8 paused;
+ u8 restart;
+ u8 force_down;
+ u8 state_runit;
+} serial_t;
+
+typedef struct service {
+ char name[NAME_MAX]; // name of service
+ state_t state; // current state
+ state_t desired_state; // should update state (inactive -> setup -> ...)
+ time_t state_change; // last status change
+ time_t desired_state_change; // last desired status change
+ bool should_restart; // if inactive, push active
+ pid_t pid; // pid of run
+ u8 dependent_count; // count of services depends on me
+ u32 death_count; // current fail cound
+ i32 death_last; // last death (waitstatus)
+ bool is_log_service; // is a log service
+ bool paused; // is paused
+ time_t stop_timeout; // stop start-time
+ pipe_t log_pipe; // pipe for logging
+ bool has_log_service; // has a log_server otherwise NULL
+ struct { // fd's of supervise/*
+ i32 control; // fd of control
+ i32 ok; // fd of ok
+ i32 lock; // fd of lock
+ i32 dependent; // fd of depends
+ } supervise;
+} service_t;
+
+extern service_t service;
+
+const char* state_name(state_t state);
+void encode(serial_t* buffer);
+int write_status(void);
+void control_loop(void);
+state_t get_rotated_state(void);
+int rotate_state(bool now);
+bool need_restart(void);
diff --git a/src/supervise/encode.c b/src/supervise/encode.c
@@ -0,0 +1,102 @@
+#include "defs.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+const char* state_name(state_t state) {
+ switch (state) {
+ case STATE_INACTIVE:
+ return "inactive";
+ case STATE_DEPENDENCY:
+ return "waiting for dependenies";
+ case STATE_SETUP:
+ return "setup";
+ case STATE_STARTING:
+ return "starting";
+ case STATE_ACTIVE_DUMMY:
+ return "active (dummy)";
+ case STATE_ACTIVE_FOREGROUND:
+ return "active (foreground)";
+ case STATE_ACTIVE_BACKGROUND:
+ return "active (background)";
+ case STATE_STOPPING:
+ return "stopping";
+ case STATE_FINISHING:
+ return "finishing";
+ default:
+ return "unknown";
+ }
+}
+
+void store_status(serial_t* buffer, char* stat_buffer, char* pid_buffer) {
+ u64 tai = (u64) service.state_change + 4611686018427387914llu;
+
+ stat_buffer[0] = '\0';
+ pid_buffer[0] = '\0';
+
+ // lower-endian
+ buffer->status_change[0] = (tai >> 56) & 0xff;
+ buffer->status_change[1] = (tai >> 48) & 0xff;
+ buffer->status_change[2] = (tai >> 40) & 0xff;
+ buffer->status_change[3] = (tai >> 32) & 0xff;
+ buffer->status_change[4] = (tai >> 24) & 0xff;
+ buffer->status_change[5] = (tai >> 16) & 0xff;
+ buffer->status_change[6] = (tai >> 8) & 0xff;
+ buffer->status_change[7] = (tai >> 0) & 0xff;
+
+ // big-endian (network)
+ buffer->pid[0] = (service.pid >> 0) & 0xff;
+ buffer->pid[1] = (service.pid >> 8) & 0xff;
+ buffer->pid[2] = (service.pid >> 16) & 0xff;
+ buffer->pid[3] = (service.pid >> 24) & 0xff;
+
+ buffer->state = service.state;
+ buffer->fail_count = service.death_count;
+
+ buffer->flags = ((service.stop_timeout != 0) << 4) |
+ ((service.should_restart) << 3) |
+ ((service.should_restart) << 2);
+
+ buffer->paused = service.paused;
+ buffer->restart = need_restart() ? 'u' : 'd';
+ buffer->force_down = 0;
+
+ if (service.death_last == 0) {
+ buffer->return_code = 0;
+ buffer->flags |= 0;
+ } else if (WIFEXITED(service.death_last)) {
+ buffer->return_code = WEXITSTATUS(service.death_last);
+ buffer->flags |= 1;
+ } else {
+ buffer->return_code = WTERMSIG(service.death_last);
+ buffer->flags |= 2;
+ }
+
+ switch (service.state) {
+ case STATE_INACTIVE:
+ buffer->state_runit = 0; // inactive
+ break;
+ case STATE_DEPENDENCY:
+ case STATE_SETUP:
+ case STATE_STARTING:
+ case STATE_ACTIVE_DUMMY:
+ case STATE_ACTIVE_FOREGROUND:
+ case STATE_ACTIVE_BACKGROUND:
+ case STATE_ACTIVE_PID:
+ buffer->state_runit = 1; // running
+ break;
+ case STATE_STOPPING:
+ case STATE_FINISHING:
+ buffer->state_runit = 2; // finishing
+ break;
+ }
+
+
+ strcpy(stat_buffer, state_name(service.state));
+ if (service.desired_state) {
+ strcat(stat_buffer, ", wants ");
+ strcat(stat_buffer, state_name(service.desired_state));
+ }
+ if (service.paused)
+ strcat(stat_buffer, ", is paused");
+}
diff --git a/src/supervise/need_restart.c b/src/supervise/need_restart.c
@@ -0,0 +1,25 @@
+#include "common.h"
+#include "defs.h"
+
+#include <fcntl.h>
+#include <stdbool.h>
+
+
+bool need_restart(void) {
+ struct flock lock = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0
+ };
+
+ if (service.should_restart)
+ return true;
+
+ if (fcntl(service.supervise.lock, F_GETLK, &lock) == -1)
+ errprint("unable to retrieve locks of 'supervise/depends'");
+ // F_SETLK could throw if lock couldn't be placed, F_GETLK should not throw
+ THROW_MIN(, "unable to retrieve locks of 'supervise/depends'", EXIT_PERM);
+
+ return lock.l_type != F_ULOCK;
+}
diff --git a/src/supervise/rotate_state.c b/src/supervise/rotate_state.c
@@ -0,0 +1,73 @@
+#include "defs.h"
+#include "util.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+state_t get_rotated_state(void) {
+ bool depends = !access("depends", R_OK),
+ setup = !access("setup", X_OK),
+ start = !access("start", X_OK),
+ run = !access("run", X_OK),
+ pid = !access("pid", X_OK),
+ stop = !access("stop", X_OK),
+ finish = !access("finish", X_OK);
+
+ switch (service.state) {
+ case STATE_INACTIVE:
+ return depends ? STATE_DEPENDENCY
+ : setup ? STATE_SETUP
+ : start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ case STATE_DEPENDENCY:
+ return setup ? STATE_SETUP
+ : start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ case STATE_SETUP:
+ return start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ case STATE_STARTING:
+ return pid ? STATE_ACTIVE_PID
+ : STATE_ACTIVE_BACKGROUND;
+ case STATE_ACTIVE_FOREGROUND:
+ return finish ? STATE_FINISHING
+ : STATE_INACTIVE;
+ case STATE_ACTIVE_BACKGROUND:
+ case STATE_ACTIVE_PID:
+ return stop ? STATE_STOPPING
+ : finish ? STATE_FINISHING
+ : !need_restart() ? STATE_INACTIVE
+ : setup ? STATE_SETUP
+ : start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ case STATE_ACTIVE_DUMMY:
+ return finish ? STATE_FINISHING
+ : !need_restart() ? STATE_INACTIVE
+ : setup ? STATE_SETUP
+ : start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ case STATE_STOPPING:
+ return finish ? STATE_FINISHING
+ : !need_restart() ? STATE_INACTIVE
+ : setup ? STATE_SETUP
+ : start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ case STATE_FINISHING:
+ return !need_restart() ? STATE_INACTIVE
+ : setup ? STATE_SETUP
+ : start ? STATE_STARTING
+ : run ? STATE_ACTIVE_FOREGROUND
+ : STATE_ACTIVE_DUMMY;
+ }
+ return -1;
+}
+
+int rotate_state(bool now) {
+}
diff --git a/src/supervise/state_functions.c b/src/supervise/state_functions.c
@@ -0,0 +1,14 @@
+#include "defs.h"
+
+state_function_t state_functions[] = {
+ [STATE_INACTIVE] = {},
+ [STATE_DEPENDENCY] = state_dependency,
+ [STATE_SETUP] = state_setup,
+ [STATE_STARTING] = state_starting,
+ [STATE_ACTIVE_FOREGROUND] = state_active_foreground,
+ [STATE_ACTIVE_BACKGROUND] = state_active_background,
+ [STATE_ACTIVE_PID] = state_active_pid,
+ [STATE_ACTIVE_DUMMY] = state_active_dummy,
+ [STATE_STOPPING] = state_stopping,
+ [STATE_FINISHING] = state_finishing,
+};
+\ No newline at end of file
diff --git a/src/supervise/supervise.c b/src/supervise/supervise.c
@@ -0,0 +1,86 @@
+#include "common.h"
+#include "defs.h"
+#include "types.h"
+#include "util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+#define RUN_PATH "/run/fiss"
+
+service_t service;
+
+const char* current_prog(void) {
+ return "supervise";
+}
+
+int main(int argc, char** argv) {
+ static char path_buffer[PATH_MAX];
+ static char supervise_path[PATH_MAX];
+ struct stat sstruct;
+
+ if (argc >= 2 && chdir(argv[1]) == -1) {
+ fprint(1, "unable to change directory to '%s': %s\n", argv[1], strerror(errno));
+ exit(errno == ENOENT ? EXIT_USER : EXIT_PERM);
+ }
+
+ // reset service struct
+ memset(&service, 0, sizeof service);
+
+ THROW_NULL(getcwd(supervise_path, sizeof supervise_path), "unable to get current directory", EXIT_PERM);
+
+ // copy the last component of {PWD} to name
+ strcpy(service.name, strrchr(supervise_path, '/') + 1);
+
+ path_join(path_buffer, RUN_PATH, "supervise", NULL);
+
+ if (stat(RUN_PATH, &sstruct) == -1) {
+ THROW_MIN(mkdir(RUN_PATH, 0666), "unable to create %s", EXIT_TEMP, RUN_PATH);
+ } else if (!S_ISDIR(sstruct.st_mode)) {
+ fprint(1, "%s exists, but is not a directory", RUN_PATH);
+ exit(EXIT_TEMP);
+ }
+
+ if (stat(path_buffer, &sstruct) == -1) {
+ THROW_MIN(mkdir(path_buffer, 0666), "unable to create %s", EXIT_TEMP, path_buffer);
+ } else if (!S_ISDIR(sstruct.st_mode)) {
+ fprint(1, "%s exists, but is not a directory", RUN_PATH);
+ exit(EXIT_TEMP);
+ }
+
+ // symlink /run/fiss/supervise/<service> to ./supervise
+ THROW_MIN(symlink(path_buffer, "supervise"), "unable to link '%s' to 'supervise'", EXIT_TEMP, path_buffer);
+
+ // open ./supervise/lock
+ THROW_MIN(service.supervise.lock = open("supervise/lock", O_WRONLY | O_CREAT, 0600), "unable to open 'supervise/lock'", EXIT_PERM);
+
+ // lock ./supervise/lock, don't block! That means it will just throw if another instance is running
+ THROW_MIN(flock(service.supervise.lock, LOCK_EX | LOCK_NB), "unable to lock 'supervise/lock', probably an other 'supervise'-instance is running", EXIT_PERM);
+
+ // make fifo at ./supervise/ok, everyone should be able to check at least that
+ // the service is online, thus perm 666 instead of 600
+ THROW_MIN(mkfifo("supervise/ok", 0666), "unable to create 'supervise/control'", EXIT_TEMP);
+
+ // make fifo at ./supervise/control, only root should be able to control the service thus 0600
+ THROW_MIN(mkfifo("supervise/control", 0600), "unable to create 'supervise/control'", EXIT_TEMP);
+
+ // open ./supervise/ok and just leave it like that
+ THROW_MIN(service.supervise.ok = open("supervise/ok", O_RDONLY), "unable to open 'supervise/ok'", EXIT_PERM);
+
+ // open ./supervise/control
+ THROW_MIN(service.supervise.control = open("supervise/control", O_RDONLY), "unable to open 'supervise/control'", EXIT_PERM);
+
+ // open ./supervise/depends
+ THROW_MIN(service.supervise.dependent = open("supervise/depends", O_RDONLY | O_CREAT, 0666), "unable to open 'supervise/depends'", EXIT_PERM);
+
+ THROW_MIN(write_status(), "unable to write status", -1);
+}
diff --git a/src/supervise/write_status.c b/src/supervise/write_status.c
@@ -0,0 +1,24 @@
+#include "defs.h"
+#include "open.h"
+
+#include <string.h>
+
+int write_status(void) {
+ struct serial serial;
+ char pid_buffer[16];
+ int buffer_len;
+
+ encode(&serial);
+ if (openwrite("supervise/status", O_TRUNC, 0644, &serial, sizeof(serial)) == -1)
+ return -1;
+
+ buffer_len = snprintf(pid_buffer, sizeof pid_buffer, "%d", service.pid);
+ if (openwrite("supervise/pid", O_TRUNC, 0644, &pid_buffer, buffer_len) == -1)
+ return -1;
+
+ const char* stat = state_name(service.state);
+ if (openwrite("supervise/stat", O_TRUNC, 0644, stat, strlen(stat)) == -1)
+ return -1;
+
+ return 0;
+}
diff --git a/src/vlogger/Makefile b/src/vlogger/Makefile
@@ -1,11 +1,12 @@
TOPDIR=../..
-include $(TOPDIR)/config.mk
-OBJS = ../finit/message.o
+OBJS = ../fsvs/message.o
BINS = vlogger
INTERM = vlogger.8.txt
MANS = vlogger.8
PAGES = vlogger.8.html
-HEADERS = ../finit/message.h
+HEADERS = ../fsvs/message.h
+LIBS = $(LIBDIR)/libutil/libutil.a
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/vlogger/vlogger.c b/src/vlogger/vlogger.c
@@ -1,7 +1,7 @@
-#include "../common/util.h"
-#include "../finit/message.h"
+#include "../fsvs/message.h"
#include "config.h"
+#include "util.h"
#include <errno.h>
#include <libgen.h>
@@ -158,7 +158,7 @@ int main(int argc, char* argv[]) {
sfacility = ident->name;
}
execl("/etc/vlogger", argv0, tag ? tag : "", slevel, sfacility, NULL);
- print_errno("error: unable to exec /etc/vlogger: %s\n");
+ fprint(1, "error: unable to exec /etc/vlogger: %r\n");
exit(1);
}
diff --git a/src/zzz/Makefile b/src/zzz/Makefile
@@ -7,5 +7,6 @@ INTERM = zzz.8.txt
MANS = zzz.8
PAGES = zzz.8.html
HEADERS =
+LIBS = $(LIBDIR)/libutil/libutil.a
include $(TOPDIR)/mk/prog.mk
\ No newline at end of file
diff --git a/src/zzz/zzz.c b/src/zzz/zzz.c
@@ -1,5 +1,5 @@
-#include "../common/util.h"
#include "config.h"
+#include "util.h"
#include <errno.h>
#include <fcntl.h>
@@ -45,7 +45,7 @@ int main(int argc, char** argv) {
new_state = "disk";
new_disk = "platform";
} else {
- fprintf(stderr, "error: program-name `%s` invalid\n", argv[0]);
+ fprint(1, "error: program-name `%s` invalid\n", argv[0]);
return 1;
}
@@ -90,7 +90,7 @@ int main(int argc, char** argv) {
new_disk = "suspend";
break;
default:
- printf("zzz [-n] [-S] [-z] [-Z] [-R] [-H]\n");
+ print("zzz [-n] [-S] [-z] [-Z] [-R] [-H]\n");
return 1;
}
}
@@ -100,11 +100,11 @@ int main(int argc, char** argv) {
if (stat(SV_SUSPEND_EXEC, &st) == 0 && st.st_mode & S_IXUSR) {
if ((pid = fork()) == -1) {
- print_errno("failed to fork for " SV_SUSPEND_EXEC ": %s\n");
+ fprint(1, "failed to fork for " SV_SUSPEND_EXEC ": %r\n");
return 1;
} else if (pid == 0) { // child
execl(SV_SUSPEND_EXEC, SV_SUSPEND_EXEC, NULL);
- print_errno("failed to execute " SV_SUSPEND_EXEC ": %s\n");
+ fprint(1, "failed to execute " SV_SUSPEND_EXEC ": %r\n");
_exit(1);
}
@@ -123,11 +123,11 @@ int main(int argc, char** argv) {
if (stat(SV_RESUME_EXEC, &st) == 0 && st.st_mode & S_IXUSR) {
if ((pid = fork()) == -1) {
- print_errno("failed to fork for " SV_RESUME_EXEC ": %s\n");
+ fprint(1, "failed to fork for " SV_RESUME_EXEC ": %r\n");
return 1;
} else if (pid == 0) { // child
execl(SV_RESUME_EXEC, SV_RESUME_EXEC, NULL);
- print_errno("failed to execute " SV_RESUME_EXEC ": %s\n");
+ fprint(1, "failed to execute " SV_RESUME_EXEC ": %r\n");
_exit(1);
}
diff --git a/state-next.md b/state-next.md
@@ -0,0 +1,12 @@
+| v from / > to | dependency | setup | starting | active_foreground | active_background | active_dummy | stopping | finishing | dependency_end | inactive |
+| ----------------- | -------------- | ------------ | ------------ | ----------------- | ----------------- | ------------ | ----------- | ------------- | -------------- | -------- |
+| inactive | if `./depends` | if `./setup` | if `./start` | if `./run` | | else | | | | |
+| dependency | | if `./setup` | if `./start` | if `./run` | | else | | | | |
+| setup | | | if `./start` | if `./run` | | else | | | | |
+| starting | | | | | else | | | | | |
+| active_foreground | | | | | | | | if `./finish` | if !restart | else |
+| active_background | | | | | | | if `./stop` | if `./finish` | if !restart | else |
+| active_dummy | | | | | | | | if `./finish` | if !restart | else |
+| stopping | | | | | | | | if `./finish` | if !restart | else |
+| finishing | | | | | | | | | if !restart | else |
+| dependency_end | | | | | | | | | | else |
diff --git a/supervise/lock b/supervise/lock
diff --git a/supervise/pid b/supervise/pid
diff --git a/supervise/stat b/supervise/stat
@@ -0,0 +1 @@
+down
diff --git a/supervise/status b/supervise/status
Binary files differ.
diff --git a/tools/make-docs.py b/tools/make-docs.py
@@ -1,38 +1,25 @@
import sys
import re
+if len(sys.argv) < 2:
+ print("make-docs.py <template> [infile]")
+ exit(1)
+
+temp_path = sys.argv[1]
infile = sys.stdin
-if len(sys.argv) >= 2:
- infile = open(sys.argv[1])
+if len(sys.argv) >= 3:
+ infile = open(sys.argv[2])
WIDTH = 80
HEADER_CHAR = '='
TITLE_CHAR = '-'
-HEADER_SUFFIX = "<span class=right><span id=toggle_dark onclick=toggle_dark()> turn the lights off </span> <a href=https://github.com/friedelschoen/fiss><img id=github alt=GitHub src=assets/github-mark.svg /></a></span>"
-
-PREFIX = """<!doctype html>
-<html lang=en>
-
-<head>
- <title>Friedel's Initialization and Service Supervision</title>
- <meta charset=utf-8 />
- <meta name=viewport content='width=device-width,initial-scale=1' />
- <link rel=stylesheet href=assets/style.css />
- <script type=text/javascript src=assets/toggle-dark.js></script>
-</head>
-
-<body>
-<div id=wrapper>"""
-
-SUFFIX = """
-</div>
-</body>
-</html>
-"""
-
-HEADER_TEMPLATE = "<span class=header><a class=title id=top href=#top>{text}</a><span class=right><span id=toggle_dark onclick=toggle_dark()> turn the lights on </span> <a href=https://github.com/friedelschoen/fiss><img id=github alt=GitHub src=assets/github-mark.svg /></a></span></span>"
+HEADER_SUFFIX = "<span class=right><span id=toggle_dark onclick=toggle_dark()> turn the lights off </span> <a href=https://github.com/friedelschoen/fiss><img id=github alt=GitHub src=github-mark.svg /></a></span>"
+HEADER_TEMPLATE = "<span class=header><a class=title id=top href=#top>{text}</a><span class=right><span id=toggle_dark onclick=toggle_dark()> turn the lights on </span> <a href=https://github.com/friedelschoen/fiss><img id=github alt=GitHub src=github-mark.svg /></a></span></span>"
TITLE_TEMPLATE = "<a class=title id={id} href=#{id}>{text}</a>"
+with open(temp_path) as temp:
+ PREFIX, SUFFIX = temp.read().split('%%%', 1)
+
def inline_convert(text):
text = re.sub(r'\*(.+?)\*', r'<b>\1</b>', text)
text = re.sub(r'_(.+?)_', r'<u>\1</u>', text)