config/git: import suckless sbase

i currently use the output of sha512sum in several
places of xbmk, which is a bit unreliable in case
output changes.

other cases where i use util outputs in variables
are probably reliable, because i'm using mostly
posix utilities in those.

to mitigate this, i now import suckless sbase, which
has a reasonable sha512sum implementation.

*every* binary it builds is being placed in build.list,
because i'll probably start using more of them.

for example, i may start modifying the "date"
implementation, adding the GNU-specific options that
i need as mentioned on init.sh

i'm importing it in util/ because the sha512sum
util is needed for verifying project sources, so
if sbase itself is a "project source", that means
we can into a chicken and egg bootstrapping problem.

this is sbase at revision:
055cc1ae1b3a13c3d8f25af0a4a3316590efcd48

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2025-10-04 09:14:33 +01:00
parent 2cfaba181b
commit e9a910b33c
281 changed files with 28415 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
*.o
/build
/getconf.h
/libutf.a
/libutil.a
/basename
/cal
/cat
/chgrp
/chmod
/chown
/chroot
/cksum
/cmp
/cols
/comm
/cp
/cron
/cut
/date
/dd
/dirname
/du
/echo
/ed
/env
/expand
/expr
/false
/find
/flock
/fold
/getconf
/grep
/head
/hostname
/join
/kill
/link
/ln
/logger
/logname
/ls
/md5sum
/mkdir
/mkfifo
/mknod
/mktemp
/mv
/nice
/nl
/nohup
/od
/paste
/pathchk
/printenv
/printf
/pwd
/readlink
/renice
/rev
/rm
/rmdir
/sbase-box
/sed
/seq
/setsid
/sha1sum
/sha224sum
/sha256sum
/sha384sum
/sha512sum
/sha512-224sum
/sha512-256sum
/sleep
/sort
/split
/sponge
/strings
/sync
/tail
/tar
/tee
/test
/tftp
/time
/touch
/tr
/true
/tsort
/tty
/uname
/unexpand
/uniq
/unlink
/uudecode
/uuencode
/wc
/which
/whoami
/xargs
/xinstall
/yes
+63
View File
@@ -0,0 +1,63 @@
MIT License
© 2011 Connor Lane Smith <cls@lubutu.com>
© 2011-2016 Dimitris Papastamos <sin@2f30.org>
© 2014-2016 Laslo Hunhold <dev@frign.de>
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.
Authors/contributors include:
© 2011 Kamil Cholewiński <harry666t@gmail.com>
© 2011 Rob Pilling <robpilling@gmail.com>
© 2011 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2011 pancake <pancake@youterm.com>
© 2011 Random832 <random832@fastmail.us>
© 2012 William Haddon <william@haddonthethird.net>
© 2012 Kurt H. Maier <khm@sciops.net>
© 2012 Christoph Lohmann <20h@r-36.net>
© 2012 David Galos <galosd83@students.rowan.edu>
© 2012 Robert Ransom <rransom.8774@gmail.com>
© 2013 Jakob Kramer <jakob.kramer@gmx.de>
© 2013 Anselm R Garbe <anselm@garbe.us>
© 2013 Truls Becken <truls.becken@gmail.com>
© 2013 dsp <dsp@2f30.org>
© 2013 Markus Teich <markus.teich@stusta.mhn.de>
© 2013 Jesse Ogle <jesse.p.ogle@gmail.com>
© 2013 Lorenzo Cogotti <miciamail@hotmail.it>
© 2013 Federico G. Benavento <benavento@gmail.com>
© 2013 Roberto E. Vargas Caballero <k0ga@shike2.com>
© 2013 Christian Hesse <mail@eworm.de>
© 2013 Markus Wichmann <nullplan@gmx.net>
© 2014 Silvan Jegen <s.jegen@gmail.com>
© 2014 Daniel Bainton <dpb@driftaway.org>
© 2014 Tuukka Kataja <stuge@xor.fi>
© 2014 Jeffrey Picard <jeff@jeffreypicard.com>
© 2014 Evan Gates <evan.gates@gmail.com>
© 2014 Michael Forney <mforney@mforney.org>
© 2014 Ari Malinen <ari.malinen@gmail.com>
© 2014 Brandon Mulcahy <brandon@jangler.info>
© 2014 Adria Garriga <rhaps0dy@installgentoo.com>
© 2014-2015 Greg Reagle <greg.reagle@umbc.edu>
© 2015 Tai Chi Minh Ralph Eastwood <tcmreastwood@gmail.com>
© 2015 Quentin Rameau <quinq@quinq.eu.org>
© 2015 Dionysis Grigoropoulos <info@erethon.com>
© 2015 Wolfgang Corcoran-Mathe <wcm@sigwinch.xyz>
© 2016 Mattias Andrée <maandree@kth.se>
© 2016 Eivind Uggedal <eivind@uggedal.com>
+256
View File
@@ -0,0 +1,256 @@
.POSIX:
include config.mk
.SUFFIXES:
.SUFFIXES: .o .c
CPPFLAGS =\
-D_DEFAULT_SOURCE \
-D_NETBSD_SOURCE \
-D_BSD_SOURCE \
-D_XOPEN_SOURCE=700 \
-D_FILE_OFFSET_BITS=64
HDR =\
arg.h\
compat.h\
crypt.h\
fs.h\
md5.h\
queue.h\
sha1.h\
sha224.h\
sha256.h\
sha384.h\
sha512.h\
sha512-224.h\
sha512-256.h\
text.h\
utf.h\
util.h
LIBUTFOBJ =\
libutf/fgetrune.o\
libutf/fputrune.o\
libutf/isalnumrune.o\
libutf/isalpharune.o\
libutf/isblankrune.o\
libutf/iscntrlrune.o\
libutf/isdigitrune.o\
libutf/isgraphrune.o\
libutf/isprintrune.o\
libutf/ispunctrune.o\
libutf/isspacerune.o\
libutf/istitlerune.o\
libutf/isxdigitrune.o\
libutf/lowerrune.o\
libutf/rune.o\
libutf/runetype.o\
libutf/upperrune.o\
libutf/utf.o\
libutf/utftorunestr.o
LIBUTILOBJ =\
libutil/concat.o\
libutil/cp.o\
libutil/crypt.o\
libutil/confirm.o\
libutil/ealloc.o\
libutil/enmasse.o\
libutil/eprintf.o\
libutil/eregcomp.o\
libutil/estrtod.o\
libutil/fnck.o\
libutil/fshut.o\
libutil/getlines.o\
libutil/human.o\
libutil/linecmp.o\
libutil/md5.o\
libutil/memmem.o\
libutil/mkdirp.o\
libutil/mode.o\
libutil/parseoffset.o\
libutil/putword.o\
libutil/reallocarray.o\
libutil/recurse.o\
libutil/rm.o\
libutil/sha1.o\
libutil/sha224.o\
libutil/sha256.o\
libutil/sha384.o\
libutil/sha512.o\
libutil/sha512-224.o\
libutil/sha512-256.o\
libutil/strcasestr.o\
libutil/strlcat.o\
libutil/strlcpy.o\
libutil/strsep.o\
libutil/strnsubst.o\
libutil/strtonum.o\
libutil/unescape.o\
libutil/writeall.o
LIB = libutf.a libutil.a
BIN =\
basename\
cal\
cat\
chgrp\
chmod\
chown\
chroot\
cksum\
cmp\
cols\
comm\
cp\
cron\
cut\
date\
dd\
dirname\
du\
echo\
ed\
env\
expand\
expr\
false\
find\
flock\
fold\
getconf\
grep\
head\
hostname\
join\
kill\
link\
ln\
logger\
logname\
ls\
md5sum\
mkdir\
mkfifo\
mknod\
mktemp\
mv\
nice\
nl\
nohup\
od\
paste\
pathchk\
printenv\
printf\
pwd\
readlink\
renice\
rev\
rm\
rmdir\
sed\
seq\
setsid\
sha1sum\
sha224sum\
sha256sum\
sha384sum\
sha512sum\
sha512-224sum\
sha512-256sum\
sleep\
sort\
split\
sponge\
strings\
sync\
tail\
tar\
tee\
test\
tftp\
time\
touch\
tr\
true\
tsort\
tty\
uname\
unexpand\
uniq\
unlink\
uudecode\
uuencode\
wc\
which\
whoami\
xargs\
xinstall\
yes
OBJ = $(LIBUTFOBJ) $(LIBUTILOBJ)
all: $(BIN)
$(BIN): $(LIB)
$(OBJ) $(BIN): $(HDR)
.o:
$(CC) $(LDFLAGS) -o $@ $< $(LIB)
.c.o:
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
.c:
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< $(LIB)
libutf.a: $(LIBUTFOBJ)
$(AR) $(ARFLAGS) $@ $?
$(RANLIB) $@
libutil.a: $(LIBUTILOBJ)
$(AR) $(ARFLAGS) $@ $?
$(RANLIB) $@
getconf: getconf.h
getconf.h:
scripts/getconf.sh > $@
proto: $(BIN)
scripts/mkproto $(DESTDIR)$(PREFIX) $(DESTDIR)$(MANPREFIX) proto
install uninstall: proto
scripts/$@ proto
sbase-box-install: sbase-box proto
scripts/install proto
$(DESTDIR)$(PREFIX)/bin/sbase-box -i $(DESTDIR)$(PREFIX)/bin/
sbase-box-uninstall: sbase-box proto
$(DESTDIR)$(PREFIX)/bin/sbase-box -d $(DESTDIR)$(PREFIX)/bin/
scripts/uninstall proto
dist: clean
mkdir -p sbase
cp -R LICENSE Makefile README TODO config.mk *.c *.1 *.h libutf libutil sbase
mv sbase sbase-$(VERSION)
tar -cf sbase-$(VERSION).tar sbase-$(VERSION)
gzip sbase-$(VERSION).tar
rm -rf sbase-$(VERSION)
sbase-box: $(BIN)
scripts/mkbox
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ build/*.c $(LIB)
clean:
rm -f $(BIN) $(OBJ) $(LIB) sbase-box sbase-$(VERSION).tar.gz
rm -f getconf.h
rm -f proto
rm -rf build
.PHONY: all install uninstall dist sbase-box-install sbase-box-uninstall clean
+147
View File
@@ -0,0 +1,147 @@
sbase - suckless unix tools
===========================
sbase is a collection of unix tools that are inherently portable across
UNIX and UNIX-like systems.
The complement of sbase is ubase[1] which is Linux-specific and provides
all the non-portable tools. Together they are intended to form a base
system similar to busybox but much smaller and suckless.
Building
--------
To build sbase, simply type make. You may have to fiddle with config.mk
depending on your system.
You can also build sbase-box, which generates a single binary containing
all the required tools. You can then symlink the individual tools to
sbase-box or run: make sbase-box-install
To run the tools for sbase-box directly use: sbase-box cmd [args]
Ideally you will want to statically link sbase. If you are on Linux we
recommend using musl-libc[2].
Portability
-----------
sbase has been compiled on a variety of different operating systems,
including Linux, *BSD, OSX, Haiku, Solaris, SCO OpenServer and others.
Various combinations of operating systems and architectures have also
been built.
You can build sbase with gcc, clang, tcc, nwcc and pcc.
Status
------
The following tools are implemented:
'#' -> UTF-8 support, '=' -> Implicit UTF-8 support, '*' -> Finished,
'|' -> Audited, 'o' -> POSIX 2013 compliant, 'x' -> Non-POSIX,
'0' -> NUL handling, '()' -> Petty flag
UTILITY MISSING
------- -------
0=*|o basename .
0=*|o cal .
0=*|o cat .
0=*|o chgrp .
0=*|o chmod .
0=*|o chown .
0=*|x chroot .
0=*|o cksum .
0=*|o cmp .
0#*|x cols .
0=*|o comm .
0=*|o cp .
0=*|x cron .
0#*|o cut .
0=*|o date .
0=*|o dd .
0=*|o dirname .
0=*|o du .
0=*|o echo .
o ed .
0=*|o env .
0#*|o expand .
0#*|o expr .
0=*|o false .
0= find .
0=* x flock .
0#*|o fold .
0=*|o getconf (-v)
=*|o grep .
0=*|o head .
0=*|x hostname .
0=*|x install .
0=* o join .
0=*|o kill .
0=*|o link .
0=*|o ln .
0=*|o logger .
0=*|o logname .
0#* o ls (-C, -k, -m, -p, -s, -x)
0=*|x md5sum .
0=*|o mkdir .
0=*|o mkfifo .
0=*|x mknod .
0=*|x mktemp .
0=*|o mv (-i)
0=*|o nice .
0#*|o nl .
0=*|o nohup .
0=*|o od .
#*|o paste .
0#* o pathchk .
0=*|x printenv .
0#*|o printf .
0=*|o pwd .
0=*|x readlink .
0=*|o renice .
0#* x rev .
0=*|o rm .
0=*|o rmdir .
# sed .
0=*|x seq .
0=*|x setsid .
0=*|x sha1sum .
0=* x sha224sum .
0=*|x sha256sum .
0=* x sha384sum .
0=*|x sha512sum .
0=* x sha512-224sum .
0=* x sha512-256sum .
0=*|o sleep .
0#*|o sort .
0=*|o split .
0=*|x sponge .
0#*|o strings .
0=*|x sync .
0=*|o tail .
0=*|x tar .
0=*|o tee .
0=*|o test .
0=*|x tftp .
0=*|o time .
0=*|o touch .
0#*|o tr .
0=*|o true .
0=* o tsort .
0=*|o tty .
0=*|o uname .
0#*|o unexpand .
0=*|o uniq .
0=*|o unlink .
0=*|o uudecode .
0=*|o uuencode .
0#*|o wc .
0=*|x which .
0=*|x whoami .
0=*|o xargs .
0=*|x yes .
[1] http://git.suckless.org/ubase/
[2] http://www.musl-libc.org/
+92
View File
@@ -0,0 +1,92 @@
The following list of commands is taken from the toybox roadmap[0] and
has been stripped down accordingly. Commands that belong to ubase[1]
are not listed here as well as commands that fall outside the scope of
sbase such as vi and sh are also not listed here.
at
awk
bc
diff
patch
stty
If you are looking for some work to do on sbase, another option is to
pick a utility from the list in the README which has missing flags or
features noted.
What also needs to be implemented is the capability of the tools to
handle data with NUL-bytes in it.
The return values of mdcheckline() in crypt.c need to be fixed (0 -> success,
1 -> error).
[0] http://landley.net/toybox/roadmap.html
[1] http://git.suckless.org/ubase/
Bugs
====
ed
--
* cat <<EOF | ed
i
LLL
.
s/$/\\
* cat <<EOF | ed
0a
int radix = 16;
int Pflag;
int Aflag;
int vflag;
int gflag;
int uflag;
int arflag;
.
?radix?;/^$/-s/^/static /
* cat <<EOF | ed
0a
Line
.
s# *##
* cat <<EOF | ed
0a
line
.
1g/^$/p
* cat <<EOF | ed
0a
line1
line2
line3
.
g/^$/d
,p
* Editing huge files doesn't work well.
printf
------
* Flags for string conversion-specifier (%s) are not supported.
* Escape sequences that expand to '%' are treated as beginning of
conversion specification.
* An trailing '%' at the end of a format string causes a read past
the end of the string.
tr
--
* When a character class is present, all other characters in the
string are ignored.
sbase-box
---------
* List of commands does not contain `install` (only `xinstall`).
xargs
-----
* Add -L.
+65
View File
@@ -0,0 +1,65 @@
/*
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][0] == '-'\
&& argv[0][1];\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
for (brk_ = 0, argv[0]++, argv_ = argv;\
argv[0][0] && !brk_;\
argv[0]++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][0];\
switch (argc_)
/* Handles obsolete -NUM syntax */
#define ARGNUM case '0':\
case '1':\
case '2':\
case '3':\
case '4':\
case '5':\
case '6':\
case '7':\
case '8':\
case '9'
#define ARGEND }\
}
#define ARGC() argc_
#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX))
#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define LNGARG() &argv[0][0]
#endif
+22
View File
@@ -0,0 +1,22 @@
.Dd October 8, 2015
.Dt BASENAME 1
.Os sbase
.Sh NAME
.Nm basename
.Nd strip leading directory components of a path
.Sh SYNOPSIS
.Nm
.Ar path
.Op Ar suffix
.Sh DESCRIPTION
.Nm
writes
.Ar path
without leading directory components and
.Ar suffix
to stdout.
.Sh SEE ALSO
.Xr dirname 1 ,
.Xr basename 3
.Sh STANDARDS
POSIX.1-2013.
+37
View File
@@ -0,0 +1,37 @@
/* See LICENSE file for copyright and license details. */
#include <libgen.h>
#include <stdio.h>
#include <string.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s path [suffix]\n", argv0);
}
int
main(int argc, char *argv[])
{
ssize_t off;
char *p;
ARGBEGIN {
default:
usage();
} ARGEND
if (argc != 1 && argc != 2)
usage();
p = basename(argv[0]);
if (argc == 2) {
off = strlen(p) - strlen(argv[1]);
if (off > 0 && !strcmp(p + off, argv[1]))
p[off] = '\0';
}
puts(p);
return fshut(stdout, "<stdout>");
}
+68
View File
@@ -0,0 +1,68 @@
.Dd October 8, 2015
.Dt CAL 1
.Os sbase
.Sh NAME
.Nm cal
.Nd show calendar
.Sh SYNOPSIS
.Nm
.Op Fl 1 | Fl 3 | Fl y | Fl n Ar num
.Op Fl s | Fl m | Fl f Ar num
.Op Fl c Ar num
.Oo Oo Ar month Oc Ar year Oc
.Sh DESCRIPTION
.Nm
writes a calendar of
.Ar month
and
.Ar year
or the current month to stdout.
If
.Ar year
is given without
.Ar month ,
.Nm
writes a 3-column calendar of the whole
year to stdout.
The date formatting is according to
.Xr localtime 3 .
.Pp
The Julian calendar is used until Sep 2, 1752.
The Gregorian calendar is used starting the next day on Sep 14, 1752.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl 1
Print current month.
This is the default.
.It Fl 3
Print previous, current and next month.
.It Fl c Ar num
Print
.Ar num
calendars in a row.
The default is 3.
.It Fl f Ar num
Set
.Ar num
(0 is Sunday, 6 is Saturday) as first day of week.
.It Fl m
Set Monday as first day of week.
.It Fl n Ar num
Output
.Ar num
months starting from and including the current month.
.It Fl s
Set Sunday as first day of week.
.It Fl y
Print the entire
.Ar year
or current year.
.El
.Sh SEE ALSO
.Xr localtime 3
.Sh STANDARDS
POSIX.1-2013.
.Pp
The flags
.Op Fl 13cfmnsy
are an extension to that specification.
+226
View File
@@ -0,0 +1,226 @@
/* See LICENSE file for copyright and license details. */
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "util.h"
enum { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
enum caltype { JULIAN, GREGORIAN };
enum { TRANS_YEAR = 1752, TRANS_MONTH = SEP, TRANS_DAY = 2 };
static struct tm *ltime;
static int
isleap(size_t year, enum caltype cal)
{
if (cal == GREGORIAN) {
if (year % 400 == 0)
return 1;
if (year % 100 == 0)
return 0;
return (year % 4 == 0);
}
else { /* cal == Julian */
return (year % 4 == 0);
}
}
static int
monthlength(size_t year, int month, enum caltype cal)
{
int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
return (month == FEB && isleap(year, cal)) ? 29 : mdays[month];
}
/* From http://www.tondering.dk/claus/cal/chrweek.php#calcdow */
static int
dayofweek(size_t year, int month, int dom, enum caltype cal)
{
size_t y;
int m, a;
a = (13 - month) / 12;
y = year - a;
m = month + 12 * a - 1;
if (cal == GREGORIAN)
return (dom + y + y / 4 - y / 100 + y / 400 + (31 * m) / 12) % 7;
else /* cal == Julian */
return (5 + dom + y + y / 4 + (31 * m) / 12) % 7;
}
static void
printgrid(size_t year, int month, int fday, int line)
{
enum caltype cal;
int offset, dom, d = 0, trans; /* are we in the transition from Julian to Gregorian? */
int today = 0;
cal = (year < TRANS_YEAR || (year == TRANS_YEAR && month <= TRANS_MONTH)) ? JULIAN : GREGORIAN;
trans = (year == TRANS_YEAR && month == TRANS_MONTH);
offset = dayofweek(year, month, 1, cal) - fday;
if (offset < 0)
offset += 7;
if (line == 1) {
for (; d < offset; ++d)
printf(" ");
dom = 1;
} else {
dom = 8 - offset + (line - 2) * 7;
if (trans && !(line == 2 && fday == 3))
dom += 11;
}
if (ltime && year == ltime->tm_year + 1900 && month == ltime->tm_mon)
today = ltime->tm_mday;
for (; d < 7 && dom <= monthlength(year, month, cal); ++d, ++dom) {
if (dom == today)
printf("\x1b[7m%2d\x1b[0m ", dom); /* highlight today's date */
else
printf("%2d ", dom);
if (trans && dom == TRANS_DAY)
dom += 11;
}
for (; d < 7; ++d)
printf(" ");
}
static void
drawcal(size_t year, int month, size_t ncols, size_t nmons, int fday)
{
char *smon[] = { "January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December" };
char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", };
size_t m, n, col, cur_year, cur_month, dow;
int line, pad;
char month_year[sizeof("Su Mo Tu We Th Fr Sa")];
for (m = 0; m < nmons; ) {
n = m;
for (col = 0; m < nmons && col < ncols; ++col, ++m) {
cur_year = year + m / 12;
cur_month = month + m % 12;
if (cur_month > 11) {
cur_month -= 12;
cur_year += 1;
}
snprintf(month_year, sizeof(month_year), "%s %zu", smon[cur_month], cur_year);
pad = sizeof(month_year) - 1 - strlen(month_year);
printf("%*s%s%*s ", pad / 2 + pad % 2, "", month_year, pad / 2, "");
}
putchar('\n');
for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
for (dow = fday; dow < (fday + 7); ++dow)
printf("%s ", days[dow % 7]);
printf(" ");
}
putchar('\n');
for (line = 1; line <= 6; ++line) {
for (col = 0, m = n; m < nmons && col < ncols; ++col, ++m) {
cur_year = year + m / 12;
cur_month = month + m % 12;
if (cur_month > 11) {
cur_month -= 12;
cur_year += 1;
}
printgrid(cur_year, cur_month, fday, line);
printf(" ");
}
putchar('\n');
}
}
}
static void
usage(void)
{
eprintf("usage: %s [-1 | -3 | -y | -n num] "
"[-s | -m | -f num] [-c num] [[month] year]\n", argv0);
}
int
main(int argc, char *argv[])
{
time_t now;
size_t year, ncols, nmons;
int fday, month;
now = time(NULL);
ltime = localtime(&now);
year = ltime->tm_year + 1900;
month = ltime->tm_mon + 1;
fday = 0;
if (!isatty(STDOUT_FILENO))
ltime = NULL; /* don't highlight today's date */
ncols = 3;
nmons = 0;
ARGBEGIN {
case '1':
nmons = 1;
break;
case '3':
nmons = 3;
if (--month == 0) {
month = 12;
year--;
}
break;
case 'c':
ncols = estrtonum(EARGF(usage()), 0, MIN(SIZE_MAX, LLONG_MAX));
break;
case 'f':
fday = estrtonum(EARGF(usage()), 0, 6);
break;
case 'm': /* Monday */
fday = 1;
break;
case 'n':
nmons = estrtonum(EARGF(usage()), 1, MIN(SIZE_MAX, LLONG_MAX));
break;
case 's': /* Sunday */
fday = 0;
break;
case 'y':
month = 1;
nmons = 12;
break;
default:
usage();
} ARGEND
if (nmons == 0) {
if (argc == 1) {
month = 1;
nmons = 12;
} else {
nmons = 1;
}
}
switch (argc) {
case 2:
month = estrtonum(argv[0], 1, 12);
argv++;
case 1: /* fallthrough */
year = estrtonum(argv[0], 0, INT_MAX);
break;
case 0:
break;
default:
usage();
}
drawcal(year, month - 1, ncols, nmons, fday);
return fshut(stdout, "<stdout>");
}
+27
View File
@@ -0,0 +1,27 @@
.Dd October 8, 2015
.Dt CAT 1
.Os sbase
.Sh NAME
.Nm cat
.Nd concatenate files
.Sh SYNOPSIS
.Nm
.Op Fl u
.Op Ar file ...
.Sh DESCRIPTION
.Nm
reads each
.Ar file
in sequence and writes it to stdout.
If no
.Ar file
is given
.Nm
reads from stdin.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl u
Unbuffered output.
.El
.Sh STANDARDS
POSIX.1-2013.
+52
View File
@@ -0,0 +1,52 @@
/* See LICENSE file for copyright and license details. */
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s [-u] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
int fd, ret = 0;
ARGBEGIN {
case 'u':
break;
default:
usage();
} ARGEND
if (!argc) {
if (concat(0, "<stdin>", 1, "<stdout>") < 0)
ret = 1;
} else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fd = 0;
} else if ((fd = open(*argv, O_RDONLY)) < 0) {
weprintf("open %s:", *argv);
ret = 1;
continue;
}
switch (concat(fd, *argv, 1, "<stdout>")) {
case -1:
ret = 1;
break;
case -2:
return 1; /* exit on write error */
}
if (fd != 0)
close(fd);
}
}
return ret;
}
+47
View File
@@ -0,0 +1,47 @@
.Dd October 8, 2015
.Dt CHGRP 1
.Os sbase
.Sh NAME
.Nm chgrp
.Nd change file group ownership
.Sh SYNOPSIS
.Nm
.Op Fl h
.Oo
.Fl R
.Op Fl H | L | P
.Oc
.Ar group
.Ar file ...
.Sh DESCRIPTION
.Nm
sets the group id of each
.Ar file
to the gid of
.Ar group .
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl h
Preserve
.Ar file
if it is a symbolic link.
.It Fl R
Change file group ownerships recursively.
.It Fl H
Dereference
.Ar file
if it is a symbolic link.
.It Fl L
Dereference all symbolic links.
.It Fl P
Preserve symbolic links.
This is the default.
.El
.Sh SEE ALSO
.Xr chmod 1 ,
.Xr chown 1 ,
.Xr chmod 2 ,
.Xr chown 2 ,
.Xr getgrnam 3
.Sh STANDARDS
POSIX.1-2013.
+75
View File
@@ -0,0 +1,75 @@
/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <unistd.h>
#include "fs.h"
#include "util.h"
static int hflag = 0;
static gid_t gid = -1;
static int ret = 0;
static void
chgrp(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
{
int flags = 0;
if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth) || (hflag && !(r->depth)))
flags |= AT_SYMLINK_NOFOLLOW;
if (fchownat(dirfd, name, -1, gid, flags) < 0) {
weprintf("chown %s:", r->path);
ret = 1;
} else if (S_ISDIR(st->st_mode)) {
recurse(dirfd, name, NULL, r);
}
}
static void
usage(void)
{
eprintf("usage: %s [-h] [-R [-H | -L | -P]] group file ...\n", argv0);
}
int
main(int argc, char *argv[])
{
struct group *gr;
struct recursor r = { .fn = chgrp, .maxdepth = 1, .follow = 'P' };
ARGBEGIN {
case 'h':
hflag = 1;
break;
case 'R':
r.maxdepth = 0;
break;
case 'H':
case 'L':
case 'P':
r.follow = ARGC();
break;
default:
usage();
} ARGEND
if (argc < 2)
usage();
errno = 0;
if ((gr = getgrnam(argv[0]))) {
gid = gr->gr_gid;
} else {
if (errno)
eprintf("getgrnam %s:", argv[0]);
gid = estrtonum(argv[0], 0, UINT_MAX);
}
for (argc--, argv++; *argv; argc--, argv++)
recurse(AT_FDCWD, *argv, NULL, &r);
return ret || recurse_status;
}
+74
View File
@@ -0,0 +1,74 @@
.Dd December 21, 2019
.Dt CHMOD 1
.Os sbase
.Sh NAME
.Nm chmod
.Nd change file modes
.Sh SYNOPSIS
.Nm
.Op Fl R
.Ar mode
.Ar file ...
.Sh DESCRIPTION
.Nm
changes the file mode of each
.Ar file
to
.Ar mode .
.Pp
If
.Ar mode
is
.Em octal
"[sog]e"
.Bl -tag -width Ds
.It s
.Xr sticky 1 => s += 1
.Pp
.Xr setgid 2 => s += 2
.Pp
.Xr setuid 4 => s += 4
.It o|g|e
owner | group | everyone
.Pp
.Xr execute 1 => o|g|e += 1
.Pp
.Xr write 2 => o|g|e += 2
.Pp
.Xr read 4 => o|g|e += 4
.El
.Pp
Leading zeroes may be omitted.
.Pp
If
.Ar mode
is
.Em symbolic
"[ugoa]*[+-=][rwxXst]*"
.Bl -tag -width Ds
.It u|g|o|a
owner | group | other (non-group) | everyone
.It +|-|=
add | remove | set
.It r|w|x|s|t
read | write | execute | setuid and setgid | sticky
.It X
execute, if directory or at least one execute bit is already set
.El
.Pp
Symbolic links are followed if they are passed as operands, and ignored
if they are encountered during directory traversal.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl R
Change modes recursively.
.El
.Sh SEE ALSO
.Xr chgrp 1 ,
.Xr umask 1
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl HLP
flags are an extension to that specification.
+77
View File
@@ -0,0 +1,77 @@
/* See LICENSE file for copyright and license details. */
#include <fcntl.h>
#include <sys/stat.h>
#include "fs.h"
#include "util.h"
static char *modestr = "";
static mode_t mask = 0;
static int ret = 0;
static void
chmodr(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
{
mode_t m;
m = parsemode(modestr, st->st_mode, mask);
if (!S_ISLNK(st->st_mode) && fchmodat(dirfd, name, m, 0) < 0) {
weprintf("chmod %s:", r->path);
ret = 1;
} else if (S_ISDIR(st->st_mode)) {
recurse(dirfd, name, NULL, r);
}
}
static void
usage(void)
{
eprintf("usage: %s [-R] mode file ...\n", argv0);
}
int
main(int argc, char *argv[])
{
struct recursor r = { .fn = chmodr, .maxdepth = 1, .follow = 'H', .flags = DIRFIRST };
size_t i;
argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
for (; *argv && (*argv)[0] == '-'; argc--, argv++) {
if (!(*argv)[1])
usage();
for (i = 1; (*argv)[i]; i++) {
switch ((*argv)[i]) {
case 'R':
r.maxdepth = 0;
break;
case 'r': case 'w': case 'x': case 'X': case 's': case 't':
/* -[rwxXst] are valid modes, so we're done */
if (i == 1)
goto done;
/* fallthrough */
case '-':
/* -- terminator */
if (i == 1 && !(*argv)[i + 1]) {
argv++;
argc--;
goto done;
}
/* fallthrough */
default:
usage();
}
}
}
done:
mask = getumask();
modestr = *argv;
if (argc < 2)
usage();
for (--argc, ++argv; *argv; argc--, argv++)
recurse(AT_FDCWD, *argv, NULL, &r);
return ret || recurse_status;
}
+57
View File
@@ -0,0 +1,57 @@
.Dd October 8, 2015
.Dt CHOWN 1
.Os sbase
.Sh NAME
.Nm chown
.Nd change file ownership
.Sh SYNOPSIS
.Nm
.Op Fl h
.Oo
.Fl R
.Op Fl H | L | P
.Oc
.Ar owner Ns Op Pf : Op Ar group
.Op Ar file ...
.Nm
.Op Fl h
.Oo
.Fl R
.Op Fl H | L | P
.Oc
.Pf : Ar group
.Op Ar file ...
.Sh DESCRIPTION
.Nm
sets the user and/or group id of each
.Ar file
to the uid of
.Ar owner
and/or the gid of
.Ar group
respectively.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl h
Preserve
.Ar file
if it is a symbolic link.
.It Fl R
Change file ownerships recursively.
.It Fl H
Dereference
.Ar file
if it is a symbolic link.
.It Fl L
Dereference all symbolic links.
.It Fl P
Preserve symbolic links.
This is the default.
.El
.Sh SEE ALSO
.Xr chmod 1 ,
.Xr chown 2 ,
.Xr getgrnam 3 ,
.Xr getpwnam 3
.Sh STANDARDS
POSIX.1-2013.
+104
View File
@@ -0,0 +1,104 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fs.h"
#include "util.h"
static int hflag = 0;
static uid_t uid = -1;
static gid_t gid = -1;
static int ret = 0;
static void
chownpwgr(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r)
{
int flags = 0;
if ((r->maxdepth == 0 && r->follow == 'P') || (r->follow == 'H' && r->depth) || (hflag && !(r->depth)))
flags |= AT_SYMLINK_NOFOLLOW;
if (fchownat(dirfd, name, uid, gid, flags) < 0) {
weprintf("chown %s:", r->path);
ret = 1;
} else if (S_ISDIR(st->st_mode)) {
recurse(dirfd, name, NULL, r);
}
}
static void
usage(void)
{
eprintf("usage: %s [-h] [-R [-H | -L | -P]] owner[:[group]] file ...\n"
" %s [-h] [-R [-H | -L | -P]] :group file ...\n",
argv0, argv0);
}
int
main(int argc, char *argv[])
{
struct group *gr;
struct passwd *pw;
struct recursor r = { .fn = chownpwgr, .maxdepth = 1, .follow = 'P' };
char *owner, *group;
ARGBEGIN {
case 'h':
hflag = 1;
break;
case 'r':
case 'R':
r.maxdepth = 0;
break;
case 'H':
case 'L':
case 'P':
r.follow = ARGC();
break;
default:
usage();
} ARGEND
if (argc < 2)
usage();
owner = argv[0];
if ((group = strchr(owner, ':')))
*group++ = '\0';
if (owner && *owner) {
errno = 0;
pw = getpwnam(owner);
if (pw) {
uid = pw->pw_uid;
} else {
if (errno)
eprintf("getpwnam %s:", owner);
uid = estrtonum(owner, 0, UINT_MAX);
}
}
if (group && *group) {
errno = 0;
gr = getgrnam(group);
if (gr) {
gid = gr->gr_gid;
} else {
if (errno)
eprintf("getgrnam %s:", group);
gid = estrtonum(group, 0, UINT_MAX);
}
}
if (uid == (uid_t)-1 && gid == (gid_t)-1)
usage();
for (argc--, argv++; *argv; argc--, argv++)
recurse(AT_FDCWD, *argv, NULL, &r);
return ret || recurse_status;
}
+25
View File
@@ -0,0 +1,25 @@
.Dd October 8, 2015
.Dt CHROOT 1
.Os sbase
.Sh NAME
.Nm chroot
.Nd run a command or shell with a different root directory
.Sh SYNOPSIS
.Nm
.Ar dir
.Op Ar cmd Op Ar arg ...
.Sh DESCRIPTION
.Nm
runs
.Ar cmd
after changing the root directory to
.Ar dir
with the
.Xr chroot 2
system call and after changing the working directory to the new root.
If
.Ar cmd
is not specified, an interactive shell is started in the new root.
.Sh SEE ALSO
.Xr chdir 2 ,
.Xr chroot 2
+49
View File
@@ -0,0 +1,49 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s dir [cmd [arg ...]]\n", argv0);
}
int
main(int argc, char *argv[])
{
char *shell[] = { "/bin/sh", "-i", NULL }, *aux, *cmd;
int savederrno;
ARGBEGIN {
default:
usage();
} ARGEND
if (!argc)
usage();
if ((aux = getenv("SHELL")))
shell[0] = aux;
if (chroot(argv[0]) < 0)
eprintf("chroot %s:", argv[0]);
if (chdir("/") < 0)
eprintf("chdir:");
if (argc == 1) {
cmd = *shell;
execvp(cmd, shell);
} else {
cmd = argv[1];
execvp(cmd, argv + 1);
}
savederrno = errno;
weprintf("execvp %s:", cmd);
_exit(126 + (savederrno == ENOENT));
}
+24
View File
@@ -0,0 +1,24 @@
.Dd October 8, 2015
.Dt CKSUM 1
.Os sbase
.Sh NAME
.Nm cksum
.Nd compute file checksum
.Sh SYNOPSIS
.Nm
.Op Ar file ...
.Sh DESCRIPTION
.Nm
calculates a cyclic redundancy check (CRC) of
.Ar file
according to
.St -iso8802-3
and writes it, the file size in bytes and path to stdout.
.Pp
If no
.Ar file
is given,
.Nm
reads from stdin.
.Sh STANDARDS
POSIX.1-2013.
+132
View File
@@ -0,0 +1,132 @@
/* See LICENSE file for copyright and license details. */
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static int ret = 0;
static const unsigned long crctab[] = { 0x00000000,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
static void
cksum(int fd, const char *s)
{
ssize_t n;
size_t len = 0, i;
uint32_t ck = 0;
unsigned char buf[BUFSIZ];
while ((n = read(fd, buf, sizeof(buf))) > 0) {
for (i = 0; i < n; i++)
ck = (ck << 8) ^ crctab[(ck >> 24) ^ buf[i]];
len += n;
}
if (n < 0) {
weprintf("read %s:", s ? s : "<stdin>");
ret = 1;
return;
}
for (i = len; i; i >>= 8)
ck = (ck << 8) ^ crctab[(ck >> 24) ^ (i & 0xFF)];
printf("%"PRIu32" %zu", ~ck, len);
if (s) {
putchar(' ');
fputs(s, stdout);
}
putchar('\n');
}
static void
usage(void)
{
eprintf("usage: %s [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
int fd;
ARGBEGIN {
default:
usage();
} ARGEND
if (!argc) {
cksum(0, NULL);
} else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fd = 0;
} else if ((fd = open(*argv, O_RDONLY)) < 0) {
weprintf("open %s:", *argv);
ret = 1;
continue;
}
cksum(fd, *argv);
if (fd != 0)
close(fd);
}
}
ret |= fshut(stdout, "<stdout>");
return ret;
}
+49
View File
@@ -0,0 +1,49 @@
.Dd October 8, 2015
.Dt CMP 1
.Os sbase
.Sh NAME
.Nm cmp
.Nd compare two files
.Sh SYNOPSIS
.Nm
.Op Fl l | Fl s
.Ar file1 file2
.Sh DESCRIPTION
.Nm
compares
.Ar file1
and
.Ar file2
byte by byte.
If they differ,
.Nm
writes the first differing byte- and line-number to stdout.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl l
Print byte-number and bytes (in octal) for each difference.
.It Fl s
Print nothing and only return status.
.El
.Sh EXIT STATUS
.Bl -tag -width Ds
.It 0
.Ar file1
and
.Ar file2
are identical.
.It 1
.Ar file1
and
.Ar file2
are different.
.It > 1
An error occurred.
.El
.Sh SEE ALSO
.Xr comm 1 ,
.Xr diff 1
.Sh STANDARDS
POSIX.1-2013.
.Pp
The "char" in the default result format has been replaced with "byte".
+82
View File
@@ -0,0 +1,82 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
static void
usage(void)
{
enprintf(2, "usage: %s [-l | -s] file1 file2\n", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp[2];
size_t line = 1, n;
int ret = 0, lflag = 0, sflag = 0, same = 1, b[2];
ARGBEGIN {
case 'l':
lflag = 1;
break;
case 's':
sflag = 1;
break;
default:
usage();
} ARGEND
if (argc != 2 || (lflag && sflag))
usage();
for (n = 0; n < 2; n++) {
if (!strcmp(argv[n], "-")) {
argv[n] = "<stdin>";
fp[n] = stdin;
} else {
if (!(fp[n] = fopen(argv[n], "r"))) {
if (!sflag)
weprintf("fopen %s:", argv[n]);
return 2;
}
}
}
for (n = 1; ; n++) {
b[0] = getc(fp[0]);
b[1] = getc(fp[1]);
if (b[0] == b[1]) {
if (b[0] == EOF)
break;
else if (b[0] == '\n')
line++;
continue;
} else if (b[0] == EOF || b[1] == EOF) {
if (!sflag)
weprintf("EOF on %s\n", argv[(b[0] != EOF)]);
same = 0;
break;
} else if (!lflag) {
if (!sflag)
printf("%s %s differ: byte %zu, line %zu\n",
argv[0], argv[1], n, line);
same = 0;
break;
} else {
printf("%zu %o %o\n", n, b[0], b[1]);
same = 0;
}
}
if (!ret)
ret = !same;
if (fshut(fp[0], argv[0]) | (fp[0] != fp[1] && fshut(fp[1], argv[1])) |
fshut(stdout, "<stdout>"))
ret = 2;
return ret;
}
+56
View File
@@ -0,0 +1,56 @@
.Dd October 8, 2015
.Dt COLS 1
.Os sbase
.Sh NAME
.Nm cols
.Nd columnize output
.Sh SYNOPSIS
.Nm
.Op Fl c Ar num
.Op Ar file ...
.Sh DESCRIPTION
.Nm
reads each
.Ar file
in sequence and writes them to stdout, in as many vertical
columns as will fit in
.Ar num
character columns.
If no
.Ar file
is given,
.Nm
reads from stdin.
.Pp
By default
.Nm cols
tries to figure out the width of the output device.
If that fails, it defaults to 65 chars.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl c Ar num
Set maximum number of character columns to
.Ar num ,
unless input lines exceed this limit.
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It COLUMNS
The width of the output device.
.El
.Sh HISTORY
.Nm
is similar to
.Xr mc 1
in Plan 9. It was renamed to
.Nm
to avoid the name collision with the popular file manager
Midnight Commander.
.Sh CAVEATS
This implementation of
.Nm
assumes that each UTF-8 code point occupies one character cell,
and thus mishandles TAB characters (among others).
.Pp
.Nm
currently mangles files which contain embedded NULs.
+98
View File
@@ -0,0 +1,98 @@
/* See LICENSE file for copyright and license details. */
#include <sys/ioctl.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "text.h"
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s [-c num] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp;
struct winsize w;
struct linebuf b = EMPTY_LINEBUF;
size_t chars = 65, maxlen = 0, i, j, k, len, cols, rows;
int cflag = 0, ret = 0;
char *p;
ARGBEGIN {
case 'c':
cflag = 1;
chars = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
break;
default:
usage();
} ARGEND
if (!cflag) {
if ((p = getenv("COLUMNS")))
chars = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
else if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) && w.ws_col > 0)
chars = w.ws_col;
}
if (!argc) {
getlines(stdin, &b);
} else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fp = stdin;
} else if (!(fp = fopen(*argv, "r"))) {
weprintf("fopen %s:", *argv);
ret = 1;
continue;
}
getlines(fp, &b);
if (fp != stdin && fshut(fp, *argv))
ret = 1;
}
}
for (i = 0; i < b.nlines; i++) {
for (j = 0, len = 0; j < b.lines[i].len; j++) {
if (UTF8_POINT(b.lines[i].data[j]))
len++;
}
if (len && b.lines[i].data[b.lines[i].len - 1] == '\n') {
b.lines[i].data[--(b.lines[i].len)] = '\0';
len--;
}
if (len > maxlen)
maxlen = len;
}
for (cols = 1; (cols + 1) * maxlen + cols <= chars; cols++);
rows = b.nlines / cols + (b.nlines % cols > 0);
for (i = 0; i < rows; i++) {
for (j = 0; j < cols && i + j * rows < b.nlines; j++) {
for (k = 0, len = 0; k < b.lines[i + j * rows].len; k++) {
if (UTF8_POINT(b.lines[i + j * rows].data[k]))
len++;
}
fwrite(b.lines[i + j * rows].data, 1,
b.lines[i + j * rows].len, stdout);
if (j < cols - 1)
for (k = len; k < maxlen + 1; k++)
putchar(' ');
}
putchar('\n');
}
ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
return ret;
}
+40
View File
@@ -0,0 +1,40 @@
.Dd October 8, 2015
.Dt COMM 1
.Os sbase
.Sh NAME
.Nm comm
.Nd select or reject lines common to two files
.Sh SYNOPSIS
.Nm
.Op Fl 123
.Ar file1
.Ar file2
.Sh DESCRIPTION
.Nm
reads
.Ar file1
and
.Ar file2 ,
which should both be sorted lexically, and writes three text columns
to stdout:
.Bl -tag -width Ds
.It 1
Lines only in
.Ar file1 .
.It 2
Lines only in
.Ar file2 .
.It 3
Common lines.
.El
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl 1 | Fl 2 | Fl 3
Suppress column 1 | 2 | 3
.El
.Sh SEE ALSO
.Xr cmp 1 ,
.Xr sort 1 ,
.Xr uniq 1
.Sh STANDARDS
POSIX.1-2013.
+97
View File
@@ -0,0 +1,97 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "text.h"
#include "util.h"
static int show = 0x07;
static void
printline(int pos, struct line *line)
{
int i;
if (!(show & (0x1 << pos)))
return;
for (i = 0; i < pos; i++) {
if (show & (0x1 << i))
putchar('\t');
}
fwrite(line->data, 1, line->len, stdout);
}
static void
usage(void)
{
eprintf("usage: %s [-123] file1 file2\n", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp[2];
static struct line line[2];
size_t linecap[2] = { 0, 0 };
ssize_t len;
int ret = 0, i, diff = 0, seenline = 0;
ARGBEGIN {
case '1':
case '2':
case '3':
show &= 0x07 ^ (1 << (ARGC() - '1'));
break;
default:
usage();
} ARGEND
if (argc != 2)
usage();
for (i = 0; i < 2; i++) {
if (!strcmp(argv[i], "-")) {
argv[i] = "<stdin>";
fp[i] = stdin;
} else if (!(fp[i] = fopen(argv[i], "r"))) {
eprintf("fopen %s:", argv[i]);
}
}
for (;;) {
for (i = 0; i < 2; i++) {
if (diff && i == (diff < 0))
continue;
if ((len = getline(&(line[i].data), &linecap[i],
fp[i])) > 0) {
line[i].len = len;
seenline = 1;
continue;
}
if (ferror(fp[i]))
eprintf("getline %s:", argv[i]);
if ((diff || seenline) && line[!i].data[0])
printline(!i, &line[!i]);
while ((len = getline(&(line[!i].data), &linecap[!i],
fp[!i])) > 0) {
line[!i].len = len;
printline(!i, &line[!i]);
}
if (ferror(fp[!i]))
eprintf("getline %s:", argv[!i]);
goto end;
}
diff = linecmp(&line[0], &line[1]);
LIMIT(diff, -1, 1);
seenline = 0;
printline((2 - diff) % 3, &line[MAX(0, diff)]);
}
end:
ret |= fshut(fp[0], argv[0]);
ret |= (fp[0] != fp[1]) && fshut(fp[1], argv[1]);
ret |= fshut(stdout, "<stdout>");
return ret;
}
+6
View File
@@ -0,0 +1,6 @@
/* See LICENSE file for copyright and license details. */
#include <limits.h>
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
#endif
+15
View File
@@ -0,0 +1,15 @@
# sbase version
VERSION = 0.1
# paths
PREFIX = /usr/local
MANPREFIX = $(PREFIX)/share/man
# tools
#CC =
#AR =
RANLIB = ranlib
# -lrt might be needed on some systems
# CFLAGS =
# LDFLAGS =
+71
View File
@@ -0,0 +1,71 @@
.Dd April 22, 2025
.Dt CP 1
.Os sbase
.Sh NAME
.Nm cp
.Nd copy files and directories
.Sh SYNOPSIS
.Nm
.Op Fl afipv
.Oo
.Fl R
.Op Fl H | L | P
.Oc
.Ar source ...
.Ar dest
.Sh DESCRIPTION
.Nm
copies
.Ar source
to
.Ar dest .
If more than one
.Ar source
is given
.Ar dest
has to be a directory.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl a
Preserve block devices, character devices, sockets and FIFOs.
Implies
.Fl p ,
.Fl P
and
.Fl R .
.It Fl f
If an existing
.Ar dest
cannot be opened, remove it and try again.
.It Fl i
Interactive prompt before overwrite.
.It Fl p
Preserve mode, timestamp and permissions.
.It Fl v
Write "'source' -> 'dest'" for each
.Ar source
to stdout.
.It Fl H
Dereference
.Ar source
if it is a symbolic link.
.It Fl L
Dereference all symbolic links.
This is the default without
.Fl R .
.It Fl P
Preserve symbolic links.
This is the default with
.Fl R .
.It Fl R
Traverse directories recursively.
If this flag is not specified, directories are not copied.
.El
.Sh SEE ALSO
.Xr mv 1
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl av
flags are an extension to that specification.
+63
View File
@@ -0,0 +1,63 @@
/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include "fs.h"
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s [-afipv] [-R [-H | -L | -P]] source ... dest\n", argv0);
}
int
main(int argc, char *argv[])
{
struct stat st;
ARGBEGIN {
case 'i':
cp_iflag = 1;
break;
case 'a':
cp_follow = 'P';
cp_aflag = cp_pflag = cp_rflag = 1;
break;
case 'f':
cp_fflag = 1;
break;
case 'p':
cp_pflag = 1;
break;
case 'r':
case 'R':
cp_rflag = 1;
break;
case 'v':
cp_vflag = 1;
break;
case 'H':
case 'L':
case 'P':
cp_follow = ARGC();
break;
default:
usage();
} ARGEND
if (argc < 2)
usage();
if (!cp_follow)
cp_follow = cp_rflag ? 'P' : 'L';
if (argc > 2) {
if (stat(argv[argc - 1], &st) < 0)
eprintf("stat %s:", argv[argc - 1]);
if (!S_ISDIR(st.st_mode))
eprintf("%s: not a directory\n", argv[argc - 1]);
}
enmasse(argc, argv, cp);
return fshut(stdout, "<stdout>") || cp_status;
}
+23
View File
@@ -0,0 +1,23 @@
.Dd October 8, 2015
.Dt CRON 1
.Os sbase
.Sh NAME
.Nm cron
.Nd clock daemon
.Sh SYNOPSIS
.Nm
.Op Fl f Ar file
.Op Fl n
.Sh DESCRIPTION
.Nm
schedules commands to be run at specified dates and times.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl f Ar file
Use the specified
.Ar file
instead of the default
.Pa /etc/crontab .
.It Fl n
Do not daemonize.
.El
+566
View File
@@ -0,0 +1,566 @@
/* See LICENSE file for copyright and license details. */
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "queue.h"
#include "util.h"
struct field {
enum {
ERROR,
WILDCARD,
NUMBER,
RANGE,
REPEAT,
LIST
} type;
long *val;
int len;
};
struct ctabentry {
struct field min;
struct field hour;
struct field mday;
struct field mon;
struct field wday;
char *cmd;
TAILQ_ENTRY(ctabentry) entry;
};
struct jobentry {
char *cmd;
pid_t pid;
TAILQ_ENTRY(jobentry) entry;
};
static sig_atomic_t chldreap;
static sig_atomic_t reload;
static sig_atomic_t quit;
static TAILQ_HEAD(, ctabentry) ctabhead = TAILQ_HEAD_INITIALIZER(ctabhead);
static TAILQ_HEAD(, jobentry) jobhead = TAILQ_HEAD_INITIALIZER(jobhead);
static char *config = "/etc/crontab";
static char *pidfile = "/var/run/crond.pid";
static int nflag;
static void
loginfo(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (nflag == 0)
vsyslog(LOG_INFO, fmt, ap);
else
vfprintf(stdout, fmt, ap);
fflush(stdout);
va_end(ap);
}
static void
logwarn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (nflag == 0)
vsyslog(LOG_WARNING, fmt, ap);
else
vfprintf(stderr, fmt, ap);
va_end(ap);
}
static void
logerr(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (nflag == 0)
vsyslog(LOG_ERR, fmt, ap);
else
vfprintf(stderr, fmt, ap);
va_end(ap);
}
static void
runjob(char *cmd)
{
struct jobentry *je;
time_t t;
pid_t pid;
t = time(NULL);
/* If command is already running, skip it */
TAILQ_FOREACH(je, &jobhead, entry) {
if (strcmp(je->cmd, cmd) == 0) {
loginfo("already running %s pid: %d at %s",
je->cmd, je->pid, ctime(&t));
return;
}
}
switch ((pid = fork())) {
case -1:
logerr("error: failed to fork job: %s time: %s",
cmd, ctime(&t));
return;
case 0:
setsid();
loginfo("run: %s pid: %d at %s",
cmd, getpid(), ctime(&t));
execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
logerr("error: failed to execute job: %s time: %s",
cmd, ctime(&t));
_exit(1);
default:
je = emalloc(sizeof(*je));
je->cmd = estrdup(cmd);
je->pid = pid;
TAILQ_INSERT_TAIL(&jobhead, je, entry);
}
}
static void
waitjob(void)
{
struct jobentry *je, *tmp;
int status;
time_t t;
pid_t pid;
t = time(NULL);
while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
je = NULL;
TAILQ_FOREACH(tmp, &jobhead, entry) {
if (tmp->pid == pid) {
je = tmp;
break;
}
}
if (je) {
TAILQ_REMOVE(&jobhead, je, entry);
free(je->cmd);
free(je);
}
if (WIFEXITED(status) == 1)
loginfo("complete: pid: %d returned: %d time: %s",
pid, WEXITSTATUS(status), ctime(&t));
else if (WIFSIGNALED(status) == 1)
loginfo("complete: pid: %d terminated by signal: %s time: %s",
pid, strsignal(WTERMSIG(status)), ctime(&t));
else if (WIFSTOPPED(status) == 1)
loginfo("complete: pid: %d stopped by signal: %s time: %s",
pid, strsignal(WSTOPSIG(status)), ctime(&t));
}
}
static int
isleap(int year)
{
if (year % 400 == 0)
return 1;
if (year % 100 == 0)
return 0;
return (year % 4 == 0);
}
static int
daysinmon(int mon, int year)
{
int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (year < 1900)
year += 1900;
if (isleap(year))
days[1] = 29;
return days[mon];
}
static int
matchentry(struct ctabentry *cte, struct tm *tm)
{
struct {
struct field *f;
int tm;
int len;
} matchtbl[] = {
{ .f = &cte->min, .tm = tm->tm_min, .len = 60 },
{ .f = &cte->hour, .tm = tm->tm_hour, .len = 24 },
{ .f = &cte->mday, .tm = tm->tm_mday, .len = daysinmon(tm->tm_mon, tm->tm_year) },
{ .f = &cte->mon, .tm = tm->tm_mon, .len = 12 },
{ .f = &cte->wday, .tm = tm->tm_wday, .len = 7 },
};
size_t i;
int j;
for (i = 0; i < LEN(matchtbl); i++) {
switch (matchtbl[i].f->type) {
case WILDCARD:
continue;
case NUMBER:
if (matchtbl[i].f->val[0] == matchtbl[i].tm)
continue;
break;
case RANGE:
if (matchtbl[i].f->val[0] <= matchtbl[i].tm)
if (matchtbl[i].f->val[1] >= matchtbl[i].tm)
continue;
break;
case REPEAT:
if (matchtbl[i].tm > 0) {
if (matchtbl[i].tm % matchtbl[i].f->val[0] == 0)
continue;
} else {
if (matchtbl[i].len % matchtbl[i].f->val[0] == 0)
continue;
}
break;
case LIST:
for (j = 0; j < matchtbl[i].f->len; j++)
if (matchtbl[i].f->val[j] == matchtbl[i].tm)
break;
if (j < matchtbl[i].f->len)
continue;
break;
default:
break;
}
break;
}
if (i != LEN(matchtbl))
return 0;
return 1;
}
static int
parsefield(const char *field, long low, long high, struct field *f)
{
int i;
char *e1, *e2;
const char *p;
p = field;
while (isdigit(*p))
p++;
f->type = ERROR;
switch (*p) {
case '*':
if (strcmp(field, "*") == 0) {
f->val = NULL;
f->len = 0;
f->type = WILDCARD;
} else if (strncmp(field, "*/", 2) == 0) {
f->val = emalloc(sizeof(*f->val));
f->len = 1;
errno = 0;
f->val[0] = strtol(field + 2, &e1, 10);
if (e1[0] != '\0' || errno != 0 || f->val[0] == 0)
break;
f->type = REPEAT;
}
break;
case '\0':
f->val = emalloc(sizeof(*f->val));
f->len = 1;
errno = 0;
f->val[0] = strtol(field, &e1, 10);
if (e1[0] != '\0' || errno != 0)
break;
f->type = NUMBER;
break;
case '-':
f->val = emalloc(2 * sizeof(*f->val));
f->len = 2;
errno = 0;
f->val[0] = strtol(field, &e1, 10);
if (e1[0] != '-' || errno != 0)
break;
errno = 0;
f->val[1] = strtol(e1 + 1, &e2, 10);
if (e2[0] != '\0' || errno != 0)
break;
f->type = RANGE;
break;
case ',':
for (i = 1; isdigit(*p) || *p == ','; p++)
if (*p == ',')
i++;
f->val = emalloc(i * sizeof(*f->val));
f->len = i;
errno = 0;
f->val[0] = strtol(field, &e1, 10);
if (f->val[0] < low || f->val[0] > high)
break;
for (i = 1; *e1 == ',' && errno == 0; i++) {
errno = 0;
f->val[i] = strtol(e1 + 1, &e2, 10);
e1 = e2;
}
if (e1[0] != '\0' || errno != 0)
break;
f->type = LIST;
break;
default:
return -1;
}
for (i = 0; i < f->len; i++)
if (f->val[i] < low || f->val[i] > high)
f->type = ERROR;
if (f->type == ERROR) {
free(f->val);
return -1;
}
return 0;
}
static void
freecte(struct ctabentry *cte, int nfields)
{
switch (nfields) {
case 6:
free(cte->cmd);
case 5:
free(cte->wday.val);
case 4:
free(cte->mon.val);
case 3:
free(cte->mday.val);
case 2:
free(cte->hour.val);
case 1:
free(cte->min.val);
}
free(cte);
}
static void
unloadentries(void)
{
struct ctabentry *cte, *tmp;
for (cte = TAILQ_FIRST(&ctabhead); cte; cte = tmp) {
tmp = TAILQ_NEXT(cte, entry);
TAILQ_REMOVE(&ctabhead, cte, entry);
freecte(cte, 6);
}
}
static int
loadentries(void)
{
struct ctabentry *cte;
FILE *fp;
char *line = NULL, *p, *col;
int r = 0, y;
size_t size = 0;
ssize_t len;
struct fieldlimits {
char *name;
long min;
long max;
struct field *f;
} flim[] = {
{ "min", 0, 59, NULL },
{ "hour", 0, 23, NULL },
{ "mday", 1, 31, NULL },
{ "mon", 1, 12, NULL },
{ "wday", 0, 6, NULL }
};
size_t x;
if ((fp = fopen(config, "r")) == NULL) {
logerr("error: can't open %s: %s\n", config, strerror(errno));
return -1;
}
for (y = 0; (len = getline(&line, &size, fp)) != -1; y++) {
p = line;
if (line[0] == '#' || line[0] == '\n' || line[0] == '\0')
continue;
cte = emalloc(sizeof(*cte));
flim[0].f = &cte->min;
flim[1].f = &cte->hour;
flim[2].f = &cte->mday;
flim[3].f = &cte->mon;
flim[4].f = &cte->wday;
for (x = 0; x < LEN(flim); x++) {
do
col = strsep(&p, "\t\n ");
while (col && col[0] == '\0');
if (!col || parsefield(col, flim[x].min, flim[x].max, flim[x].f) < 0) {
logerr("error: failed to parse `%s' field on line %d\n",
flim[x].name, y + 1);
freecte(cte, x);
r = -1;
break;
}
}
if (r == -1)
break;
col = strsep(&p, "\n");
if (col)
while (col[0] == '\t' || col[0] == ' ')
col++;
if (!col || col[0] == '\0') {
logerr("error: missing `cmd' field on line %d\n",
y + 1);
freecte(cte, 5);
r = -1;
break;
}
cte->cmd = estrdup(col);
TAILQ_INSERT_TAIL(&ctabhead, cte, entry);
}
if (r < 0)
unloadentries();
free(line);
fclose(fp);
return r;
}
static void
reloadentries(void)
{
unloadentries();
if (loadentries() < 0)
logwarn("warning: discarding old crontab entries\n");
}
static void
sighandler(int sig)
{
switch (sig) {
case SIGCHLD:
chldreap = 1;
break;
case SIGHUP:
reload = 1;
break;
case SIGTERM:
quit = 1;
break;
}
}
static void
usage(void)
{
eprintf("usage: %s [-f file] [-n]\n", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp;
struct ctabentry *cte;
time_t t;
struct tm *tm;
struct sigaction sa;
ARGBEGIN {
case 'n':
nflag = 1;
break;
case 'f':
config = EARGF(usage());
break;
default:
usage();
} ARGEND
if (argc > 0)
usage();
if (nflag == 0) {
openlog(argv[0], LOG_CONS | LOG_PID, LOG_CRON);
if (daemon(1, 0) < 0) {
logerr("error: failed to daemonize %s\n", strerror(errno));
return 1;
}
if ((fp = fopen(pidfile, "w"))) {
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
sa.sa_handler = sighandler;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
loadentries();
while (1) {
t = time(NULL);
sleep(60 - t % 60);
if (quit == 1) {
if (nflag == 0)
unlink(pidfile);
unloadentries();
/* Don't wait or kill forked processes, just exit */
break;
}
if (reload == 1 || chldreap == 1) {
if (reload == 1) {
reloadentries();
reload = 0;
}
if (chldreap == 1) {
waitjob();
chldreap = 0;
}
continue;
}
TAILQ_FOREACH(cte, &ctabhead, entry) {
t = time(NULL);
tm = localtime(&t);
if (matchentry(cte, tm) == 1)
runjob(cte->cmd);
}
}
if (nflag == 0)
closelog();
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
/* See LICENSE file for copyright and license details. */
struct crypt_ops {
void (*init)(void *);
void (*update)(void *, const void *, unsigned long);
void (*sum)(void *, uint8_t *);
void *s;
};
int cryptcheck(int, char **, struct crypt_ops *, uint8_t *, size_t);
int cryptmain(int, char **, struct crypt_ops *, uint8_t *, size_t);
int cryptsum(struct crypt_ops *, int, const char *, uint8_t *);
void mdprint(const uint8_t *, const char *, size_t);
+69
View File
@@ -0,0 +1,69 @@
.Dd October 8, 2015
.Dt CUT 1
.Os sbase
.Sh NAME
.Nm cut
.Nd extract columns of data
.Sh SYNOPSIS
.Nm
.Fl b Ar list
.Op Fl n
.Op Ar file ...
.Nm
.Fl c Ar list
.Op Ar file ...
.Nm
.Fl f Ar list
.Op Fl d Ar delim
.Op Fl s
.Op Ar file ...
.Sh DESCRIPTION
.Nm
out bytes, characters or delimited fields from each line of
.Ar file
and write to stdout.
.Pp
If no
.Ar file
is given or
.Ar file
is '-',
.Nm
reads from stdin.
.Pp
.Ar list
is a comma or space separated list of numbers and ranges starting
from 1.
Ranges have the form 'N-M'. If N or M is missing, beginning or end
of line is assumed.
Numbers and ranges may be repeated, overlapping and in any order.
.Pp
Selected input is written in the same order it is read
and is written exactly once.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl b Ar list | Fl c Ar list
.Ar list
specifies byte | character positions.
.It Fl d Ar delim
Use
.Ar delim
as field delimiter, which can be an arbitrary string.
Default is '\et'.
.It Fl f Ar list
.Ar list
specifies field numbers.
Lines not containing field delimiters are passed through, unless
.Fl s
is specified.
.It Fl n
Do not split multibyte characters.
A character is written when its last byte is selected.
.It Fl s
Suppress lines not containing field delimiters.
.El
.Sh STANDARDS
POSIX.1-2013.
.Pp
The possibility of separating numbers and ranges with a space and specifying
multibyte delimiters of arbitrary length is an extension to that specification.
+215
View File
@@ -0,0 +1,215 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "text.h"
#include "utf.h"
#include "util.h"
typedef struct Range {
size_t min, max;
struct Range *next;
} Range;
static Range *list = NULL;
static char mode = 0;
static char *delim = "\t";
static size_t delimlen = 1;
static int nflag = 0;
static int sflag = 0;
static void
insert(Range *r)
{
Range *l, *p, *t;
for (p = NULL, l = list; l; p = l, l = l->next) {
if (r->max && r->max + 1 < l->min) {
r->next = l;
break;
} else if (!l->max || r->min < l->max + 2) {
l->min = MIN(r->min, l->min);
for (p = l, t = l->next; t; p = t, t = t->next)
if (r->max && r->max + 1 < t->min)
break;
l->max = (p->max && r->max) ? MAX(p->max, r->max) : 0;
l->next = t;
return;
}
}
if (p)
p->next = r;
else
list = r;
}
static void
parselist(char *str)
{
char *s;
size_t n = 1;
Range *r;
if (!*str)
eprintf("empty list\n");
for (s = str; *s; s++) {
if (*s == ' ')
*s = ',';
if (*s == ',')
n++;
}
r = ereallocarray(NULL, n, sizeof(*r));
for (s = str; n; n--, s++) {
r->min = (*s == '-') ? 1 : strtoul(s, &s, 10);
r->max = (*s == '-') ? strtoul(s + 1, &s, 10) : r->min;
r->next = NULL;
if (!r->min || (r->max && r->max < r->min) || (*s && *s != ','))
eprintf("bad list value\n");
insert(r++);
}
}
static size_t
seek(struct line *s, size_t pos, size_t *prev, size_t count)
{
size_t n = pos - *prev, i, j;
if (mode == 'b') {
if (n >= s->len)
return s->len;
if (nflag)
while (n && !UTF8_POINT(s->data[n]))
n--;
*prev += n;
return n;
} else if (mode == 'c') {
for (n++, i = 0; i < s->len; i++)
if (UTF8_POINT(s->data[i]) && !--n)
break;
} else {
for (i = (count < delimlen + 1) ? 0 : delimlen; n && i < s->len; ) {
if ((s->len - i) >= delimlen &&
!memcmp(s->data + i, delim, delimlen)) {
if (!--n && count)
break;
i += delimlen;
continue;
}
for (j = 1; j + i <= s->len && !fullrune(s->data + i, j); j++);
i += j;
}
}
*prev = pos;
return i;
}
static void
cut(FILE *fp, const char *fname)
{
Range *r;
struct line s;
static struct line line;
static size_t size;
size_t i, n, p;
ssize_t len;
while ((len = getline(&line.data, &size, fp)) > 0) {
line.len = len;
if (line.data[line.len - 1] == '\n')
line.data[--line.len] = '\0';
if (mode == 'f' && !memmem(line.data, line.len, delim, delimlen)) {
if (!sflag) {
fwrite(line.data, 1, line.len, stdout);
fputc('\n', stdout);
}
continue;
}
for (i = 0, p = 1, s = line, r = list; r; r = r->next) {
n = seek(&s, r->min, &p, i);
s.data += n;
s.len -= n;
i += (mode == 'f') ? delimlen : 1;
if (!s.len)
break;
if (!r->max) {
fwrite(s.data, 1, s.len, stdout);
break;
}
n = seek(&s, r->max + 1, &p, i);
i += (mode == 'f') ? delimlen : 1;
if (fwrite(s.data, 1, n, stdout) != n)
eprintf("fwrite <stdout>:");
s.data += n;
s.len -= n;
}
putchar('\n');
}
if (ferror(fp))
eprintf("getline %s:", fname);
}
static void
usage(void)
{
eprintf("usage: %s -b list [-n] [file ...]\n"
" %s -c list [file ...]\n"
" %s -f list [-d delim] [-s] [file ...]\n",
argv0, argv0, argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp;
int ret = 0;
ARGBEGIN {
case 'b':
case 'c':
case 'f':
mode = ARGC();
parselist(EARGF(usage()));
break;
case 'd':
delim = EARGF(usage());
if (!*delim)
eprintf("empty delimiter\n");
delimlen = unescape(delim);
break;
case 'n':
nflag = 1;
break;
case 's':
sflag = 1;
break;
default:
usage();
} ARGEND
if (!mode)
usage();
if (!argc)
cut(stdin, "<stdin>");
else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fp = stdin;
} else if (!(fp = fopen(*argv, "r"))) {
weprintf("fopen %s:", *argv);
ret = 1;
continue;
}
cut(fp, *argv);
if (fp != stdin && fshut(fp, *argv))
ret = 1;
}
}
ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
return ret;
}
+81
View File
@@ -0,0 +1,81 @@
.Dd October 8, 2015
.Dt DATE 1
.Os sbase
.Sh NAME
.Nm date
.Nd print or set date and time
.Sh SYNOPSIS
.Nm
.Op Fl d Ar time
.Op Fl u
.Oo
.Cm + Ns Ar format |
.Sm off
.Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
.Sm on
.Oc
.Sh DESCRIPTION
.Nm
prints the date and time according to
.Xr locale 7
or
.Ar format
using
.Xr strftime 3
or sets the date.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl d Ar time
Print
.Ar time
given as the number of seconds since the
Unix epoch 1970-01-01T00:00:00Z.
.It Fl u
Print or set UTC time instead of local time.
.El
.Pp
An operand with a leading plus
.Pq Cm +
sign signals a user-defined format string using
.Xr strftime 3
conversion specifications.
.Pp
An operand without a leading plus sign is interpreted as a value
for setting the system's current date and time.
The canonical representation for setting the date and time is:
.Pp
.Bl -tag -width Ds -compact -offset indent
.It Ar mm
The month of the year, from 01 to 12.
.It Ar dd
The day of the month, from 01 to 31.
.It Ar HH
The hour of the day, from 00 to 23.
.It Ar MM
The minute of the hour, from 00 to 59.
.It Ar CC
The first two digits of the year (the century).
.It Ar yy
The second two digits of the year.
If
.Ar yy
is specified, but
.Ar CC
is not, a value for
.Ar yy
between 69 and 99 results in a
.Ar CC
value of 19.
Otherwise, a
.Ar CC
value of 20 is used.
.El
.Pp
The century and year are optional.
The default is the current year.
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl d
flag is an extension to that specification.
+103
View File
@@ -0,0 +1,103 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s [-u] [-d time] [+format | mmddHHMM[[CC]yy]]\n", argv0);
}
static int
datefield(const char *s, size_t i)
{
if (!isdigit(s[i]) || !isdigit(s[i+1]))
eprintf("invalid date format: %s\n", s);
return (s[i] - '0') * 10 + (s[i+1] - '0');
}
static void
setdate(const char *s, struct tm *now)
{
struct tm date;
struct timespec ts;
switch (strlen(s)) {
case 8:
date.tm_year = now->tm_year;
break;
case 10:
date.tm_year = datefield(s, 8);
if (date.tm_year < 69)
date.tm_year += 100;
break;
case 12:
date.tm_year = ((datefield(s, 8) - 19) * 100) + datefield(s, 10);
break;
default:
eprintf("invalid date format: %s\n", s);
break;
}
date.tm_mon = datefield(s, 0) - 1;
date.tm_mday = datefield(s, 2);
date.tm_hour = datefield(s, 4);
date.tm_min = datefield(s, 6);
date.tm_sec = 0;
date.tm_isdst = -1;
ts.tv_sec = mktime(&date);
if (ts.tv_sec == -1)
eprintf("mktime:");
ts.tv_nsec = 0;
if (clock_settime(CLOCK_REALTIME, &ts) == -1)
eprintf("clock_settime:");
}
int
main(int argc, char *argv[])
{
struct tm *now;
time_t t;
char buf[BUFSIZ], *fmt = "%a %b %e %H:%M:%S %Z %Y";
t = time(NULL);
if (t == -1)
eprintf("time:");
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
break;
case 'u':
if (setenv("TZ", "UTC0", 1) < 0)
eprintf("setenv:");
break;
default:
usage();
} ARGEND
if (!(now = localtime(&t)))
eprintf("localtime:");
if (argc) {
if (argc != 1)
usage();
if (argv[0][0] != '+') {
setdate(argv[0], now);
return 0;
}
fmt = &argv[0][1];
}
strftime(buf, sizeof(buf), fmt, now);
puts(buf);
return fshut(stdout, "<stdout>");
}
+91
View File
@@ -0,0 +1,91 @@
.Dd April 28, 2020
.Dt DD 1
.Os sbase
.Sh NAME
.Nm dd
.Nd convert and copy a file
.Sh SYNOPSIS
.Nm
.Op Ar operand Ns ...
.Sh DESCRIPTION
.Nm
copies its input to its output, possibly after conversion, using
the specified block sizes,
.Pp
The following operands are available:
.Bl -tag -width ibs=expr
.It Cm if= Ns Ar file
Read from the file named by
.Ar file
instead of standard input.
.It Cm of= Ns Ar file
Write to the file named by
.Ar file
instead of standard output.
.It Cm ibs= Ns Ar expr
Set the input block size to
.Ar expr
(defaults to 512).
.It Cm obs= Ns Ar expr
Set the output block size to
.Ar expr
(defaults to 512).
.It Cm bs= Ns Ar expr
Set the input and output block sizes to
.Ar expr .
Additionally, if no conversion other than
.Cm noerror ,
.Cm notrunc ,
or
.Cm sync
is specified, input blocks are copied as single output blocks, even
when the input block is short.
.It Cm skip= Ns Ar n
Skip
.Ar n
input blocks before starting to copy.
.It Cm seek= Ns Ar n
Skip
.Ar n
output blocks before starting to copy.
.It Cm count= Ns Ar n
Copy at most
.Ar n
input blocks.
.It Cm conv= Ns Ar value Ns Op , Ns Ar value Ns ...
Apply the conversions specified by
.Ar value .
.Bl -tag -width Ds
.It Cm lcase
Map uppercase characters to the corresponding lowercase character
using
.Fn tolower .
.It Cm ucase
Map lowercase characters to the corresponding uppercase character
using
.Fn toupper .
.It Cm swab
Swap each pair of bytes in the input block.
If there is an odd number of bytes in a block, the last one is
unmodified.
.It Cm noerror
In case of an error reading from the input, do not fail.
Instead, print a diagnostic message and a summary of the current
status.
.It Cm notrunc
Do not truncate the output file.
.It Cm sync
In case of a partial input block, pad with null bytes to form a
complete block.
.El
.El
.Sh STANDARDS
The
.Nm
utility is compliant with the
.St -p1003.1-2008
specification, except that it does not implement the
.Cm block
and
.Cm unblock
conversions.
+237
View File
@@ -0,0 +1,237 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static off_t ifull, ofull, ipart, opart;
static void
usage(void)
{
eprintf("usage: %s [operand...]\n", argv0);
}
static size_t
parsesize(char *expr)
{
char *s = expr;
size_t n = 1;
for (;;) {
n *= strtoumax(s, &s, 10);
switch (*s) {
case 'k': n <<= 10; s++; break;
case 'b': n <<= 9; s++; break;
}
if (*s != 'x' || !s[1])
break;
s++;
}
if (*s || n == 0)
eprintf("invalid block size expression '%s'\n", expr);
return n;
}
static void
bswap(unsigned char *buf, size_t len)
{
int c;
for (len &= ~1; len > 0; buf += 2, len -= 2) {
c = buf[0];
buf[0] = buf[1];
buf[1] = c;
}
}
static void
lcase(unsigned char *buf, size_t len)
{
for (; len > 0; buf++, len--)
buf[0] = tolower(buf[0]);
}
static void
ucase(unsigned char *buf, size_t len)
{
for (; len > 0; buf++, len--)
buf[0] = toupper(buf[0]);
}
static void
summary(void)
{
fprintf(stderr, "%"PRIdMAX"+%"PRIdMAX" records in\n", (intmax_t)ifull, (intmax_t)ipart);
fprintf(stderr, "%"PRIdMAX"+%"PRIdMAX" records out\n", (intmax_t)ofull, (intmax_t)opart);
}
int
main(int argc, char *argv[])
{
enum {
LCASE = 1 << 0,
UCASE = 1 << 1,
SWAB = 1 << 2,
NOERROR = 1 << 3,
NOTRUNC = 1 << 4,
SYNC = 1 << 5,
} conv = 0;
char *arg, *val, *end;
const char *iname = "-", *oname = "-";
int ifd = 0, ofd = 1, eof = 0;
size_t len, bs = 0, ibs = 512, obs = 512, ipos = 0, opos = 0;
off_t skip = 0, seek = 0, count = -1;
ssize_t ret;
unsigned char *buf;
argv0 = argc ? (argc--, *argv++) : "dd";
for (; argc > 0; argc--, argv++) {
arg = *argv;
val = strchr(arg, '=');
if (!val)
usage();
*val++ = '\0';
if (strcmp(arg, "if") == 0) {
iname = val;
} else if (strcmp(arg, "of") == 0) {
oname = val;
} else if (strcmp(arg, "ibs") == 0) {
ibs = parsesize(val);
} else if (strcmp(arg, "obs") == 0) {
obs = parsesize(val);
} else if (strcmp(arg, "bs") == 0) {
bs = parsesize(val);
} else if (strcmp(arg, "skip") == 0) {
skip = estrtonum(val, 0, LLONG_MAX);
} else if (strcmp(arg, "seek") == 0) {
seek = estrtonum(val, 0, LLONG_MAX);
} else if (strcmp(arg, "count") == 0) {
count = estrtonum(val, 0, LLONG_MAX);
} else if (strcmp(arg, "conv") == 0) {
do {
end = strchr(val, ',');
if (end)
*end++ = '\0';
if (strcmp(val, "lcase") == 0)
conv |= LCASE;
else if (strcmp(val, "ucase") == 0)
conv |= UCASE;
else if (strcmp(val, "swab") == 0)
conv |= SWAB;
else if (strcmp(val, "noerror") == 0)
conv |= NOERROR;
else if (strcmp(val, "notrunc") == 0)
conv |= NOTRUNC;
else if (strcmp(val, "sync") == 0)
conv |= SYNC;
else
eprintf("unknown conv flag '%s'\n", val);
val = end;
} while (val);
} else {
weprintf("unknown operand '%s'\n", arg);
usage();
}
}
if (bs)
ibs = obs = bs;
if (strcmp(iname, "-") != 0) {
ifd = open(iname, O_RDONLY);
if (ifd < 0)
eprintf("open %s:", iname);
}
if (strcmp(oname, "-") != 0) {
ofd = open(oname, O_WRONLY | O_CREAT | (conv & NOTRUNC || seek ? 0 : O_TRUNC), 0666);
if (ofd < 0)
eprintf("open %s:", oname);
}
len = MAX(ibs, obs) + ibs;
buf = emalloc(len);
if (skip && lseek(ifd, skip * ibs, SEEK_SET) < 0) {
while (skip--) {
ret = read(ifd, buf, ibs);
if (ret < 0)
eprintf("read:");
if (ret == 0) {
eof = 1;
break;
}
}
}
if (seek) {
if (!(conv & NOTRUNC) && ftruncate(ofd, seek * ibs) != 0)
eprintf("ftruncate:");
if (lseek(ofd, seek * ibs, SEEK_SET) < 0)
eprintf("lseek:");
/* XXX: handle non-seekable files */
}
while (!eof) {
while (ipos - opos < obs) {
if (ifull + ipart == count) {
eof = 1;
break;
}
ret = read(ifd, buf + ipos, ibs);
if (ret == 0) {
eof = 1;
break;
}
if (ret < 0) {
weprintf("read:");
if (!(conv & NOERROR))
return 1;
summary();
if (!(conv & SYNC))
continue;
ret = 0;
}
if (ret < ibs) {
ipart++;
if (conv & SYNC) {
memset(buf + ipos + ret, 0, ibs - ret);
ret = ibs;
}
} else {
ifull++;
}
if (conv & SWAB)
bswap(buf + ipos, ret);
if (conv & LCASE)
lcase(buf + ipos, ret);
if (conv & UCASE)
ucase(buf + ipos, ret);
ipos += ret;
if (bs && !(conv & (SWAB | LCASE | UCASE)))
break;
}
if (ipos == opos)
break;
do {
ret = write(ofd, buf + opos, MIN(obs, ipos - opos));
if (ret < 0)
eprintf("write:");
if (ret == 0)
eprintf("write returned 0\n");
if (ret < obs)
opart++;
else
ofull++;
opos += ret;
} while (ipos - opos >= (eof ? 1 : obs));
if (opos < ipos)
memmove(buf, buf + opos, ipos - opos);
ipos -= opos;
opos = 0;
}
summary();
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
.Dd October 8, 2015
.Dt DIRNAME 1
.Os sbase
.Sh NAME
.Nm dirname
.Nd strip final path component
.Sh SYNOPSIS
.Nm
.Ar path
.Sh DESCRIPTION
.Nm
writes
.Ar path
with its final path component removed to stdout.
.Sh SEE ALSO
.Xr basename 1 ,
.Xr dirname 3
.Sh STANDARDS
POSIX.1-2013.
+27
View File
@@ -0,0 +1,27 @@
/* See LICENSE file for copyright and license details. */
#include <libgen.h>
#include <stdio.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s path\n", argv0);
}
int
main(int argc, char *argv[])
{
ARGBEGIN {
default:
usage();
} ARGEND
if (argc != 1)
usage();
puts(dirname(argv[0]));
return fshut(stdout, "<stdout>");
}
+57
View File
@@ -0,0 +1,57 @@
.Dd October 8, 2015
.Dt DU 1
.Os sbase
.Sh NAME
.Nm du
.Nd display disk usage statistics
.Sh SYNOPSIS
.Nm
.Op Fl a | s
.Op Fl d Ar depth
.Op Fl h
.Op Fl k
.Op Fl H | L | P
.Op Fl x
.Op Ar file ...
.Sh DESCRIPTION
.Nm
displays the file system block usage for each
.Ar file
argument and for each directory in the file hierarchy rooted in directory
argument.
If no
.Ar file
is specified, the block usage of the hierarchy rooted in the current directory
is displayed.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl a
Display an entry for each file in the file hierarchy.
.It Fl s
Display only the grand total for the specified files.
.It Fl d Ar depth
Maximum directory depth to print files and directories.
.It Fl h
Enable human-readable output.
.It Fl k
By default all sizes are reported in 512-byte block counts.
The
.Fl k
option causes the numbers to be reported in kilobyte counts.
.It Fl H
Only dereference symbolic links that are passed as command line arguments when
recursively traversing directories.
.It Fl L
Always dereference symbolic links while recursively traversing directories.
.It Fl P
Don't dereference symbolic links.
This is the default.
.It Fl x
Do not traverse file systems mount points.
.El
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl dhP
flags are an extension to that specification.
+167
View File
@@ -0,0 +1,167 @@
/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <search.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "fs.h"
#include "util.h"
static size_t maxdepth = SIZE_MAX;
static size_t blksize = 512;
static int aflag = 0;
static int sflag = 0;
static int hflag = 0;
struct file {
dev_t devno;
ino_t inode;
};
static void
printpath(off_t n, const char *path)
{
if (hflag)
printf("%s\t%s\n", humansize(n * blksize), path);
else
printf("%jd\t%s\n", (intmax_t)n, path);
}
static off_t
nblks(blkcnt_t blocks)
{
return (512 * blocks + blksize - 1) / blksize;
}
static int
cmp(const void *p1, const void *p2)
{
const struct file *f1 = p1, *f2 = p2;
if (f1->devno > f2->devno)
return -1;
if (f1->devno < f2->devno)
return 1;
/* f1->devno == f2->devno */
if (f1->inode < f2->inode)
return -1;
if (f1->inode > f2->inode)
return 1;
return 0;
}
static int
duplicated(dev_t dev, ino_t ino)
{
static void *tree;
struct file **fpp, *fp, file = {dev, ino};
if ((fpp = tsearch(&file, &tree, cmp)) == NULL)
eprintf("%s:", argv0);
if (*fpp != &file)
return 1;
/* new file added */
fp = emalloc(sizeof(*fp));
*fp = file;
*fpp = fp;
return 0;
}
static void
du(int dirfd, const char *path, struct stat *st, void *data, struct recursor *r)
{
off_t *total = data, subtotal;
subtotal = nblks(st->st_blocks);
if (S_ISDIR(st->st_mode)) {
recurse(dirfd, path, &subtotal, r);
} else if (r->follow != 'P' || st->st_nlink > 1) {
if (duplicated(st->st_dev, st->st_ino))
goto print;
}
*total += subtotal;
print:
if (!r->depth)
printpath(*total, r->path);
else if (!sflag && r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag))
printpath(subtotal, r->path);
}
static void
usage(void)
{
eprintf("usage: %s [-a | -s] [-d depth] [-h] [-k] [-H | -L | -P] [-x] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
struct recursor r = { .fn = du, .follow = 'P' };
off_t n = 0;
int kflag = 0, dflag = 0;
char *bsize;
ARGBEGIN {
case 'a':
aflag = 1;
break;
case 'd':
dflag = 1;
maxdepth = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
break;
case 'h':
hflag = 1;
break;
case 'k':
kflag = 1;
break;
case 's':
sflag = 1;
break;
case 'x':
r.flags |= SAMEDEV;
break;
case 'H':
case 'L':
case 'P':
r.follow = ARGC();
break;
default:
usage();
} ARGEND
if ((aflag && sflag) || (dflag && sflag))
usage();
bsize = getenv("BLOCKSIZE");
if (bsize)
blksize = estrtonum(bsize, 1, MIN(LLONG_MAX, SIZE_MAX));
if (kflag)
blksize = 1024;
if (!argc) {
recurse(AT_FDCWD, ".", &n, &r);
} else {
for (; *argv; argc--, argv++) {
n = 0;
recurse(AT_FDCWD, *argv, &n, &r);
}
}
return fshut(stdout, "<stdout>") || recurse_status;
}
+27
View File
@@ -0,0 +1,27 @@
.Dd October 8, 2015
.Dt ECHO 1
.Os sbase
.Sh NAME
.Nm echo
.Nd print arguments
.Sh SYNOPSIS
.Nm
.Op Fl n
.Op Ar string ...
.Sh DESCRIPTION
.Nm
writes each
.Ar string
to stdout, separated by spaces and terminated by
a newline.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl n
Do not print the terminating newline.
.El
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl n
flag is an extension to that specification.
+24
View File
@@ -0,0 +1,24 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <string.h>
#include "util.h"
int
main(int argc, char *argv[])
{
int nflag = 0;
argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
if (*argv && !strcmp(*argv, "-n")) {
nflag = 1;
argc--, argv++;
}
for (; *argv; argc--, argv++)
putword(stdout, *argv);
if (!nflag)
putchar('\n');
return fshut(stdout, "<stdout>");
}
+238
View File
@@ -0,0 +1,238 @@
.Dd December 27, 2016
.Dt ED 1
.Os sbase
.Sh NAME
.Nm ed
.Nd text editor
.Sh SYNOPSIS
.Nm
.Op Fl s
.Op Fl p Ar string
.Op Ar file
.Sh DESCRIPTION
.Nm
is the standard text editor.
It performs line-oriented operations on a buffer; The buffer's contents are
manipulated in command mode and text is written to the buffer in input mode.
Command mode is the default.
To exit input mode enter a dot ('.') on a line of its own.
.Pp
If
.Nm
is invoked with a file as an argument, it will simulate an edit command and read
the file's contents into a buffer.
Changes to this buffer are local to
.Nm
until a write command is given.
.Pp
.Nm
uses the basic regular expression syntax and allows any character but space and
newline to be used as a delimiter in regular expressions.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl s
Suppress diagnostic messages
.It Fl p Ar string
Use
.Ar string
as a prompt when in command mode
.El
.Sh EXTENDED DESCRIPTION
.Ss Addresses
Commands operate on addresses.
Addresses are used to refer to lines within the buffer.
Address ranges may have spaces before and after the separator.
Unless otherwise specified, 0 is an invalid address.
The following symbols are valid addresses:
.Bl -tag -width Ds
.It n
The nth line.
.It .
The current line, or "dot".
.It $
The last line.
.It +
The next line.
.It +n
The nth next line.
.It ^ or -
The previous line.
.It ^n or -n
The nth previous line.
.It x,y
The range of lines from x to y.
The default value of x is 1, and the default value of y is $.
.It x;y
As above, except that the current line is set to x.
Omitting x in this case uses the current line as the default value.
.It /re/
The next line matching re.
.It ?re?
The last line matching re.
.It 'c
The line marked by c. See k below.
.El
.Ss Commands
.Nm
expects to see one command per line, with the following exception: commands may
be suffixed with either a list, number, or print command.
These suffixed commands are run after the command they're suffixed to has
executed.
.Pp
The following is the list of commands that
.Nm
knows about.
The parentheses contain the default addresses that a command uses.
.Bl -tag -width Ds
.It (.)a
Append text after the addressed line.
The dot is set to the last line entered.
If no text was entered, the dot is set to the addressed line.
An address of 0 appends to the start of the buffer.
.It (.,.)c
Delete the addressed lines and then accept input to replace them.
The dot is set to the last line entered.
If no text was entered, the dot is set to the line before the deleted lines.
.It (.,.)d
Delete the addressed lines.
If there is a line after the deleted range, the dot is set to it.
Otherwise, the dot is set to the line before the deleted range.
.It e Ar file
Delete the contents of the buffer and load in
.Ar file
for editing, printing the bytes read to standard output.
If no filename is given,
.Nm
uses the currently remembered filename.
The remembered filename is set to
.Ar file
for later use.
.It E Ar file
As above, but without warning if the current buffer has unsaved changes.
.It f Ar file
Set the currently remembered filename to
.Ar file
, or print the currently remembered filename if
.Ar file
is omitted.
.It (1,$)g/re/command
Apply command to lines matching re.
The dot is set to the matching line before command is executed.
When each matching line has been operated on, the dot is set to the last line
operated on.
If no lines match then the dot remains unchanged.
The command used may not be g, G, v, or V.
.It (1,$)G/re/
Interactively edit the range of line addresses that match re.
The dot is set to the matching line and printed before a command is input.
When each matching line has been operated on, the dot is set to the last line
operated on.
If no lines match then the dot remains unchanged.
The command used may not be a, c, i, g, G, v, or V.
.It h
Print the reason for the most recent error.
.It H
Toggle error explanations.
If on, the above behaviour is produced on all subsequent errors.
.It (.)i
Insert text into the buffer before the addressed line.
The dot is set to the last line entered.
If no text was entered, the dot is set to the addressed line
.It (.,.+1)j
Join two lines together.
If only one address is given, nothing happens.
The dot is set to the newly joined line.
.It (.)kc
Mark the line with the lower case character c. The dot is unchanged.
.It (.,.)l
Unambiguously print the addressed lines.
The dot is set to the last line written.
.It (.,.)m(.)
Move lines in the buffer to the line address on the right hand side.
An address of 0 on the right hand side moves to the start of the buffer.
The dot is set to the last line moved.
.It (.,.)n
Print the addressed lines and their numbers.
The dot is set to the last line printed.
.It (.,.)p
Print the addressed lines.
The dot is set to the last line printed.
.It P
Toggle the prompt.
Defaults to off, but is switched on if the -p flag is used.
.It q
Quit
.Nm
, warning if there are unsaved changes.
.It Q
As above, but without warning if the current buffer has unsaved changes.
.It ($)r Ar file
Read in
.Ar file
and append it to the current buffer, printing the bytes read to standard output.
The currently remembered filename isn't changed unless it's empty.
An address of 0 reads the file into the start of the buffer.
.It (.,.)s/re/replacement/flags
Substitute re for replacement in lines matching re.
An & within replacement is replaced with the whole string matched by re.
Backrefs can be used with the form \\n, where n is a positive non-zero integer.
When % is the only character in replacement, it is substituted for the
replacement string from the last substitute command.
If a newline is part of replacement then the matched string is split into two
lines; this cannot be done as part of a g or v command.
If flags contains an integer n, then the nth match is replaced.
If flags contains g, all matches are replaced.
The dot is set to the last line matched.
.It (.,.)t(.)
As m, but copying instead of moving.
The dot is set to the last line added.
.It u
Undo the last change.
The dot is set to whatever it was before the undone command was performed.
.It (1.$)v/re/command
As with g, but operating on lines that don't match re.
.It (1.$)V/re/
As with G, but operating on lines that don't match re.
.It (1,$)w Ar file
Write the addressed lines to
.Ar file
, overwriting its previous contents if the file exists, and print the number of
bytes written.
If no filename is given the currently remembered filename will be used instead.
The dot is unchanged.
.It (1,$)W Ar file
As above, but instead of overwriting the contents of
.Ar file
the addressed lines are appended to
.Ar file
instead.
.It (.+1)
Print the addressed line.
Sets the dot to that line.
.It ($)=
Print the line number of the addressed line.
The dot is unchanged.
.It &
Repeat the last command.
.It ! Ar command
Execute
.Ar command
using sh.
If the first character of
.Ar command
is '!' then it is replaced with the text of the previous command.
An unescaped % is replaced with the currently remembered filename.
! does not process escape characters.
When
.Ar command
returns a '!' is printed.
The dot is unchanged.
.El
.Sh SEE ALSO
.Xr sed 1 ,
.Xr regexp 3
.Sh STANDARDS
POSIX.1-2013.
Except where noted here:
g and v operate on single commands rather than lists delimited with '\e'.
e, E, r, w, and W commands cannot accept shell escapes.
+1650
View File
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
.Dd October 8, 2015
.Dt ENV 1
.Os sbase
.Sh NAME
.Nm env
.Nd modify the environment, then print it or run a command
.Sh SYNOPSIS
.Nm
.Op Fl i
.Oo Fl u Ar var Oc ...
.Oo Ar var Ns = Ns Ar value Oc ...
.Oo Ar cmd Oo arg ... Oc Oc
.Sh DESCRIPTION
.Nm
unsets each
.Ar var ,
then adds or sets each
.Ar ( var , value )
tuple in the environment.
.Pp
If
.Ar cmd
is given, it is executed in this new environment;
otherwise, the modified environment is printed to stdout.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl i
Completely ignore the existing environment and execute
.Ar cmd
only with each
.Ar ( var , value )
tuple specified.
.It Fl u Ar var
Unset
.Ar var
in the environment.
.El
.Sh SEE ALSO
.Xr printenv 1 ,
.Xr putenv 3 ,
.Xr environ 7
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl u
flag is an extension to that specification.
+49
View File
@@ -0,0 +1,49 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
extern char **environ;
static void
usage(void)
{
eprintf("usage: %s [-i] [-u var] ... [var=value] ... [cmd [arg ...]]\n", argv0);
}
int
main(int argc, char *argv[])
{
int savederrno;
ARGBEGIN {
case 'i':
*environ = NULL;
break;
case 'u':
if (unsetenv(EARGF(usage())) < 0)
eprintf("unsetenv:");
break;
default:
usage();
} ARGEND
for (; *argv && strchr(*argv, '='); argc--, argv++)
putenv(*argv);
if (*argv) {
execvp(*argv, argv);
savederrno = errno;
weprintf("execvp %s:", *argv);
_exit(126 + (savederrno == ENOENT));
}
for (; *environ; environ++)
puts(*environ);
return fshut(stdout, "<stdout>");
}
+47
View File
@@ -0,0 +1,47 @@
.Dd October 8, 2015
.Dt EXPAND 1
.Os sbase
.Sh NAME
.Nm expand
.Nd expand tabs to spaces
.Sh SYNOPSIS
.Nm
.Op Fl i
.Op Fl t Ar tablist
.Op Ar file ...
.Sh DESCRIPTION
.Nm
converts tabs to spaces in each
.Ar file
as specified in
.Ar tablist .
If no file is given,
.Nm
reads from stdin.
.Pp
Backspace characters are preserved and decrement the column count
for tab calculations.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl i
Only expand tabs at the beginning of lines, i.e. expand each
line until a character different from '\et' and ' ' is reached.
.It Fl t Ar tablist
Specify tab size or tabstops.
.Ar tablist
is a list of one (in the former case) or multiple (in the latter case)
strictly positive integers separated by ' ' or ','.
.Pp
The default
.Ar tablist
is "8".
.El
.Sh SEE ALSO
.Xr fold 1 ,
.Xr unexpand 1
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl i
flag is an extension to that specification.
+131
View File
@@ -0,0 +1,131 @@
/* See LICENSE file for copyright and license details. */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "utf.h"
#include "util.h"
static int iflag = 0;
static size_t *tablist = NULL;
static size_t tablistlen = 0;
static size_t
parselist(const char *s)
{
size_t i;
char *p, *tmp;
tmp = estrdup(s);
for (i = 0; (p = strsep(&tmp, " ,")); i++) {
if (*p == '\0')
eprintf("empty field in tablist\n");
tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
if (i > 0 && tablist[i - 1] >= tablist[i])
eprintf("tablist must be ascending\n");
}
tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
/* tab length = 1 for the overflowing case later in the matcher */
tablist[i] = 1;
return i;
}
static int
expand(const char *file, FILE *fp)
{
size_t bol = 1, col = 0, i;
Rune r;
while (efgetrune(&r, fp, file)) {
switch (r) {
case '\t':
if (tablistlen == 1)
i = 0;
else for (i = 0; i < tablistlen; i++)
if (col < tablist[i])
break;
if (bol || !iflag) {
do {
col++;
putchar(' ');
} while (col % tablist[i]);
} else {
putchar('\t');
col = tablist[i];
}
break;
case '\b':
bol = 0;
if (col)
col--;
putchar('\b');
break;
case '\n':
bol = 1;
col = 0;
putchar('\n');
break;
default:
col++;
if (r != ' ')
bol = 0;
efputrune(&r, stdout, "<stdout>");
break;
}
}
return 0;
}
static void
usage(void)
{
eprintf("usage: %s [-i] [-t tablist] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp;
int ret = 0;
char *tl = "8";
ARGBEGIN {
case 'i':
iflag = 1;
break;
case 't':
tl = EARGF(usage());
if (!*tl)
eprintf("tablist cannot be empty\n");
break;
default:
usage();
} ARGEND
tablistlen = parselist(tl);
if (!argc) {
expand("<stdin>", stdin);
} else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fp = stdin;
} else if (!(fp = fopen(*argv, "r"))) {
weprintf("fopen %s:", *argv);
ret = 1;
continue;
}
expand(*argv, fp);
if (fp != stdin && fshut(fp, *argv))
ret = 1;
}
}
ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
return ret;
}
+101
View File
@@ -0,0 +1,101 @@
.Dd October 8, 2015
.Dt EXPR 1
.Os sbase
.Sh NAME
.Nm expr
.Nd evaluate expression
.Sh SYNOPSIS
.Nm
.Ar expression
.Sh DESCRIPTION
.Nm
evaluates
.Ar expression
and writes the result to stdout.
.Pp
There are two elemental expressions,
.Sy integer
and
.Sy string .
Let
.Sy expr
be a non-elemental expression and
.Sy expr1 ,
.Sy expr2
arbitrary expressions.
Then
.Sy expr
has the recursive form
.Sy expr = [(] expr1 operand expr2 [)] .
.Pp
With
.Sy operand
being in order of increasing precedence:
.Bl -tag -width Ds
.It |
Evaluate to
.Sy expr1
if it is neither an empty string nor 0; otherwise evaluate to
.Sy expr2 .
.It &
Evaluate to
.Sy expr1
if
.Sy expr1
and
.Sy expr2
are neither empty strings nor 0; otherwise evaluate to 0.
.It = > >= < <= !=
If
.Sy expr1
and
.Sy expr2
are integers, evaluate to 1 if the relation is true and 0 if it is false.
If
.Sy expr1
and
.Sy expr2
are strings, apply the relation to the return value of
.Xr strcmp 3 .
.It + -
If
.Sy expr1
and
.Sy expr2
are integers, evaluate to their sum or subtraction.
.It * / %
If
.Sy expr1
and
.Sy expr2
are integers, evaluate to their multiplication, division or remainder.
.It :
Evaluate to the number of characters matched in
.Sy expr1
against
.Sy expr2 . expr2
is anchored with an implicit '^'.
.Pp
You can't directly match the empty string, since zero matched characters
resolve equally to a failed match.
To work around this limitation, use "expr X'' : 'X$' instead of "expr ''
: '$'"
.El
.Sh EXIT STATUS
.Bl -tag -width Ds
.It 0
.Ar expression
is neither an empty string nor 0.
.It 1
.Ar expression
is an empty string or 0.
.It 2
.Ar expression
is invalid.
.It > 2
An error occurred.
.El
.Sh SEE ALSO
.Xr test 1
.Sh STANDARDS
POSIX.1-2013.
+244
View File
@@ -0,0 +1,244 @@
/* See LICENSE file for copyright and license details. */
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utf.h"
#include "util.h"
/* tokens, one-character operators represent themselves */
enum {
VAL = CHAR_MAX + 1, GE, LE, NE
};
struct val {
char *str;
long long num;
};
static void
tonum(struct val *v)
{
const char *errstr;
long long d;
/* check if val is the result of an earlier calculation */
if (!v->str)
return;
d = strtonum(v->str, LLONG_MIN, LLONG_MAX, &errstr);
if (errstr)
enprintf(2, "error: expected integer, got %s\n", v->str);
v->num = d;
}
static void
ezero(struct val *v)
{
if (v->num != 0)
return;
enprintf(2, "division by zero\n");
}
static int
valcmp(struct val *a, struct val *b)
{
int ret;
const char *err1, *err2;
long long d1, d2;
d1 = strtonum(a->str, LLONG_MIN, LLONG_MAX, &err1);
d2 = strtonum(b->str, LLONG_MIN, LLONG_MAX, &err2);
if (!err1 && !err2) {
ret = (d1 > d2) - (d1 < d2);
} else {
ret = strcmp(a->str, b->str);
}
return ret;
}
static void
match(struct val *vstr, struct val *vregx, struct val *ret)
{
regex_t re;
regmatch_t matches[2];
size_t anchlen;
char *s, *p, *anchreg;
char *str = vstr->str, *regx = vregx->str;
/* anchored regex */
anchlen = strlen(regx) + 1 + 1;
anchreg = emalloc(anchlen);
estrlcpy(anchreg, "^", anchlen);
estrlcat(anchreg, regx, anchlen);
enregcomp(3, &re, anchreg, 0);
free(anchreg);
if (regexec(&re, str, 2, matches, 0)) {
regfree(&re);
ret->str = re.re_nsub ? "" : NULL;
return;
} else if (re.re_nsub) {
regfree(&re);
s = str + matches[1].rm_so;
p = str + matches[1].rm_eo;
*p = '\0';
ret->str = enstrdup(3, s);
return;
} else {
regfree(&re);
str += matches[0].rm_so;
ret->num = utfnlen(str, matches[0].rm_eo - matches[0].rm_so);
return;
}
}
static void
doop(int *ophead, int *opp, struct val *valhead, struct val *valp)
{
struct val ret = { .str = NULL, .num = 0 }, *a, *b;
int op;
/* an operation "a op b" needs an operator and two values */
if (opp[-1] == '(')
enprintf(2, "syntax error: extra (\n");
if (valp - valhead < 2)
enprintf(2, "syntax error: missing expression or extra operator\n");
a = valp - 2;
b = valp - 1;
op = opp[-1];
switch (op) {
case '|':
if ( a->str && *a->str) ret.str = a->str;
else if (!a->str && a->num) ret.num = a->num;
else if ( b->str && *b->str) ret.str = b->str;
else ret.num = b->num;
break;
case '&':
if (((a->str && *a->str) || a->num) &&
((b->str && *b->str) || b->num)) {
ret.str = a->str;
ret.num = a->num;
}
break;
case '=': ret.num = (valcmp(a, b) == 0); break;
case '>': ret.num = (valcmp(a, b) > 0); break;
case GE : ret.num = (valcmp(a, b) >= 0); break;
case '<': ret.num = (valcmp(a, b) < 0); break;
case LE : ret.num = (valcmp(a, b) <= 0); break;
case NE : ret.num = (valcmp(a, b) != 0); break;
case '+': tonum(a); tonum(b); ret.num = a->num + b->num; break;
case '-': tonum(a); tonum(b); ret.num = a->num - b->num; break;
case '*': tonum(a); tonum(b); ret.num = a->num * b->num; break;
case '/': tonum(a); tonum(b); ezero(b); ret.num = a->num / b->num; break;
case '%': tonum(a); tonum(b); ezero(b); ret.num = a->num % b->num; break;
case ':': match(a, b, &ret); break;
}
valp[-2] = ret;
}
static int
lex(char *s, struct val *v)
{
int type = VAL;
char *ops = "|&=><+-*/%():";
if (s[0] && strchr(ops, s[0]) && !s[1]) {
/* one-char operand */
type = s[0];
} else if (s[0] && strchr("><!", s[0]) && s[1] == '=' && !s[2]) {
/* two-char operand */
type = (s[0] == '>') ? GE : (s[0] == '<') ? LE : NE;
}
return type;
}
static int
parse(char *expr[], int numexpr)
{
struct val *valhead, *valp, v = { .str = NULL, .num = 0 };
int *ophead, *opp, type, lasttype = 0;
char prec[] = {
[ 0 ] = 0, [VAL] = 0, ['('] = 0, [')'] = 0,
['|'] = 1,
['&'] = 2,
['='] = 3, ['>'] = 3, [GE] = 3, ['<'] = 3, [LE] = 3, [NE] = 3,
['+'] = 4, ['-'] = 4,
['*'] = 5, ['/'] = 5, ['%'] = 5,
[':'] = 6,
};
valp = valhead = enreallocarray(3, NULL, numexpr, sizeof(*valp));
opp = ophead = enreallocarray(3, NULL, numexpr, sizeof(*opp));
for (; *expr; expr++) {
switch ((type = lex(*expr, &v))) {
case VAL:
/* treatment of *expr is not known until
* doop(); treat as a string for now */
valp->str = *expr;
valp++;
break;
case '(':
*opp++ = type;
break;
case ')':
if (lasttype == '(')
enprintf(2, "syntax error: empty ( )\n");
while (opp > ophead && opp[-1] != '(')
doop(ophead, opp--, valhead, valp--);
if (opp == ophead)
enprintf(2, "syntax error: extra )\n");
opp--;
break;
default: /* operator */
if (prec[lasttype])
enprintf(2, "syntax error: extra operator\n");
while (opp > ophead && prec[opp[-1]] >= prec[type])
doop(ophead, opp--, valhead, valp--);
*opp++ = type;
break;
}
lasttype = type;
v.str = NULL;
v.num = 0;
}
while (opp > ophead)
doop(ophead, opp--, valhead, valp--);
if (valp == valhead)
enprintf(2, "syntax error: missing expression\n");
if (--valp > valhead)
enprintf(2, "syntax error: extra expression\n");
if (valp->str)
puts(valp->str);
else
printf("%lld\n", valp->num);
return (valp->str && *valp->str) || valp->num;
}
int
main(int argc, char *argv[])
{
int ret;
argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
ret = !parse(argv, argc);
if (fshut(stdout, "<stdout>"))
ret = 3;
return ret;
}
+13
View File
@@ -0,0 +1,13 @@
.Dd October 8, 2015
.Dt FALSE 1
.Os sbase
.Sh NAME
.Nm false
.Nd return failure
.Sh SYNOPSIS
.Nm
.Sh DESCRIPTION
.Nm
returns a status code indicating failure.
.Sh STANDARDS
POSIX.1-2013.
+6
View File
@@ -0,0 +1,6 @@
/* See LICENSE file for copyright and license details. */
int
main(void)
{
return 1;
}
+151
View File
@@ -0,0 +1,151 @@
.Dd July 30, 2025
.Dt FIND 1
.Os sbase
.Sh NAME
.Nm find
.Nd find files
.Sh SYNOPSIS
.Nm
.Op Fl H | L
.Ar path Op ...
.Op Ar expression
.Sh DESCRIPTION
.Nm
walks a file hierarchy starting at each
.Ar path
and applies the
.Ar expression
to each file encountered.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl H
Dereference symbolic links provided as
.Ar path .
.It Fl L
Dereference all symbolic links encountered.
.El
.Sh EXTENDED DESCRIPTION
.Ar expression
is a combination of the following primaries and boolean operators.
In the following descriptions the number n can be replaced by +n, n, or
-n, to mean more than, exactly, or less than n respectively.
.Ss Primaries
.Bl -tag -width Ds
.It Fl name Ar pattern
True if the name of the file matches the given pattern.
.It Fl path Ar pattern
True if the path to the file matches the given pattern.
.It Fl nouser
True if the file belongs to a user for which
.Xr getpwuid 3
returns NULL.
.It Fl nogroup
True if the file belongs to a group for which
.Xr getgrgid 3
returns NULL.
.It Fl xdev
True.
Do not enter directory on a different device.
.It Fl prune
True.
Do not enter directory.
.It Fl perm Ar mode
True if permissions on the file match mode.
Mode is a symbolic mode as used in chmod.
A leading '-' in mode checks that at least all bits in mode are set in
permissions for file.
Without the leading '-' the permissions for file must exactly match
mode.
.It Fl type Ar t
True if file is of type specified by
.Ar t .
.Bl -tag -width Ds
.It Ar b
block special
.It Ar c
character special
.It Ar d
directory
.It Ar l
symbolic link
.It Ar p
FIFO
.It Ar f
regular file
.It Ar s
socket
.El
.It Fl links Ar n
True if file has
.Ar n
links.
.It Fl user Ar name
True if file belongs to user
.Ar name .
.It Fl group Ar name
True if file belongs to group
.Ar name .
.It Fl size Ar n[c]
True if file size in 512 byte sectors (rounded up), or bytes (if
.Ar c
is given), is
.Ar n .
.It Fl atime n
True if file access time is
.Ar n
days.
.It Fl ctime
True if file status change time is
.Ar n
days.
.It Fl mtime
True if file modified time is
.Ar n
days.
.It Fl exec Ar cmd [arg ...] \&;
Execute cmd with given arguments, replacing each {} in argument list
with the current file.
True if cmd exits with status 0.
.It Fl exec Ar cmd [arg ...] {} +
True.
Add as many files as possible to argument list and execute when the list
is full or all files have been found.
.It Fl ok Ar cmd [arg ...] \&;
Prompt the user on each file encountered whether or not to execute cmd
as with -exec.
True if the user responds yes and cmd exits with status 0, false
otherwise.
.It Fl print
True.
Print the current pathname followed by a newline ('\en') character.
.It Fl print0
True.
Print the current pathname followed by a NUL ('\e0') character.
.It Fl newer Ar file
True if the modification time of the current file is newer than that of
the provided file.
.It Fl depth
True.
Causes find to evaluate files within in a directory before the directory
itself.
.El
.Ss Operators
In order of decreasing precedence
.Bl -tag -width Ds
.It Ar \&( expression \&)
True if expression is true.
.It Ar \&! expression
True if expression if false.
.It Ar expression [ Fl a ] Ar expression
True if both expressions are true.
Second expression is not evaluated if first expression is false.
.Fl a
is implied if there is no operator between primaries.
.It Ar expression Fl o Ar expression
True if either expression is true.
Second expression is not evaluated if first expression is true.
.El
.Pp
If no expression is supplied, -print is used.
If an expression is supplied but none of -print, -exec, or -ok is
supplied, then -a -print is appended to the expressions.
+1103
View File
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
.Dd October 8, 2015
.Dt FLOCK 1
.Os sbase
.Sh NAME
.Nm flock
.Nd tool to manage locks on files
.Sh SYNOPSIS
.Nm
.Op Fl nosux
.Ar file
.Ar cmd Op arg ...
.Sh DESCRIPTION
.Nm
is used to manage advisory locks on open files.
It is commonly used to prevent long running cron jobs from running in
parallel.
If
.Ar file
does not exist, it will be created.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl n
Set non-blocking mode on the lock.
Fail immediately if the lock cannot be acquired.
.It Fl o
Close the file descriptor before exec to avoid having the exec'ed
program holding on to the lock.
.It Fl s
Acquire a shared lock.
.It Fl u
Release the lock.
.It Fl x
Acquire an exclusive lock.
This is the default.
.El
+82
View File
@@ -0,0 +1,82 @@
/* See LICENSE file for copyright and license details. */
#include <sys/file.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s [-nosux] file cmd [arg ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
int fd, status, savederrno, flags = LOCK_EX, nonblk = 0, oflag = 0;
pid_t pid;
ARGBEGIN {
case 'n':
nonblk = LOCK_NB;
break;
case 'o':
oflag = 1;
break;
case 's':
flags = LOCK_SH;
break;
case 'u':
flags = LOCK_UN;
break;
case 'x':
flags = LOCK_EX;
break;
default:
usage();
} ARGEND
if (argc < 2)
usage();
if ((fd = open(*argv, O_RDONLY | O_CREAT, 0644)) < 0)
eprintf("open %s:", *argv);
if (flock(fd, flags | nonblk)) {
if (nonblk && errno == EWOULDBLOCK)
return 1;
eprintf("flock:");
}
switch ((pid = fork())) {
case -1:
eprintf("fork:");
case 0:
if (oflag && close(fd) < 0)
eprintf("close:");
argv++;
execvp(*argv, argv);
savederrno = errno;
weprintf("execvp %s:", *argv);
_exit(126 + (savederrno == ENOENT));
default:
break;
}
if (waitpid(pid, &status, 0) < 0)
eprintf("waitpid:");
if (close(fd) < 0)
eprintf("close:");
if (WIFSIGNALED(status))
return 128 + WTERMSIG(status);
if (WIFEXITED(status))
return WEXITSTATUS(status);
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
.Dd October 8, 2015
.Dt FOLD 1
.Os sbase
.Sh NAME
.Nm fold
.Nd wrap lines to width
.Sh SYNOPSIS
.Nm
.Op Fl bs
.Op Fl w Ar num | Fl Ns Ar num
.Op Ar file ...
.Sh DESCRIPTION
.Nm
reads each
.Ar file
and prints its lines wrapped such that no line
exceeds a certain width.
If no file is given,
.Nm
reads from stdin.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl b
Count bytes rather than characters.
.It Fl s
If a line contains spaces, break
at the last space within width.
.It Fl w Ar num | Fl Ns Ar num
Break at
.Ar num
characters.
The default is 80.
.El
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl Ns Ar num
syntax is an extension to that specification.
+130
View File
@@ -0,0 +1,130 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "text.h"
#include "util.h"
#include "utf.h"
static int bflag = 0;
static int sflag = 0;
static size_t width = 80;
static void
foldline(struct line *l, const char *fname) {
size_t i, col, last, spacesect, len;
Rune r;
int runelen;
for (i = 0, last = 0, col = 0, spacesect = 0; i < l->len; i += runelen) {
if (col >= width && ((l->data[i] != '\r' && l->data[i] != '\b') || bflag)) {
if (bflag && col > width)
i -= runelen; /* never split a character */
len = ((sflag && spacesect) ? spacesect : i) - last;
if (fwrite(l->data + last, 1, len, stdout) != len)
eprintf("fwrite <stdout>:");
if (l->data[i] != '\n')
putchar('\n');
if (sflag && spacesect)
i = spacesect;
last = i;
col = 0;
spacesect = 0;
}
runelen = charntorune(&r, l->data + i, l->len - i);
if (!runelen || r == Runeerror)
eprintf("charntorune: %s: invalid utf\n", fname);
if (sflag && isblankrune(r))
spacesect = i + runelen;
if (!bflag && iscntrl(l->data[i])) {
switch(l->data[i]) {
case '\b':
col -= (col > 0);
break;
case '\r':
col = 0;
break;
case '\t':
col += (8 - (col % 8));
if (col >= width)
i--;
break;
}
} else {
col += bflag ? runelen : 1;
}
}
if (l->len - last)
fwrite(l->data + last, 1, l->len - last, stdout);
}
static void
fold(FILE *fp, const char *fname)
{
static struct line line;
static size_t size = 0;
ssize_t len;
while ((len = getline(&line.data, &size, fp)) > 0) {
line.len = len;
foldline(&line, fname);
}
if (ferror(fp))
eprintf("getline %s:", fname);
}
static void
usage(void)
{
eprintf("usage: %s [-bs] [-w num | -num] [FILE ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
FILE *fp;
int ret = 0;
ARGBEGIN {
case 'b':
bflag = 1;
break;
case 's':
sflag = 1;
break;
case 'w':
width = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
break;
ARGNUM:
if (!(width = ARGNUMF()))
eprintf("illegal width value, too small\n");
break;
default:
usage();
} ARGEND
if (!argc) {
fold(stdin, "<stdin>");
} else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fp = stdin;
} else if (!(fp = fopen(*argv, "r"))) {
weprintf("fopen %s:", *argv);
ret = 1;
continue;
}
fold(fp, *argv);
if (fp != stdin && fshut(fp, *argv))
ret = 1;
}
}
ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
return ret;
}
+47
View File
@@ -0,0 +1,47 @@
/* See LICENSE file for copyright and license details. */
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
struct history {
struct history *prev;
dev_t dev;
ino_t ino;
};
struct recursor {
void (*fn)(int, const char *, struct stat *, void *, struct recursor *);
char path[PATH_MAX];
size_t pathlen;
struct history *hist;
int depth;
int maxdepth;
int follow;
int flags;
};
enum {
SAMEDEV = 1 << 0,
DIRFIRST = 1 << 1,
SILENT = 1 << 2,
CONFIRM = 1 << 3,
IGNORE = 1 << 4,
};
extern int cp_aflag;
extern int cp_fflag;
extern int cp_iflag;
extern int cp_pflag;
extern int cp_rflag;
extern int cp_vflag;
extern int cp_follow;
extern int cp_status;
extern int rm_status;
extern int recurse_status;
void recurse(int, const char *, void *, struct recursor *);
int cp(const char *, const char *, int);
void rm(int, const char *, struct stat *st, void *, struct recursor *);
+57
View File
@@ -0,0 +1,57 @@
.Dd October 8, 2015
.Dt GETCONF 1
.Os sbase
.Sh NAME
.Nm getconf
.Nd get configuration values
.Sh SYNOPSIS
.Nm
.Op Fl v Ar spec
.Ar var
.Ar [path]
.Sh DESCRIPTION
.Nm
writes the value of the variable
.Ar var
to stdout.
.sp
If
.Ar path
is given,
.Ar var
is matched against configuration values from
.Xr pathconf 3 .
If
.Ar path
is not given,
.Ar var
is matched against configuration values from
.Xr sysconf 3 ,
.Xr confstr 3
and limits.h (Minimum and Maximum).
.sp
If
.Ar var
is not defined,
.Nm
writes "undefined" to stdout.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl v Ar spec
Ignored.
.El
.Sh EXIT STATUS
.Bl -tag -width Ds
.It 0
.Ar var
was matched and its value written successfully.
.It 1
An error occured or
.Ar var
was invalid.
.El
.Sh STANDARDS
POSIX.1-2013.
Except for the
.Op Fl v
flag.
+108
View File
@@ -0,0 +1,108 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
struct var {
const char *k;
long v;
};
#include "getconf.h"
void
usage(void)
{
eprintf("usage: %s [-v spec] var [path]\n", argv0);
}
int
main(int argc, char *argv[])
{
size_t len;
long res;
int i;
char *str;
ARGBEGIN {
case 'v':
/* ignore */
EARGF(usage());
break;
default:
usage();
break;
} ARGEND
if (argc == 1) {
/* sysconf */
for (i = 0; i < LEN(sysconf_l); i++) {
if (strcmp(argv[0], sysconf_l[i].k))
continue;
errno = 0;
if ((res = sysconf(sysconf_l[i].v)) < 0) {
if (errno)
eprintf("sysconf %ld:", sysconf_l[i].v);
puts("undefined");
} else {
printf("%ld\n", res);
}
return fshut(stdout, "<stdout>");
}
/* confstr */
for (i = 0; i < LEN(confstr_l); i++) {
if (strcmp(argv[0], confstr_l[i].k))
continue;
errno = 0;
if (!(len = confstr(confstr_l[i].v, NULL, 0))) {
if (errno)
eprintf("confstr %ld:", confstr_l[i].v);
puts("undefined");
} else {
str = emalloc(len);
errno = 0;
if (!confstr(confstr_l[i].v, str, len)) {
if (errno)
eprintf("confstr %ld:", confstr_l[i].v);
puts("undefined");
} else {
puts(str);
}
free(str);
}
return fshut(stdout, "<stdout>");
}
/* limits */
for (i = 0; i < LEN(limits_l); i++) {
if (strcmp(argv[0], limits_l[i].k))
continue;
printf("%ld\n", limits_l[i].v);
return fshut(stdout, "<stdout>");
}
} else if (argc == 2) {
/* pathconf */
for (i = 0; i < LEN(pathconf_l); i++) {
if (strcmp(argv[0], pathconf_l[i].k))
continue;
errno = 0;
if ((res = pathconf(argv[1], pathconf_l[i].v)) < 0) {
if (errno)
eprintf("pathconf %ld:", pathconf_l[i].v);
puts("undefined");
} else {
printf("%ld\n", res);
}
return fshut(stdout, "<stdout>");
}
} else {
usage();
}
eprintf("invalid variable: %s\n", argv[0]);
return 0;
}
+94
View File
@@ -0,0 +1,94 @@
.Dd October 8, 2015
.Dt GREP 1
.Os sbase
.Sh NAME
.Nm grep
.Nd search files for patterns
.Sh SYNOPSIS
.Nm
.Op Fl EFHchilnqsvx
.Op Fl e Ar pattern
.Op Fl f Ar file
.Op Ar pattern
.Op Ar file ...
.Sh DESCRIPTION
.Nm
searches the input files for lines that match the
.Ar pattern ,
a regular expression as defined in
.Xr regex 7 or
.Xr re_format 7 .
By default each matching line is printed to stdout.
If no
.Ar file
is given
.Nm
reads from stdin.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl E
Match using extended regex.
.It Fl F
Match using fixed strings.
Treat each pattern specified as a string instead of a regular
expression.
.It Fl H
Prefix each matching line with its filename in the output.
This is the default when there is more than one file specified.
.It Fl c
Print only a count of matching lines.
.It Fl e Ar pattern
Specify a pattern used during the search of the input: an input
line is selected if it matches any of the specified patterns.
This option is most useful when multiple -e options are used to
specify multiple patterns, or when a pattern begins with a dash.
.It Fl f Ar file
Read one or more patterns from the file named by the pathname file.
Patterns in file shall be terminated by a <newline>.
A null pattern can be specified by an empty line in pattern_file.
Unless the -E or -F option is also specified, each pattern shall be
treated as a BRE.
(`-').
.It Fl h
Do not prefix each line with 'filename:' prefix.
.It Fl i
Match lines case insensitively.
.It Fl l
Print only the names of files with matching lines.
.It Fl n
Prefix each matching line with its line number in the input.
.It Fl q
Print nothing, only return status.
.It Fl s
Suppress the error messages ordinarily written for nonexistent or unreadable
files.
.It Fl v
Select lines which do
.Sy not
match the pattern.
.It Fl w
The expression is searched for as a word (as if surrounded by '\\<' and '\\>').
.It Fl x
Consider only input lines that use all characters in the line excluding the
terminating <newline> to match an entire fixed string or regular expression to
be matching lines.
.El
.Sh EXIT STATUS
.Bl -tag -width Ds
.It 0
One or more lines were matched.
.It 1
No lines were matched.
.It > 1
An error occurred.
.El
.Sh SEE ALSO
.Xr sed 1 ,
.Xr re_format 7 ,
.Xr regex 7
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl Hhw
flags are an extension to that specification.
+290
View File
@@ -0,0 +1,290 @@
/* See LICENSE file for copyright and license details. */
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "queue.h"
#include "util.h"
enum { Match = 0, NoMatch = 1, Error = 2 };
static void addpattern(const char *, size_t);
static void addpatternfile(FILE *);
static int grep(FILE *, const char *);
static int Eflag;
static int Fflag;
static int Hflag;
static int eflag;
static int fflag;
static int hflag;
static int iflag;
static int sflag;
static int vflag;
static int wflag;
static int xflag;
static int many;
static int mode;
struct pattern {
char *pattern;
regex_t preg;
SLIST_ENTRY(pattern) entry;
};
static SLIST_HEAD(phead, pattern) phead;
static void
addpattern(const char *pattern, size_t patlen)
{
struct pattern *pnode;
char *tmp;
int bol, eol;
size_t len;
if (!patlen)
return;
/* a null BRE/ERE matches every line */
if (!Fflag)
if (pattern[0] == '\0')
pattern = "^";
if (!Fflag && xflag) {
tmp = enmalloc(Error, patlen + 3);
snprintf(tmp, patlen + 3, "%s%s%s",
pattern[0] == '^' ? "" : "^",
pattern,
pattern[patlen - 1] == '$' ? "" : "$");
} else if (!Fflag && wflag) {
len = patlen + 5 + (Eflag ? 2 : 4);
tmp = enmalloc(Error, len);
bol = eol = 0;
if (pattern[0] == '^')
bol = 1;
if (pattern[patlen - 1] == '$')
eol = 1;
snprintf(tmp, len, "%s\\<%s%.*s%s\\>%s",
bol ? "^" : "",
Eflag ? "(" : "\\(",
(int)patlen - bol - eol, pattern + bol,
Eflag ? ")" : "\\)",
eol ? "$" : "");
} else {
tmp = enstrdup(Error, pattern);
}
pnode = enmalloc(Error, sizeof(*pnode));
pnode->pattern = tmp;
SLIST_INSERT_HEAD(&phead, pnode, entry);
}
static void
addpatternfile(FILE *fp)
{
static char *buf = NULL;
static size_t size = 0;
ssize_t len = 0;
while ((len = getline(&buf, &size, fp)) > 0) {
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = '\0';
addpattern(buf, (size_t)len);
}
if (ferror(fp))
enprintf(Error, "read error:");
}
static int
grep(FILE *fp, const char *str)
{
static char *buf = NULL;
static size_t size = 0;
ssize_t len = 0;
long c = 0, n;
struct pattern *pnode;
int match, result = NoMatch;
for (n = 1; (len = getline(&buf, &size, fp)) > 0; n++) {
/* Remove the trailing newline if one is present. */
if (len && buf[len - 1] == '\n')
buf[len - 1] = '\0';
match = 0;
SLIST_FOREACH(pnode, &phead, entry) {
if (Fflag) {
if (xflag) {
if (!(iflag ? strcasecmp : strcmp)(buf, pnode->pattern)) {
match = 1;
break;
}
} else {
if ((iflag ? strcasestr : strstr)(buf, pnode->pattern)) {
match = 1;
break;
}
}
} else {
if (regexec(&pnode->preg, buf, 0, NULL, 0) == 0) {
match = 1;
break;
}
}
}
if (match != vflag) {
result = Match;
switch (mode) {
case 'c':
c++;
break;
case 'l':
puts(str);
goto end;
case 'q':
exit(Match);
default:
if (!hflag && (many || Hflag))
printf("%s:", str);
if (mode == 'n')
printf("%ld:", n);
puts(buf);
break;
}
}
}
if (mode == 'c')
printf("%ld\n", c);
end:
if (ferror(fp)) {
weprintf("%s: read error:", str);
result = Error;
}
return result;
}
static void
usage(void)
{
enprintf(Error, "usage: %s [-EFHchilnqsvwx] [-e pattern] [-f file] "
"[pattern] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
struct pattern *pnode;
int m, flags = REG_NOSUB, match = NoMatch;
FILE *fp;
char *arg;
SLIST_INIT(&phead);
ARGBEGIN {
case 'E':
Eflag = 1;
Fflag = 0;
flags |= REG_EXTENDED;
break;
case 'F':
Fflag = 1;
Eflag = 0;
flags &= ~REG_EXTENDED;
break;
case 'H':
Hflag = 1;
hflag = 0;
break;
case 'e':
arg = EARGF(usage());
if (!(fp = fmemopen(arg, strlen(arg) + 1, "r")))
eprintf("fmemopen:");
addpatternfile(fp);
efshut(fp, arg);
eflag = 1;
break;
case 'f':
arg = EARGF(usage());
fp = fopen(arg, "r");
if (!fp)
enprintf(Error, "fopen %s:", arg);
addpatternfile(fp);
efshut(fp, arg);
fflag = 1;
break;
case 'h':
hflag = 1;
Hflag = 0;
break;
case 'c':
case 'l':
case 'n':
case 'q':
mode = ARGC();
break;
case 'i':
flags |= REG_ICASE;
iflag = 1;
break;
case 's':
sflag = 1;
break;
case 'v':
vflag = 1;
break;
case 'w':
wflag = 1;
break;
case 'x':
xflag = 1;
break;
default:
usage();
} ARGEND
if (argc == 0 && !eflag && !fflag)
usage(); /* no pattern */
/* just add literal pattern to list */
if (!eflag && !fflag) {
if (!(fp = fmemopen(argv[0], strlen(argv[0]) + 1, "r")))
eprintf("fmemopen:");
addpatternfile(fp);
efshut(fp, argv[0]);
argc--;
argv++;
}
if (!Fflag)
/* Compile regex for all search patterns */
SLIST_FOREACH(pnode, &phead, entry)
enregcomp(Error, &pnode->preg, pnode->pattern, flags);
many = (argc > 1);
if (argc == 0) {
match = grep(stdin, "<stdin>");
} else {
for (; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fp = stdin;
} else if (!(fp = fopen(*argv, "r"))) {
if (!sflag)
weprintf("fopen %s:", *argv);
match = Error;
continue;
}
m = grep(fp, *argv);
if (m == Error || (match != Error && m == Match))
match = m;
if (fp != stdin && fshut(fp, *argv))
match = Error;
}
}
if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
match = Error;
return match;
}
+40
View File
@@ -0,0 +1,40 @@
.Dd October 8, 2015
.Dt HEAD 1
.Os sbase
.Sh NAME
.Nm head
.Nd display initial lines of files
.Sh SYNOPSIS
.Nm
.Op Fl n Ar num | Fl Ns Ar num
.Op Ar file ...
.Sh DESCRIPTION
.Nm
writes
.Ar num
lines of each
.Ar file
to stdout.
If no
.Ar file
is given
.Nm
reads from stdin.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl n Ar num | Fl Ns Ar num
Display initial
.Ar num
|
.Sy N
lines.
Default is 10.
.El
.Sh SEE ALSO
.Xr tail 1
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Op Fl Ns num
syntax is an extension to that specification.
+77
View File
@@ -0,0 +1,77 @@
/* See LICENSE file for copyright and license details. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
static void
head(FILE *fp, const char *fname, size_t n)
{
char *buf = NULL;
size_t i = 0, size = 0;
ssize_t len;
while (i < n && (len = getline(&buf, &size, fp)) > 0) {
fwrite(buf, 1, len, stdout);
i += (len && (buf[len - 1] == '\n'));
}
free(buf);
if (ferror(fp))
eprintf("getline %s:", fname);
}
static void
usage(void)
{
eprintf("usage: %s [-num | -n num] [file ...]\n", argv0);
}
int
main(int argc, char *argv[])
{
size_t n = 10;
FILE *fp;
int ret = 0, newline = 0, many = 0;
ARGBEGIN {
case 'n':
n = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
break;
ARGNUM:
n = ARGNUMF();
break;
default:
usage();
} ARGEND
if (!argc) {
head(stdin, "<stdin>", n);
} else {
many = argc > 1;
for (newline = 0; *argv; argc--, argv++) {
if (!strcmp(*argv, "-")) {
*argv = "<stdin>";
fp = stdin;
} else if (!(fp = fopen(*argv, "r"))) {
weprintf("fopen %s:", *argv);
ret = 1;
continue;
}
if (many) {
if (newline)
putchar('\n');
printf("==> %s <==\n", *argv);
}
newline = 1;
head(fp, *argv, n);
if (fp != stdin && fshut(fp, *argv))
ret = 1;
}
}
ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
return ret;
}
+18
View File
@@ -0,0 +1,18 @@
.Dd October 8, 2015
.Dt HOSTNAME 1
.Os sbase
.Sh NAME
.Nm hostname
.Nd set or print host name
.Sh SYNOPSIS
.Nm
.Op Ar name
.Sh DESCRIPTION
.Nm
sets the current host name to
.Ar name .
If no
.Ar name
is given, the current host name is written to stdout.
.Sh SEE ALSO
.Xr hostname 7
+36
View File
@@ -0,0 +1,36 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
static void
usage(void)
{
eprintf("usage: %s [name]\n", argv0);
}
int
main(int argc, char *argv[])
{
char host[HOST_NAME_MAX + 1];
ARGBEGIN {
default:
usage();
} ARGEND
if (!argc) {
if (gethostname(host, sizeof(host)) < 0)
eprintf("gethostname:");
puts(host);
} else if (argc == 1) {
if (sethostname(argv[0], strlen(argv[0])) < 0)
eprintf("sethostname:");
} else {
usage();
}
return fshut(stdout, "<stdout>");
}
+105
View File
@@ -0,0 +1,105 @@
.Dd October 8, 2015
.Dt JOIN 1
.Os sbase
.Sh NAME
.Nm join
.Nd relational database operator
.Sh SYNOPSIS
.Nm
.Op Fl 1 Ar field
.Op Fl 2 Ar field
.Op Fl o Ar list
.Op Fl e Ar string
.Op Fl a Ar fileno | Fl v Ar fileno
.Op Fl t Ar delim
.Ar file1 file2
.Sh DESCRIPTION
.Nm
lines from
.Ar file1
and
.Ar file2
on a matching field.
If one of the input files is '-', standard input is read for that file.
.Pp
Files are read sequentially and are assumed to be sorted on the join
field.
.Nm
does not check the order of input, and joining two unsorted files will
produce unexpected output.
.Pp
By default, input lines are matched on the first blank-separated
field; output lines are space-separated and consist of the join field
followed by the remaining fields from
.Ar file1 ,
then the remaining fields from
.Ar file2 .
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl 1 Ar field
Join on the
.Ar field Ns th
field of file 1.
.It Fl 2 Ar field
Join on the
.Ar field Ns th
field of file 2.
.It Fl a Ar fileno
Print unpairable lines from file
.Ar fileno
in addition to normal output.
.It Fl e Ar string
When used with
.Fl o ,
replace empty fields in the output list with
.Ar string .
.It Fl o Ar list
Format output according to the string
.Ar list .
Each element of
.Ar list
may be either
.Ar fileno.field
or 0 (representing the join field).
Elements in
.Ar list
may be separated by blanks or commas.
For example,
.Bd -literal -offset indent
join -o "0 2.1 1.3"
.Ed
.Pp
would print the join field, the first field of
.Ar file2 ,
then the third field of
.Ar file1 .
.Pp
Only paired lines are formatted with the
.Fl o
option.
Unpairable lines (selected with
.Fl a
or
.Fl v )
are printed raw.
.It Fl t Ar delim
Use the arbitrary string
.Ar delim
as field delimiter for both input and output.
.It Fl v Ar fileno
Print unpairable lines from file
.Ar fileno
instead of normal output.
.El
.Sh STANDARDS
POSIX.1-2013.
.Pp
With the following exception:
.Bl -bullet -offset indent
.It
Unpairable lines ignore formatting specified with
.Fl o .
.El
.Pp
The possibility of specifying multibyte delimiters of arbitrary
length is an extension to the specification.
+529
View File
@@ -0,0 +1,529 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "text.h"
#include "utf.h"
#include "util.h"
enum {
INIT = 1,
GROW = 2,
};
enum {
EXPAND = 0,
RESET = 1,
};
enum { FIELD_ERROR = -2, };
struct field {
char *s;
size_t len;
};
struct jline {
struct line text;
size_t nf;
size_t maxf;
struct field *fields;
};
struct spec {
size_t fileno;
size_t fldno;
};
struct outlist {
size_t ns;
size_t maxs;
struct spec **specs;
};
struct span {
size_t nl;
size_t maxl;
struct jline **lines;
};
static char *sep = NULL;
static char *replace = NULL;
static const char defaultofs = ' ';
static const int jfield = 1; /* POSIX default join field */
static int unpairsa = 0, unpairsb = 0;
static int oflag = 0;
static int pairs = 1;
static size_t seplen;
static struct outlist output;
static void
usage(void)
{
eprintf("usage: %s [-1 field] [-2 field] [-o list] [-e string] "
"[-a | -v fileno] [-t delim] file1 file2\n", argv0);
}
static void
prfield(struct field *fp)
{
if (fwrite(fp->s, 1, fp->len, stdout) != fp->len)
eprintf("fwrite:");
}
static void
prsep(void)
{
if (sep)
fwrite(sep, 1, seplen, stdout);
else
putchar(defaultofs);
}
static void
swaplines(struct jline *la, struct jline *lb)
{
struct jline tmp;
tmp = *la;
*la = *lb;
*lb = tmp;
}
static void
prjoin(struct jline *la, struct jline *lb, size_t jfa, size_t jfb)
{
struct spec *sp;
struct field *joinfield;
size_t i;
if (jfa >= la->nf || jfb >= lb->nf)
return;
joinfield = &la->fields[jfa];
if (oflag) {
for (i = 0; i < output.ns; i++) {
sp = output.specs[i];
if (sp->fileno == 1) {
if (sp->fldno < la->nf)
prfield(&la->fields[sp->fldno]);
else if (replace)
fputs(replace, stdout);
} else if (sp->fileno == 2) {
if (sp->fldno < lb->nf)
prfield(&lb->fields[sp->fldno]);
else if (replace)
fputs(replace, stdout);
} else if (sp->fileno == 0) {
prfield(joinfield);
}
if (i < output.ns - 1)
prsep();
}
} else {
prfield(joinfield);
prsep();
for (i = 0; i < la->nf; i++) {
if (i != jfa) {
prfield(&la->fields[i]);
prsep();
}
}
for (i = 0; i < lb->nf; i++) {
if (i != jfb) {
prfield(&lb->fields[i]);
if (i < lb->nf - 1)
prsep();
}
}
}
putchar('\n');
}
static void
prline(struct jline *lp)
{
if (fwrite(lp->text.data, 1, lp->text.len, stdout) != lp->text.len)
eprintf("fwrite:");
putchar('\n');
}
static int
jlinecmp(struct jline *la, struct jline *lb, size_t jfa, size_t jfb)
{
int status;
/* return FIELD_ERROR if both lines are short */
if (jfa >= la->nf) {
status = (jfb >= lb->nf) ? FIELD_ERROR : -1;
} else if (jfb >= lb->nf) {
status = 1;
} else {
status = memcmp(la->fields[jfa].s, lb->fields[jfb].s,
MAX(la->fields[jfa].len, lb->fields[jfb].len));
LIMIT(status, -1, 1);
}
return status;
}
static void
addfield(struct jline *lp, char *sp, size_t len)
{
if (lp->nf >= lp->maxf) {
lp->fields = ereallocarray(lp->fields, (GROW * lp->maxf),
sizeof(struct field));
lp->maxf *= GROW;
}
lp->fields[lp->nf].s = sp;
lp->fields[lp->nf].len = len;
lp->nf++;
}
static void
prspanjoin(struct span *spa, struct span *spb, size_t jfa, size_t jfb)
{
size_t i, j;
for (i = 0; i < (spa->nl - 1); i++)
for (j = 0; j < (spb->nl - 1); j++)
prjoin(spa->lines[i], spb->lines[j], jfa, jfb);
}
static struct jline *
makeline(char *s, size_t len)
{
struct jline *lp;
char *tmp;
size_t i, end;
if (s[len - 1] == '\n')
s[--len] = '\0';
lp = ereallocarray(NULL, INIT, sizeof(struct jline));
lp->text.data = s;
lp->text.len = len;
lp->fields = ereallocarray(NULL, INIT, sizeof(struct field));
lp->nf = 0;
lp->maxf = INIT;
for (i = 0; i < lp->text.len && isblank(lp->text.data[i]); i++)
;
while (i < lp->text.len) {
if (sep) {
if ((lp->text.len - i) < seplen ||
!(tmp = memmem(lp->text.data + i,
lp->text.len - i, sep, seplen))) {
goto eol;
}
end = tmp - lp->text.data;
addfield(lp, lp->text.data + i, end - i);
i = end + seplen;
} else {
for (end = i; !(isblank(lp->text.data[end])); end++) {
if (end + 1 == lp->text.len)
goto eol;
}
addfield(lp, lp->text.data + i, end - i);
for (i = end; isblank(lp->text.data[i]); i++)
;
}
}
eol:
addfield(lp, lp->text.data + i, lp->text.len - i);
return lp;
}
static int
addtospan(struct span *sp, FILE *fp, int reset)
{
char *newl = NULL;
ssize_t len;
size_t size = 0;
if ((len = getline(&newl, &size, fp)) < 0) {
if (ferror(fp))
eprintf("getline:");
else
return 0;
}
if (reset)
sp->nl = 0;
if (sp->nl >= sp->maxl) {
sp->lines = ereallocarray(sp->lines, (GROW * sp->maxl),
sizeof(struct jline *));
sp->maxl *= GROW;
}
sp->lines[sp->nl] = makeline(newl, len);
sp->nl++;
return 1;
}
static void
initspan(struct span *sp)
{
sp->nl = 0;
sp->maxl = INIT;
sp->lines = ereallocarray(NULL, INIT, sizeof(struct jline *));
}
static void
freespan(struct span *sp)
{
size_t i;
for (i = 0; i < sp->nl; i++) {
free(sp->lines[i]->fields);
free(sp->lines[i]->text.data);
}
free(sp->lines);
}
static void
initolist(struct outlist *olp)
{
olp->ns = 0;
olp->maxs = 1;
olp->specs = ereallocarray(NULL, INIT, sizeof(struct spec *));
}
static void
addspec(struct outlist *olp, struct spec *sp)
{
if (olp->ns >= olp->maxs) {
olp->specs = ereallocarray(olp->specs, (GROW * olp->maxs),
sizeof(struct spec *));
olp->maxs *= GROW;
}
olp->specs[olp->ns] = sp;
olp->ns++;
}
static struct spec *
makespec(char *s)
{
struct spec *sp;
int fileno;
size_t fldno;
if (!strcmp(s, "0")) { /* join field must be 0 and nothing else */
fileno = 0;
fldno = 0;
} else if ((s[0] == '1' || s[0] == '2') && s[1] == '.') {
fileno = s[0] - '0';
fldno = estrtonum(&s[2], 1, MIN(LLONG_MAX, SIZE_MAX)) - 1;
} else {
eprintf("%s: invalid format\n", s);
}
sp = ereallocarray(NULL, INIT, sizeof(struct spec));
sp->fileno = fileno;
sp->fldno = fldno;
return sp;
}
static void
makeolist(struct outlist *olp, char *s)
{
char *item, *sp;
sp = s;
while (sp) {
item = sp;
sp = strpbrk(sp, ", \t");
if (sp)
*sp++ = '\0';
addspec(olp, makespec(item));
}
}
static void
freespecs(struct outlist *olp)
{
size_t i;
for (i = 0; i < olp->ns; i++)
free(olp->specs[i]);
}
static void
join(FILE *fa, FILE *fb, size_t jfa, size_t jfb)
{
struct span spa, spb;
int cmp, eofa, eofb;
initspan(&spa);
initspan(&spb);
cmp = eofa = eofb = 0;
addtospan(&spa, fa, RESET);
addtospan(&spb, fb, RESET);
while (spa.nl && spb.nl) {
if ((cmp = jlinecmp(spa.lines[0], spb.lines[0], jfa, jfb)) < 0) {
if (unpairsa)
prline(spa.lines[0]);
if (!addtospan(&spa, fa, RESET)) {
if (unpairsb) { /* a is EOF'd; print the rest of b */
do
prline(spb.lines[0]);
while (addtospan(&spb, fb, RESET));
}
eofa = eofb = 1;
} else {
continue;
}
} else if (cmp > 0) {
if (unpairsb)
prline(spb.lines[0]);
if (!addtospan(&spb, fb, RESET)) {
if (unpairsa) { /* b is EOF'd; print the rest of a */
do
prline(spa.lines[0]);
while (addtospan(&spa, fa, RESET));
}
eofa = eofb = 1;
} else {
continue;
}
} else if (cmp == 0) {
/* read all consecutive matching lines from a */
do {
if (!addtospan(&spa, fa, EXPAND)) {
eofa = 1;
spa.nl++;
break;
}
} while (jlinecmp(spa.lines[spa.nl-1], spb.lines[0], jfa, jfb) == 0);
/* read all consecutive matching lines from b */
do {
if (!addtospan(&spb, fb, EXPAND)) {
eofb = 1;
spb.nl++;
break;
}
} while (jlinecmp(spa.lines[0], spb.lines[spb.nl-1], jfa, jfb) == 0);
if (pairs)
prspanjoin(&spa, &spb, jfa, jfb);
} else { /* FIELD_ERROR: both lines lacked join fields */
if (unpairsa)
prline(spa.lines[0]);
if (unpairsb)
prline(spb.lines[0]);
eofa = addtospan(&spa, fa, RESET) ? 0 : 1;
eofb = addtospan(&spb, fb, RESET) ? 0 : 1;
if (!eofa && !eofb)
continue;
}
if (eofa) {
spa.nl = 0;
} else {
swaplines(spa.lines[0], spa.lines[spa.nl - 1]); /* ugly */
spa.nl = 1;
}
if (eofb) {
spb.nl = 0;
} else {
swaplines(spb.lines[0], spb.lines[spb.nl - 1]); /* ugly */
spb.nl = 1;
}
}
freespan(&spa);
freespan(&spb);
}
int
main(int argc, char *argv[])
{
size_t jf[2] = { jfield, jfield, };
FILE *fp[2];
int ret = 0, n;
char *fno;
ARGBEGIN {
case '1':
jf[0] = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
break;
case '2':
jf[1] = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
break;
case 'a':
fno = EARGF(usage());
if (strcmp(fno, "1") == 0)
unpairsa = 1;
else if (strcmp(fno, "2") == 0)
unpairsb = 1;
else
usage();
break;
case 'e':
replace = EARGF(usage());
break;
case 'o':
oflag = 1;
initolist(&output);
makeolist(&output, EARGF(usage()));
break;
case 't':
sep = EARGF(usage());
break;
case 'v':
pairs = 0;
fno = EARGF(usage());
if (strcmp(fno, "1") == 0)
unpairsa = 1;
else if (strcmp(fno, "2") == 0)
unpairsb = 1;
else
usage();
break;
default:
usage();
} ARGEND
if (sep)
seplen = unescape(sep);
if (argc != 2)
usage();
for (n = 0; n < 2; n++) {
if (!strcmp(argv[n], "-")) {
argv[n] = "<stdin>";
fp[n] = stdin;
} else if (!(fp[n] = fopen(argv[n], "r"))) {
eprintf("fopen %s:", argv[n]);
}
}
jf[0]--;
jf[1]--;
join(fp[0], fp[1], jf[0], jf[1]);
if (oflag)
freespecs(&output);
if (fshut(fp[0], argv[0]) | (fp[0] != fp[1] && fshut(fp[1], argv[1])) |
fshut(stdout, "<stdout>"))
ret = 2;
return ret;
}
+39
View File
@@ -0,0 +1,39 @@
.Dd October 8, 2015
.Dt KILL 1
.Os sbase
.Sh NAME
.Nm kill
.Nd signal processes
.Sh SYNOPSIS
.Nm
.Op Fl s Ar signame | Fl num | Fl signame
.Ar pid ...
.Nm
.Fl l Op Ar num
.Sh DESCRIPTION
.Nm
signals TERM to each process or process group specified by
.Ar pid .
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl l Op Ar num
List all available signals or the signal name of
.Ar num .
.It Fl s Ar signame | Fl num | Fl signame
Send signal corresponding to
.Ar signame
|
.Ar num .
The default is TERM.
.El
.Sh SEE ALSO
.Xr kill 2 ,
.Xr signal 7
.Sh STANDARDS
POSIX.1-2013.
.Pp
The
.Fl Ar signame
and
.Fl Ar num
syntax is marked by POSIX.1-2013 as an X/OPEN System Interfaces option.
+131
View File
@@ -0,0 +1,131 @@
/* See LICENSE file for copyright and license details. */
#include <sys/wait.h>
#include <ctype.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "util.h"
struct {
const char *name;
const int sig;
} sigs[] = {
{ "0", 0 },
#define SIG(n) { #n, SIG##n }
SIG(ABRT), SIG(ALRM), SIG(BUS), SIG(CHLD), SIG(CONT), SIG(FPE), SIG(HUP),
SIG(ILL), SIG(INT), SIG(KILL), SIG(PIPE), SIG(QUIT), SIG(SEGV), SIG(STOP),
SIG(TERM), SIG(TRAP), SIG(TSTP), SIG(TTIN), SIG(TTOU), SIG(USR1), SIG(USR2),
SIG(URG),
#undef SIG
};
const char *
sig2name(const int sig)
{
size_t i;
for (i = 0; i < LEN(sigs); i++)
if (sigs[i].sig == sig)
return sigs[i].name;
eprintf("%d: bad signal number\n", sig);
return NULL; /* not reached */
}
int
name2sig(const char *name)
{
size_t i;
for (i = 0; i < LEN(sigs); i++)
if (!strcasecmp(sigs[i].name, name))
return sigs[i].sig;
eprintf("%s: bad signal name\n", name);
return -1; /* not reached */
}
static void
usage(void)
{
eprintf("usage: %s [-s signame | -num | -signame] pid ...\n"
" %s -l [num]\n", argv0, argv0);
}
int
main(int argc, char *argv[])
{
pid_t pid;
size_t i;
int ret = 0, sig = SIGTERM;
argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
if (!argc)
usage();
if ((*argv)[0] == '-') {
switch ((*argv)[1]) {
case 'l':
if ((*argv)[2])
goto longopt;
argc--, argv++;
if (!argc) {
for (i = 0; i < LEN(sigs); i++)
puts(sigs[i].name);
} else if (argc == 1) {
sig = estrtonum(*argv, 0, INT_MAX);
if (sig > 128)
sig = WTERMSIG(sig);
puts(sig2name(sig));
} else {
usage();
}
return fshut(stdout, "<stdout>");
case 's':
if ((*argv)[2])
goto longopt;
argc--, argv++;
if (!argc)
usage();
sig = name2sig(*argv);
argc--, argv++;
break;
case '-':
if ((*argv)[2])
goto longopt;
argc--, argv++;
break;
default:
longopt:
/* XSI-extensions -argnum and -argname*/
if (isdigit((*argv)[1])) {
sig = estrtonum((*argv) + 1, 0, INT_MAX);
sig2name(sig);
} else {
sig = name2sig((*argv) + 1);
}
argc--, argv++;
}
}
if (argc && !strcmp(*argv, "--"))
argc--, argv++;
if (!argc)
usage();
for (; *argv; argc--, argv++) {
pid = estrtonum(*argv, INT_MIN, INT_MAX);
if (kill(pid, sig) < 0) {
weprintf("kill %d:", pid);
ret = 1;
}
}
return ret;
}
+6
View File
@@ -0,0 +1,6 @@
AWK = awk
UNICODE = http://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
default:
@echo Downloading and parsing $(UNICODE)
@curl -\# $(UNICODE) | $(AWK) -f mkrunetype.awk
+36
View File
@@ -0,0 +1,36 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../utf.h"
int
fgetrune(Rune *r, FILE *fp)
{
char buf[UTFmax];
int i = 0, c;
while (i < UTFmax && (c = fgetc(fp)) != EOF) {
buf[i++] = c;
if (charntorune(r, buf, i) > 0)
break;
}
if (ferror(fp))
return -1;
return i;
}
int
efgetrune(Rune *r, FILE *fp, const char *file)
{
int ret;
if ((ret = fgetrune(r, fp)) < 0) {
fprintf(stderr, "fgetrune %s: %s\n", file, strerror(errno));
exit(1);
}
return ret;
}
+27
View File
@@ -0,0 +1,27 @@
/* See LICENSE file for copyright and license details. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../utf.h"
int
fputrune(const Rune *r, FILE *fp)
{
char buf[UTFmax];
return fwrite(buf, runetochar(buf, r), 1, fp);
}
int
efputrune(const Rune *r, FILE *fp, const char *file)
{
int ret;
if ((ret = fputrune(r, fp)) < 0) {
fprintf(stderr, "fputrune %s: %s\n", file, strerror(errno));
exit(1);
}
return ret;
}
+9
View File
@@ -0,0 +1,9 @@
/* Automatically generated by mkrunetype.awk */
#include "../utf.h"
#include "runetype.h"
int
isalnumrune(Rune r)
{
return isalpharune(r) || isdigitrune(r);
}
+830
View File
@@ -0,0 +1,830 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune alpha3[][2] = {
{ 0x00D6, 0x00D8 },
{ 0x00F6, 0x00F8 },
{ 0x02EC, 0x02EE },
{ 0x0374, 0x0376 },
{ 0x037D, 0x037F },
{ 0x0386, 0x0388 },
{ 0x038A, 0x038E },
{ 0x03A1, 0x03A3 },
{ 0x03F5, 0x03F7 },
{ 0x052F, 0x0531 },
{ 0x066F, 0x0671 },
{ 0x06D3, 0x06D5 },
{ 0x0710, 0x0712 },
{ 0x0887, 0x0889 },
{ 0x09A8, 0x09AA },
{ 0x09B0, 0x09B2 },
{ 0x09DD, 0x09DF },
{ 0x0A28, 0x0A2A },
{ 0x0A30, 0x0A32 },
{ 0x0A33, 0x0A35 },
{ 0x0A36, 0x0A38 },
{ 0x0A5C, 0x0A5E },
{ 0x0A8D, 0x0A8F },
{ 0x0A91, 0x0A93 },
{ 0x0AA8, 0x0AAA },
{ 0x0AB0, 0x0AB2 },
{ 0x0AB3, 0x0AB5 },
{ 0x0B28, 0x0B2A },
{ 0x0B30, 0x0B32 },
{ 0x0B33, 0x0B35 },
{ 0x0B5D, 0x0B5F },
{ 0x0B83, 0x0B85 },
{ 0x0B90, 0x0B92 },
{ 0x0B9A, 0x0B9E },
{ 0x0C0C, 0x0C0E },
{ 0x0C10, 0x0C12 },
{ 0x0C28, 0x0C2A },
{ 0x0C8C, 0x0C8E },
{ 0x0C90, 0x0C92 },
{ 0x0CA8, 0x0CAA },
{ 0x0CB3, 0x0CB5 },
{ 0x0CDE, 0x0CE0 },
{ 0x0D0C, 0x0D0E },
{ 0x0D10, 0x0D12 },
{ 0x0DB1, 0x0DB3 },
{ 0x0DBB, 0x0DBD },
{ 0x0E30, 0x0E32 },
{ 0x0E82, 0x0E86 },
{ 0x0E8A, 0x0E8C },
{ 0x0EA3, 0x0EA7 },
{ 0x0EB0, 0x0EB2 },
{ 0x0EC4, 0x0EC6 },
{ 0x0F47, 0x0F49 },
{ 0x10C5, 0x10C7 },
{ 0x10FA, 0x10FC },
{ 0x1248, 0x124A },
{ 0x1256, 0x125A },
{ 0x1288, 0x128A },
{ 0x12B0, 0x12B2 },
{ 0x12BE, 0x12C2 },
{ 0x12D6, 0x12D8 },
{ 0x1310, 0x1312 },
{ 0x167F, 0x1681 },
{ 0x176C, 0x176E },
{ 0x18A8, 0x18AA },
{ 0x1CEC, 0x1CEE },
{ 0x1CF3, 0x1CF5 },
{ 0x1F57, 0x1F5F },
{ 0x1FB4, 0x1FB6 },
{ 0x1FBC, 0x1FBE },
{ 0x1FC4, 0x1FC6 },
{ 0x1FF4, 0x1FF6 },
{ 0x2113, 0x2115 },
{ 0x2124, 0x212A },
{ 0x212D, 0x212F },
{ 0x2D25, 0x2D27 },
{ 0x2DA6, 0x2DA8 },
{ 0x2DAE, 0x2DB0 },
{ 0x2DB6, 0x2DB8 },
{ 0x2DBE, 0x2DC0 },
{ 0x2DC6, 0x2DC8 },
{ 0x2DCE, 0x2DD0 },
{ 0x2DD6, 0x2DD8 },
{ 0x309F, 0x30A1 },
{ 0x30FA, 0x30FC },
{ 0x312F, 0x3131 },
{ 0xA7D1, 0xA7D5 },
{ 0xA801, 0xA803 },
{ 0xA805, 0xA807 },
{ 0xA80A, 0xA80C },
{ 0xA8FB, 0xA8FD },
{ 0xA9E4, 0xA9E6 },
{ 0xA9FE, 0xAA00 },
{ 0xAA42, 0xAA44 },
{ 0xAAAF, 0xAAB1 },
{ 0xAAC0, 0xAAC2 },
{ 0xAB26, 0xAB28 },
{ 0xAB2E, 0xAB30 },
{ 0xAB5A, 0xAB5C },
{ 0xFB1D, 0xFB1F },
{ 0xFB28, 0xFB2A },
{ 0xFB36, 0xFB38 },
{ 0xFB3C, 0xFB40 },
{ 0xFB41, 0xFB43 },
{ 0xFB44, 0xFB46 },
{ 0xFE74, 0xFE76 },
{ 0x1000B, 0x1000D },
{ 0x10026, 0x10028 },
{ 0x1003A, 0x1003C },
{ 0x1003D, 0x1003F },
{ 0x10340, 0x10342 },
{ 0x1057A, 0x1057C },
{ 0x1058A, 0x1058C },
{ 0x10592, 0x10594 },
{ 0x10595, 0x10597 },
{ 0x105A1, 0x105A3 },
{ 0x105B1, 0x105B3 },
{ 0x105B9, 0x105BB },
{ 0x10785, 0x10787 },
{ 0x107B0, 0x107B2 },
{ 0x10808, 0x1080A },
{ 0x10835, 0x10837 },
{ 0x108F2, 0x108F4 },
{ 0x10A13, 0x10A15 },
{ 0x10A17, 0x10A19 },
{ 0x10AC7, 0x10AC9 },
{ 0x111DA, 0x111DC },
{ 0x11211, 0x11213 },
{ 0x11286, 0x1128A },
{ 0x1128D, 0x1128F },
{ 0x1129D, 0x1129F },
{ 0x11328, 0x1132A },
{ 0x11330, 0x11332 },
{ 0x11333, 0x11335 },
{ 0x114C5, 0x114C7 },
{ 0x11913, 0x11915 },
{ 0x11916, 0x11918 },
{ 0x1193F, 0x11941 },
{ 0x119E1, 0x119E3 },
{ 0x11C08, 0x11C0A },
{ 0x11D06, 0x11D08 },
{ 0x11D09, 0x11D0B },
{ 0x11D65, 0x11D67 },
{ 0x11D68, 0x11D6A },
{ 0x11F02, 0x11F04 },
{ 0x11F10, 0x11F12 },
{ 0x16FE1, 0x16FE3 },
{ 0x1AFF3, 0x1AFF5 },
{ 0x1AFFB, 0x1AFFD },
{ 0x1AFFE, 0x1B000 },
{ 0x1D454, 0x1D456 },
{ 0x1D49C, 0x1D49E },
{ 0x1D4AC, 0x1D4AE },
{ 0x1D4B9, 0x1D4BD },
{ 0x1D4C3, 0x1D4C5 },
{ 0x1D505, 0x1D507 },
{ 0x1D514, 0x1D516 },
{ 0x1D51C, 0x1D51E },
{ 0x1D539, 0x1D53B },
{ 0x1D53E, 0x1D540 },
{ 0x1D544, 0x1D546 },
{ 0x1D550, 0x1D552 },
{ 0x1D6C0, 0x1D6C2 },
{ 0x1D6DA, 0x1D6DC },
{ 0x1D6FA, 0x1D6FC },
{ 0x1D714, 0x1D716 },
{ 0x1D734, 0x1D736 },
{ 0x1D74E, 0x1D750 },
{ 0x1D76E, 0x1D770 },
{ 0x1D788, 0x1D78A },
{ 0x1D7A8, 0x1D7AA },
{ 0x1D7C2, 0x1D7C4 },
{ 0x1E7E6, 0x1E7E8 },
{ 0x1E7EB, 0x1E7ED },
{ 0x1E7EE, 0x1E7F0 },
{ 0x1E7FE, 0x1E800 },
{ 0x1EE03, 0x1EE05 },
{ 0x1EE1F, 0x1EE21 },
{ 0x1EE22, 0x1EE24 },
{ 0x1EE27, 0x1EE29 },
{ 0x1EE32, 0x1EE34 },
{ 0x1EE37, 0x1EE3B },
{ 0x1EE47, 0x1EE4D },
{ 0x1EE4F, 0x1EE51 },
{ 0x1EE52, 0x1EE54 },
{ 0x1EE57, 0x1EE61 },
{ 0x1EE62, 0x1EE64 },
{ 0x1EE6A, 0x1EE6C },
{ 0x1EE72, 0x1EE74 },
{ 0x1EE77, 0x1EE79 },
{ 0x1EE7C, 0x1EE80 },
{ 0x1EE89, 0x1EE8B },
{ 0x1EEA3, 0x1EEA5 },
{ 0x1EEA9, 0x1EEAB },
};
static const Rune alpha2[][2] = {
{ 0x0041, 0x005A },
{ 0x0061, 0x007A },
{ 0x00C0, 0x00D6 },
{ 0x00D8, 0x00F6 },
{ 0x00F8, 0x02C1 },
{ 0x02C6, 0x02D1 },
{ 0x02E0, 0x02E4 },
{ 0x0370, 0x0374 },
{ 0x0376, 0x0377 },
{ 0x037A, 0x037D },
{ 0x0388, 0x038A },
{ 0x038E, 0x03A1 },
{ 0x03A3, 0x03F5 },
{ 0x03F7, 0x0481 },
{ 0x048A, 0x052F },
{ 0x0531, 0x0556 },
{ 0x0560, 0x0588 },
{ 0x05D0, 0x05EA },
{ 0x05EF, 0x05F2 },
{ 0x0620, 0x064A },
{ 0x066E, 0x066F },
{ 0x0671, 0x06D3 },
{ 0x06E5, 0x06E6 },
{ 0x06EE, 0x06EF },
{ 0x06FA, 0x06FC },
{ 0x0712, 0x072F },
{ 0x074D, 0x07A5 },
{ 0x07CA, 0x07EA },
{ 0x07F4, 0x07F5 },
{ 0x0800, 0x0815 },
{ 0x0840, 0x0858 },
{ 0x0860, 0x086A },
{ 0x0870, 0x0887 },
{ 0x0889, 0x088E },
{ 0x08A0, 0x08C9 },
{ 0x0904, 0x0939 },
{ 0x0958, 0x0961 },
{ 0x0971, 0x0980 },
{ 0x0985, 0x098C },
{ 0x098F, 0x0990 },
{ 0x0993, 0x09A8 },
{ 0x09AA, 0x09B0 },
{ 0x09B6, 0x09B9 },
{ 0x09DC, 0x09DD },
{ 0x09DF, 0x09E1 },
{ 0x09F0, 0x09F1 },
{ 0x0A05, 0x0A0A },
{ 0x0A0F, 0x0A10 },
{ 0x0A13, 0x0A28 },
{ 0x0A2A, 0x0A30 },
{ 0x0A32, 0x0A33 },
{ 0x0A35, 0x0A36 },
{ 0x0A38, 0x0A39 },
{ 0x0A59, 0x0A5C },
{ 0x0A72, 0x0A74 },
{ 0x0A85, 0x0A8D },
{ 0x0A8F, 0x0A91 },
{ 0x0A93, 0x0AA8 },
{ 0x0AAA, 0x0AB0 },
{ 0x0AB2, 0x0AB3 },
{ 0x0AB5, 0x0AB9 },
{ 0x0AE0, 0x0AE1 },
{ 0x0B05, 0x0B0C },
{ 0x0B0F, 0x0B10 },
{ 0x0B13, 0x0B28 },
{ 0x0B2A, 0x0B30 },
{ 0x0B32, 0x0B33 },
{ 0x0B35, 0x0B39 },
{ 0x0B5C, 0x0B5D },
{ 0x0B5F, 0x0B61 },
{ 0x0B85, 0x0B8A },
{ 0x0B8E, 0x0B90 },
{ 0x0B92, 0x0B95 },
{ 0x0B99, 0x0B9A },
{ 0x0B9E, 0x0B9F },
{ 0x0BA3, 0x0BA4 },
{ 0x0BA8, 0x0BAA },
{ 0x0BAE, 0x0BB9 },
{ 0x0C05, 0x0C0C },
{ 0x0C0E, 0x0C10 },
{ 0x0C12, 0x0C28 },
{ 0x0C2A, 0x0C39 },
{ 0x0C58, 0x0C5A },
{ 0x0C60, 0x0C61 },
{ 0x0C85, 0x0C8C },
{ 0x0C8E, 0x0C90 },
{ 0x0C92, 0x0CA8 },
{ 0x0CAA, 0x0CB3 },
{ 0x0CB5, 0x0CB9 },
{ 0x0CDD, 0x0CDE },
{ 0x0CE0, 0x0CE1 },
{ 0x0CF1, 0x0CF2 },
{ 0x0D04, 0x0D0C },
{ 0x0D0E, 0x0D10 },
{ 0x0D12, 0x0D3A },
{ 0x0D54, 0x0D56 },
{ 0x0D5F, 0x0D61 },
{ 0x0D7A, 0x0D7F },
{ 0x0D85, 0x0D96 },
{ 0x0D9A, 0x0DB1 },
{ 0x0DB3, 0x0DBB },
{ 0x0DC0, 0x0DC6 },
{ 0x0E01, 0x0E30 },
{ 0x0E32, 0x0E33 },
{ 0x0E40, 0x0E46 },
{ 0x0E81, 0x0E82 },
{ 0x0E86, 0x0E8A },
{ 0x0E8C, 0x0EA3 },
{ 0x0EA7, 0x0EB0 },
{ 0x0EB2, 0x0EB3 },
{ 0x0EC0, 0x0EC4 },
{ 0x0EDC, 0x0EDF },
{ 0x0F40, 0x0F47 },
{ 0x0F49, 0x0F6C },
{ 0x0F88, 0x0F8C },
{ 0x1000, 0x102A },
{ 0x1050, 0x1055 },
{ 0x105A, 0x105D },
{ 0x1065, 0x1066 },
{ 0x106E, 0x1070 },
{ 0x1075, 0x1081 },
{ 0x10A0, 0x10C5 },
{ 0x10D0, 0x10FA },
{ 0x10FC, 0x1248 },
{ 0x124A, 0x124D },
{ 0x1250, 0x1256 },
{ 0x125A, 0x125D },
{ 0x1260, 0x1288 },
{ 0x128A, 0x128D },
{ 0x1290, 0x12B0 },
{ 0x12B2, 0x12B5 },
{ 0x12B8, 0x12BE },
{ 0x12C2, 0x12C5 },
{ 0x12C8, 0x12D6 },
{ 0x12D8, 0x1310 },
{ 0x1312, 0x1315 },
{ 0x1318, 0x135A },
{ 0x1380, 0x138F },
{ 0x13A0, 0x13F5 },
{ 0x13F8, 0x13FD },
{ 0x1401, 0x166C },
{ 0x166F, 0x167F },
{ 0x1681, 0x169A },
{ 0x16A0, 0x16EA },
{ 0x16F1, 0x16F8 },
{ 0x1700, 0x1711 },
{ 0x171F, 0x1731 },
{ 0x1740, 0x1751 },
{ 0x1760, 0x176C },
{ 0x176E, 0x1770 },
{ 0x1780, 0x17B3 },
{ 0x1820, 0x1878 },
{ 0x1880, 0x1884 },
{ 0x1887, 0x18A8 },
{ 0x18B0, 0x18F5 },
{ 0x1900, 0x191E },
{ 0x1950, 0x196D },
{ 0x1970, 0x1974 },
{ 0x1980, 0x19AB },
{ 0x19B0, 0x19C9 },
{ 0x1A00, 0x1A16 },
{ 0x1A20, 0x1A54 },
{ 0x1B05, 0x1B33 },
{ 0x1B45, 0x1B4C },
{ 0x1B83, 0x1BA0 },
{ 0x1BAE, 0x1BAF },
{ 0x1BBA, 0x1BE5 },
{ 0x1C00, 0x1C23 },
{ 0x1C4D, 0x1C4F },
{ 0x1C5A, 0x1C7D },
{ 0x1C80, 0x1C88 },
{ 0x1C90, 0x1CBA },
{ 0x1CBD, 0x1CBF },
{ 0x1CE9, 0x1CEC },
{ 0x1CEE, 0x1CF3 },
{ 0x1CF5, 0x1CF6 },
{ 0x1D00, 0x1DBF },
{ 0x1E00, 0x1F15 },
{ 0x1F18, 0x1F1D },
{ 0x1F20, 0x1F45 },
{ 0x1F48, 0x1F4D },
{ 0x1F50, 0x1F57 },
{ 0x1F5F, 0x1F7D },
{ 0x1F80, 0x1FB4 },
{ 0x1FB6, 0x1FBC },
{ 0x1FC2, 0x1FC4 },
{ 0x1FC6, 0x1FCC },
{ 0x1FD0, 0x1FD3 },
{ 0x1FD6, 0x1FDB },
{ 0x1FE0, 0x1FEC },
{ 0x1FF2, 0x1FF4 },
{ 0x1FF6, 0x1FFC },
{ 0x2090, 0x209C },
{ 0x210A, 0x2113 },
{ 0x2119, 0x211D },
{ 0x212A, 0x212D },
{ 0x212F, 0x2139 },
{ 0x213C, 0x213F },
{ 0x2145, 0x2149 },
{ 0x2183, 0x2184 },
{ 0x2C00, 0x2CE4 },
{ 0x2CEB, 0x2CEE },
{ 0x2CF2, 0x2CF3 },
{ 0x2D00, 0x2D25 },
{ 0x2D30, 0x2D67 },
{ 0x2D80, 0x2D96 },
{ 0x2DA0, 0x2DA6 },
{ 0x2DA8, 0x2DAE },
{ 0x2DB0, 0x2DB6 },
{ 0x2DB8, 0x2DBE },
{ 0x2DC0, 0x2DC6 },
{ 0x2DC8, 0x2DCE },
{ 0x2DD0, 0x2DD6 },
{ 0x2DD8, 0x2DDE },
{ 0x3005, 0x3006 },
{ 0x3031, 0x3035 },
{ 0x303B, 0x303C },
{ 0x3041, 0x3096 },
{ 0x309D, 0x309F },
{ 0x30A1, 0x30FA },
{ 0x30FC, 0x30FF },
{ 0x3105, 0x312F },
{ 0x3131, 0x318E },
{ 0x31A0, 0x31BF },
{ 0x31F0, 0x31FF },
{ 0x9FFF, 0xA48C },
{ 0xA4D0, 0xA4FD },
{ 0xA500, 0xA60C },
{ 0xA610, 0xA61F },
{ 0xA62A, 0xA62B },
{ 0xA640, 0xA66E },
{ 0xA67F, 0xA69D },
{ 0xA6A0, 0xA6E5 },
{ 0xA717, 0xA71F },
{ 0xA722, 0xA788 },
{ 0xA78B, 0xA7CA },
{ 0xA7D0, 0xA7D1 },
{ 0xA7D5, 0xA7D9 },
{ 0xA7F2, 0xA801 },
{ 0xA803, 0xA805 },
{ 0xA807, 0xA80A },
{ 0xA80C, 0xA822 },
{ 0xA840, 0xA873 },
{ 0xA882, 0xA8B3 },
{ 0xA8F2, 0xA8F7 },
{ 0xA8FD, 0xA8FE },
{ 0xA90A, 0xA925 },
{ 0xA930, 0xA946 },
{ 0xA960, 0xA97C },
{ 0xA984, 0xA9B2 },
{ 0xA9E0, 0xA9E4 },
{ 0xA9E6, 0xA9EF },
{ 0xA9FA, 0xA9FE },
{ 0xAA00, 0xAA28 },
{ 0xAA40, 0xAA42 },
{ 0xAA44, 0xAA4B },
{ 0xAA60, 0xAA76 },
{ 0xAA7E, 0xAAAF },
{ 0xAAB5, 0xAAB6 },
{ 0xAAB9, 0xAABD },
{ 0xAADB, 0xAADD },
{ 0xAAE0, 0xAAEA },
{ 0xAAF2, 0xAAF4 },
{ 0xAB01, 0xAB06 },
{ 0xAB09, 0xAB0E },
{ 0xAB11, 0xAB16 },
{ 0xAB20, 0xAB26 },
{ 0xAB28, 0xAB2E },
{ 0xAB30, 0xAB5A },
{ 0xAB5C, 0xAB69 },
{ 0xAB70, 0xABE2 },
{ 0xD7B0, 0xD7C6 },
{ 0xD7CB, 0xD7FB },
{ 0xF900, 0xFA6D },
{ 0xFA70, 0xFAD9 },
{ 0xFB00, 0xFB06 },
{ 0xFB13, 0xFB17 },
{ 0xFB1F, 0xFB28 },
{ 0xFB2A, 0xFB36 },
{ 0xFB38, 0xFB3C },
{ 0xFB40, 0xFB41 },
{ 0xFB43, 0xFB44 },
{ 0xFB46, 0xFBB1 },
{ 0xFBD3, 0xFD3D },
{ 0xFD50, 0xFD8F },
{ 0xFD92, 0xFDC7 },
{ 0xFDF0, 0xFDFB },
{ 0xFE70, 0xFE74 },
{ 0xFE76, 0xFEFC },
{ 0xFF21, 0xFF3A },
{ 0xFF41, 0xFF5A },
{ 0xFF66, 0xFFBE },
{ 0xFFC2, 0xFFC7 },
{ 0xFFCA, 0xFFCF },
{ 0xFFD2, 0xFFD7 },
{ 0xFFDA, 0xFFDC },
{ 0x10000, 0x1000B },
{ 0x1000D, 0x10026 },
{ 0x10028, 0x1003A },
{ 0x1003C, 0x1003D },
{ 0x1003F, 0x1004D },
{ 0x10050, 0x1005D },
{ 0x10080, 0x100FA },
{ 0x10280, 0x1029C },
{ 0x102A0, 0x102D0 },
{ 0x10300, 0x1031F },
{ 0x1032D, 0x10340 },
{ 0x10342, 0x10349 },
{ 0x10350, 0x10375 },
{ 0x10380, 0x1039D },
{ 0x103A0, 0x103C3 },
{ 0x103C8, 0x103CF },
{ 0x10400, 0x1049D },
{ 0x104B0, 0x104D3 },
{ 0x104D8, 0x104FB },
{ 0x10500, 0x10527 },
{ 0x10530, 0x10563 },
{ 0x10570, 0x1057A },
{ 0x1057C, 0x1058A },
{ 0x1058C, 0x10592 },
{ 0x10594, 0x10595 },
{ 0x10597, 0x105A1 },
{ 0x105A3, 0x105B1 },
{ 0x105B3, 0x105B9 },
{ 0x105BB, 0x105BC },
{ 0x10600, 0x10736 },
{ 0x10740, 0x10755 },
{ 0x10760, 0x10767 },
{ 0x10780, 0x10785 },
{ 0x10787, 0x107B0 },
{ 0x107B2, 0x107BA },
{ 0x10800, 0x10805 },
{ 0x1080A, 0x10835 },
{ 0x10837, 0x10838 },
{ 0x1083F, 0x10855 },
{ 0x10860, 0x10876 },
{ 0x10880, 0x1089E },
{ 0x108E0, 0x108F2 },
{ 0x108F4, 0x108F5 },
{ 0x10900, 0x10915 },
{ 0x10920, 0x10939 },
{ 0x10980, 0x109B7 },
{ 0x109BE, 0x109BF },
{ 0x10A10, 0x10A13 },
{ 0x10A15, 0x10A17 },
{ 0x10A19, 0x10A35 },
{ 0x10A60, 0x10A7C },
{ 0x10A80, 0x10A9C },
{ 0x10AC0, 0x10AC7 },
{ 0x10AC9, 0x10AE4 },
{ 0x10B00, 0x10B35 },
{ 0x10B40, 0x10B55 },
{ 0x10B60, 0x10B72 },
{ 0x10B80, 0x10B91 },
{ 0x10C00, 0x10C48 },
{ 0x10C80, 0x10CB2 },
{ 0x10CC0, 0x10CF2 },
{ 0x10D00, 0x10D23 },
{ 0x10E80, 0x10EA9 },
{ 0x10EB0, 0x10EB1 },
{ 0x10F00, 0x10F1C },
{ 0x10F30, 0x10F45 },
{ 0x10F70, 0x10F81 },
{ 0x10FB0, 0x10FC4 },
{ 0x10FE0, 0x10FF6 },
{ 0x11003, 0x11037 },
{ 0x11071, 0x11072 },
{ 0x11083, 0x110AF },
{ 0x110D0, 0x110E8 },
{ 0x11103, 0x11126 },
{ 0x11150, 0x11172 },
{ 0x11183, 0x111B2 },
{ 0x111C1, 0x111C4 },
{ 0x11200, 0x11211 },
{ 0x11213, 0x1122B },
{ 0x1123F, 0x11240 },
{ 0x11280, 0x11286 },
{ 0x1128A, 0x1128D },
{ 0x1128F, 0x1129D },
{ 0x1129F, 0x112A8 },
{ 0x112B0, 0x112DE },
{ 0x11305, 0x1130C },
{ 0x1130F, 0x11310 },
{ 0x11313, 0x11328 },
{ 0x1132A, 0x11330 },
{ 0x11332, 0x11333 },
{ 0x11335, 0x11339 },
{ 0x1135D, 0x11361 },
{ 0x11400, 0x11434 },
{ 0x11447, 0x1144A },
{ 0x1145F, 0x11461 },
{ 0x11480, 0x114AF },
{ 0x114C4, 0x114C5 },
{ 0x11580, 0x115AE },
{ 0x115D8, 0x115DB },
{ 0x11600, 0x1162F },
{ 0x11680, 0x116AA },
{ 0x11700, 0x1171A },
{ 0x11740, 0x11746 },
{ 0x11800, 0x1182B },
{ 0x118A0, 0x118DF },
{ 0x118FF, 0x11906 },
{ 0x1190C, 0x11913 },
{ 0x11915, 0x11916 },
{ 0x11918, 0x1192F },
{ 0x119A0, 0x119A7 },
{ 0x119AA, 0x119D0 },
{ 0x11A0B, 0x11A32 },
{ 0x11A5C, 0x11A89 },
{ 0x11AB0, 0x11AF8 },
{ 0x11C00, 0x11C08 },
{ 0x11C0A, 0x11C2E },
{ 0x11C72, 0x11C8F },
{ 0x11D00, 0x11D06 },
{ 0x11D08, 0x11D09 },
{ 0x11D0B, 0x11D30 },
{ 0x11D60, 0x11D65 },
{ 0x11D67, 0x11D68 },
{ 0x11D6A, 0x11D89 },
{ 0x11EE0, 0x11EF2 },
{ 0x11F04, 0x11F10 },
{ 0x11F12, 0x11F33 },
{ 0x12000, 0x12399 },
{ 0x12480, 0x12543 },
{ 0x12F90, 0x12FF0 },
{ 0x13000, 0x1342F },
{ 0x13441, 0x13446 },
{ 0x14400, 0x14646 },
{ 0x16800, 0x16A38 },
{ 0x16A40, 0x16A5E },
{ 0x16A70, 0x16ABE },
{ 0x16AD0, 0x16AED },
{ 0x16B00, 0x16B2F },
{ 0x16B40, 0x16B43 },
{ 0x16B63, 0x16B77 },
{ 0x16B7D, 0x16B8F },
{ 0x16E40, 0x16E7F },
{ 0x16F00, 0x16F4A },
{ 0x16F93, 0x16F9F },
{ 0x16FE0, 0x16FE1 },
{ 0x18800, 0x18CD5 },
{ 0x1AFF0, 0x1AFF3 },
{ 0x1AFF5, 0x1AFFB },
{ 0x1AFFD, 0x1AFFE },
{ 0x1B000, 0x1B122 },
{ 0x1B150, 0x1B152 },
{ 0x1B164, 0x1B167 },
{ 0x1B170, 0x1B2FB },
{ 0x1BC00, 0x1BC6A },
{ 0x1BC70, 0x1BC7C },
{ 0x1BC80, 0x1BC88 },
{ 0x1BC90, 0x1BC99 },
{ 0x1D400, 0x1D454 },
{ 0x1D456, 0x1D49C },
{ 0x1D49E, 0x1D49F },
{ 0x1D4A5, 0x1D4A6 },
{ 0x1D4A9, 0x1D4AC },
{ 0x1D4AE, 0x1D4B9 },
{ 0x1D4BD, 0x1D4C3 },
{ 0x1D4C5, 0x1D505 },
{ 0x1D507, 0x1D50A },
{ 0x1D50D, 0x1D514 },
{ 0x1D516, 0x1D51C },
{ 0x1D51E, 0x1D539 },
{ 0x1D53B, 0x1D53E },
{ 0x1D540, 0x1D544 },
{ 0x1D54A, 0x1D550 },
{ 0x1D552, 0x1D6A5 },
{ 0x1D6A8, 0x1D6C0 },
{ 0x1D6C2, 0x1D6DA },
{ 0x1D6DC, 0x1D6FA },
{ 0x1D6FC, 0x1D714 },
{ 0x1D716, 0x1D734 },
{ 0x1D736, 0x1D74E },
{ 0x1D750, 0x1D76E },
{ 0x1D770, 0x1D788 },
{ 0x1D78A, 0x1D7A8 },
{ 0x1D7AA, 0x1D7C2 },
{ 0x1D7C4, 0x1D7CB },
{ 0x1DF00, 0x1DF1E },
{ 0x1DF25, 0x1DF2A },
{ 0x1E030, 0x1E06D },
{ 0x1E100, 0x1E12C },
{ 0x1E137, 0x1E13D },
{ 0x1E290, 0x1E2AD },
{ 0x1E2C0, 0x1E2EB },
{ 0x1E4D0, 0x1E4EB },
{ 0x1E7E0, 0x1E7E6 },
{ 0x1E7E8, 0x1E7EB },
{ 0x1E7ED, 0x1E7EE },
{ 0x1E7F0, 0x1E7FE },
{ 0x1E800, 0x1E8C4 },
{ 0x1E900, 0x1E943 },
{ 0x1EE00, 0x1EE03 },
{ 0x1EE05, 0x1EE1F },
{ 0x1EE21, 0x1EE22 },
{ 0x1EE29, 0x1EE32 },
{ 0x1EE34, 0x1EE37 },
{ 0x1EE4D, 0x1EE4F },
{ 0x1EE51, 0x1EE52 },
{ 0x1EE61, 0x1EE62 },
{ 0x1EE67, 0x1EE6A },
{ 0x1EE6C, 0x1EE72 },
{ 0x1EE74, 0x1EE77 },
{ 0x1EE79, 0x1EE7C },
{ 0x1EE80, 0x1EE89 },
{ 0x1EE8B, 0x1EE9B },
{ 0x1EEA1, 0x1EEA3 },
{ 0x1EEA5, 0x1EEA9 },
{ 0x1EEAB, 0x1EEBB },
{ 0x2F800, 0x2FA1D },
};
static const Rune alpha1[] = {
0x00AA,
0x00B5,
0x00BA,
0x0559,
0x06FF,
0x07B1,
0x07FA,
0x081A,
0x0824,
0x0828,
0x093D,
0x0950,
0x09BD,
0x09CE,
0x09FC,
0x0ABD,
0x0AD0,
0x0AF9,
0x0B3D,
0x0B71,
0x0BD0,
0x0C3D,
0x0C5D,
0x0C80,
0x0CBD,
0x0D3D,
0x0D4E,
0x0EBD,
0x0F00,
0x103F,
0x1061,
0x108E,
0x10CD,
0x17D7,
0x17DC,
0x1AA7,
0x1CFA,
0x2071,
0x207F,
0x2102,
0x2107,
0x214E,
0x2D2D,
0x2D6F,
0x2E2F,
0x3400,
0x4DBF,
0x4E00,
0xA9CF,
0xAA7A,
0xAC00,
0xD7A3,
0x1083C,
0x10A00,
0x10F27,
0x11075,
0x11144,
0x11147,
0x11176,
0x1133D,
0x11350,
0x11644,
0x116B8,
0x11909,
0x11A00,
0x11A3A,
0x11A50,
0x11A9D,
0x11C40,
0x11D46,
0x11D98,
0x11FB0,
0x16F50,
0x17000,
0x187F7,
0x18D00,
0x18D08,
0x1B132,
0x1B155,
0x1D4A2,
0x1E14E,
0x1E94B,
0x1EE42,
0x20000,
0x2A6DF,
0x2A700,
0x2B739,
0x2B740,
0x2B81D,
0x2B820,
0x2CEA1,
0x2CEB0,
0x2EBE0,
0x30000,
0x3134A,
0x31350,
0x323AF,
};
int
isalpharune(Rune r)
{
const Rune *match;
if((match = bsearch(&r, alpha3, nelem(alpha3), sizeof *alpha3, &rune2cmp)))
return !((r - match[0]) % 2);
if(bsearch(&r, alpha2, nelem(alpha2), sizeof *alpha2, &rune2cmp))
return 1;
if(bsearch(&r, alpha1, nelem(alpha1), sizeof *alpha1, &rune1cmp))
return 1;
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
/* Automatically generated by mkrunetype.awk */
#include "../utf.h"
#include "runetype.h"
int
isblankrune(Rune r)
{
return r == ' ' || r == '\t';
}
+18
View File
@@ -0,0 +1,18 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune cntrl2[][2] = {
{ 0x0000, 0x001F },
{ 0x007F, 0x009F },
};
int
iscntrlrune(Rune r)
{
if(bsearch(&r, cntrl2, nelem(cntrl2), sizeof *cntrl2, &rune2cmp))
return 1;
return 0;
}
+80
View File
@@ -0,0 +1,80 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune digit2[][2] = {
{ 0x0030, 0x0039 },
{ 0x0660, 0x0669 },
{ 0x06F0, 0x06F9 },
{ 0x07C0, 0x07C9 },
{ 0x0966, 0x096F },
{ 0x09E6, 0x09EF },
{ 0x0A66, 0x0A6F },
{ 0x0AE6, 0x0AEF },
{ 0x0B66, 0x0B6F },
{ 0x0BE6, 0x0BEF },
{ 0x0C66, 0x0C6F },
{ 0x0CE6, 0x0CEF },
{ 0x0D66, 0x0D6F },
{ 0x0DE6, 0x0DEF },
{ 0x0E50, 0x0E59 },
{ 0x0ED0, 0x0ED9 },
{ 0x0F20, 0x0F29 },
{ 0x1040, 0x1049 },
{ 0x1090, 0x1099 },
{ 0x17E0, 0x17E9 },
{ 0x1810, 0x1819 },
{ 0x1946, 0x194F },
{ 0x19D0, 0x19D9 },
{ 0x1A80, 0x1A89 },
{ 0x1A90, 0x1A99 },
{ 0x1B50, 0x1B59 },
{ 0x1BB0, 0x1BB9 },
{ 0x1C40, 0x1C49 },
{ 0x1C50, 0x1C59 },
{ 0xA620, 0xA629 },
{ 0xA8D0, 0xA8D9 },
{ 0xA900, 0xA909 },
{ 0xA9D0, 0xA9D9 },
{ 0xA9F0, 0xA9F9 },
{ 0xAA50, 0xAA59 },
{ 0xABF0, 0xABF9 },
{ 0xFF10, 0xFF19 },
{ 0x104A0, 0x104A9 },
{ 0x10D30, 0x10D39 },
{ 0x11066, 0x1106F },
{ 0x110F0, 0x110F9 },
{ 0x11136, 0x1113F },
{ 0x111D0, 0x111D9 },
{ 0x112F0, 0x112F9 },
{ 0x11450, 0x11459 },
{ 0x114D0, 0x114D9 },
{ 0x11650, 0x11659 },
{ 0x116C0, 0x116C9 },
{ 0x11730, 0x11739 },
{ 0x118E0, 0x118E9 },
{ 0x11950, 0x11959 },
{ 0x11C50, 0x11C59 },
{ 0x11D50, 0x11D59 },
{ 0x11DA0, 0x11DA9 },
{ 0x11F50, 0x11F59 },
{ 0x16A60, 0x16A69 },
{ 0x16AC0, 0x16AC9 },
{ 0x16B50, 0x16B59 },
{ 0x1D7CE, 0x1D7FF },
{ 0x1E140, 0x1E149 },
{ 0x1E2F0, 0x1E2F9 },
{ 0x1E4F0, 0x1E4F9 },
{ 0x1E950, 0x1E959 },
{ 0x1FBF0, 0x1FBF9 },
};
int
isdigitrune(Rune r)
{
if(bsearch(&r, digit2, nelem(digit2), sizeof *digit2, &rune2cmp))
return 1;
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
/* Automatically generated by mkrunetype.awk */
#include "../utf.h"
#include "runetype.h"
int
isgraphrune(Rune r)
{
return !isspacerune(r) && isprintrune(r);
}
+10
View File
@@ -0,0 +1,10 @@
/* Automatically generated by mkrunetype.awk */
#include "../utf.h"
#include "runetype.h"
int
isprintrune(Rune r)
{
return !iscntrlrune(r) && (r != 0x2028) && (r != 0x2029) &&
((r < 0xFFF9) || (r > 0xFFFB));
}
+9
View File
@@ -0,0 +1,9 @@
/* Automatically generated by mkrunetype.awk */
#include "../utf.h"
#include "runetype.h"
int
ispunctrune(Rune r)
{
return isgraphrune(r) && !isalnumrune(r);
}
+31
View File
@@ -0,0 +1,31 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune space2[][2] = {
{ 0x0009, 0x000D },
{ 0x001C, 0x0020 },
{ 0x2000, 0x200A },
{ 0x2028, 0x2029 },
};
static const Rune space1[] = {
0x0085,
0x00A0,
0x1680,
0x202F,
0x205F,
0x3000,
};
int
isspacerune(Rune r)
{
if(bsearch(&r, space2, nelem(space2), sizeof *space2, &rune2cmp))
return 1;
if(bsearch(&r, space1, nelem(space1), sizeof *space1, &rune1cmp))
return 1;
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune title2[][2] = {
{ 0x1F88, 0x1F8F },
{ 0x1F98, 0x1F9F },
{ 0x1FA8, 0x1FAF },
};
static const Rune title1[] = {
0x01C5,
0x01C8,
0x01CB,
0x01F2,
0x1FBC,
0x1FCC,
0x1FFC,
};
int
istitlerune(Rune r)
{
if(bsearch(&r, title2, nelem(title2), sizeof *title2, &rune2cmp))
return 1;
if(bsearch(&r, title1, nelem(title1), sizeof *title1, &rune1cmp))
return 1;
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
/* Automatically generated by mkrunetype.awk */
#include "../utf.h"
#include "runetype.h"
int
isxdigitrune(Rune r)
{
return (r >= '0' && (r - '0') < 10) || (r >= 'a' && (r - 'a') < 6);
}
+356
View File
@@ -0,0 +1,356 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune lower4[][2] = {
{ 0x0101, 0x012F },
{ 0x0133, 0x0137 },
{ 0x013A, 0x0148 },
{ 0x014B, 0x0177 },
{ 0x017A, 0x017E },
{ 0x0183, 0x0185 },
{ 0x01A1, 0x01A5 },
{ 0x01B4, 0x01B6 },
{ 0x01CE, 0x01DC },
{ 0x01DF, 0x01EF },
{ 0x01F9, 0x021F },
{ 0x0223, 0x0233 },
{ 0x0247, 0x024F },
{ 0x0371, 0x0373 },
{ 0x03D9, 0x03EF },
{ 0x0461, 0x0481 },
{ 0x048B, 0x04BF },
{ 0x04C2, 0x04CE },
{ 0x04D1, 0x052F },
{ 0x1E01, 0x1E95 },
{ 0x1EA1, 0x1EFF },
{ 0x2C68, 0x2C6C },
{ 0x2C81, 0x2CE3 },
{ 0x2CEC, 0x2CEE },
{ 0xA641, 0xA66D },
{ 0xA681, 0xA69B },
{ 0xA723, 0xA72F },
{ 0xA733, 0xA76F },
{ 0xA77A, 0xA77C },
{ 0xA77F, 0xA787 },
{ 0xA791, 0xA793 },
{ 0xA797, 0xA7A9 },
{ 0xA7B5, 0xA7C3 },
{ 0xA7C8, 0xA7CA },
{ 0xA7D7, 0xA7D9 },
};
static const Rune lower2[][3] = {
{ 0x0061, 0x007A, 0x0041 },
{ 0x00E0, 0x00F6, 0x00C0 },
{ 0x00F8, 0x00FE, 0x00D8 },
{ 0x01AA, 0x01AB, 0x01AA },
{ 0x0234, 0x0239, 0x0234 },
{ 0x023F, 0x0240, 0x2C7E },
{ 0x0256, 0x0257, 0x0189 },
{ 0x025D, 0x025F, 0x025D },
{ 0x026D, 0x026E, 0x026D },
{ 0x0273, 0x0274, 0x0273 },
{ 0x0276, 0x027C, 0x0276 },
{ 0x027E, 0x027F, 0x027E },
{ 0x0284, 0x0286, 0x0284 },
{ 0x028A, 0x028B, 0x01B1 },
{ 0x028D, 0x0291, 0x028D },
{ 0x0295, 0x029C, 0x0295 },
{ 0x029F, 0x02AF, 0x029F },
{ 0x037B, 0x037D, 0x03FD },
{ 0x03AD, 0x03AF, 0x0388 },
{ 0x03B1, 0x03C1, 0x0391 },
{ 0x03C3, 0x03CB, 0x03A3 },
{ 0x03CD, 0x03CE, 0x038E },
{ 0x0430, 0x044F, 0x0410 },
{ 0x0450, 0x045F, 0x0400 },
{ 0x0561, 0x0586, 0x0531 },
{ 0x0587, 0x0588, 0x0587 },
{ 0x10D0, 0x10FA, 0x1C90 },
{ 0x10FD, 0x10FF, 0x1CBD },
{ 0x13F8, 0x13FD, 0x13F0 },
{ 0x1C83, 0x1C84, 0x0421 },
{ 0x1D00, 0x1D2B, 0x1D00 },
{ 0x1D6B, 0x1D77, 0x1D6B },
{ 0x1D7A, 0x1D7C, 0x1D7A },
{ 0x1D7E, 0x1D8D, 0x1D7E },
{ 0x1D8F, 0x1D9A, 0x1D8F },
{ 0x1E96, 0x1E9A, 0x1E96 },
{ 0x1E9C, 0x1E9D, 0x1E9C },
{ 0x1F00, 0x1F07, 0x1F08 },
{ 0x1F10, 0x1F15, 0x1F18 },
{ 0x1F20, 0x1F27, 0x1F28 },
{ 0x1F30, 0x1F37, 0x1F38 },
{ 0x1F40, 0x1F45, 0x1F48 },
{ 0x1F60, 0x1F67, 0x1F68 },
{ 0x1F70, 0x1F71, 0x1FBA },
{ 0x1F72, 0x1F75, 0x1FC8 },
{ 0x1F76, 0x1F77, 0x1FDA },
{ 0x1F78, 0x1F79, 0x1FF8 },
{ 0x1F7A, 0x1F7B, 0x1FEA },
{ 0x1F7C, 0x1F7D, 0x1FFA },
{ 0x1F80, 0x1F87, 0x1F88 },
{ 0x1F90, 0x1F97, 0x1F98 },
{ 0x1FA0, 0x1FA7, 0x1FA8 },
{ 0x1FB0, 0x1FB1, 0x1FB8 },
{ 0x1FB6, 0x1FB7, 0x1FB6 },
{ 0x1FC6, 0x1FC7, 0x1FC6 },
{ 0x1FD0, 0x1FD1, 0x1FD8 },
{ 0x1FD2, 0x1FD3, 0x1FD2 },
{ 0x1FD6, 0x1FD7, 0x1FD6 },
{ 0x1FE0, 0x1FE1, 0x1FE8 },
{ 0x1FE2, 0x1FE4, 0x1FE2 },
{ 0x1FE6, 0x1FE7, 0x1FE6 },
{ 0x1FF6, 0x1FF7, 0x1FF6 },
{ 0x210E, 0x210F, 0x210E },
{ 0x213C, 0x213D, 0x213C },
{ 0x2146, 0x2149, 0x2146 },
{ 0x2C30, 0x2C5F, 0x2C00 },
{ 0x2C77, 0x2C7B, 0x2C77 },
{ 0x2D00, 0x2D25, 0x10A0 },
{ 0xA730, 0xA731, 0xA730 },
{ 0xA771, 0xA778, 0xA771 },
{ 0xAB30, 0xAB52, 0xAB30 },
{ 0xAB54, 0xAB5A, 0xAB54 },
{ 0xAB60, 0xAB68, 0xAB60 },
{ 0xAB70, 0xABBF, 0x13A0 },
{ 0xFB00, 0xFB06, 0xFB00 },
{ 0xFB13, 0xFB17, 0xFB13 },
{ 0xFF41, 0xFF5A, 0xFF21 },
{ 0x10428, 0x1044F, 0x10400 },
{ 0x104D8, 0x104FB, 0x104B0 },
{ 0x10597, 0x105A1, 0x10570 },
{ 0x105A3, 0x105B1, 0x1057C },
{ 0x105B3, 0x105B9, 0x1058C },
{ 0x105BB, 0x105BC, 0x10594 },
{ 0x10CC0, 0x10CF2, 0x10C80 },
{ 0x118C0, 0x118DF, 0x118A0 },
{ 0x16E60, 0x16E7F, 0x16E40 },
{ 0x1D41A, 0x1D433, 0x1D41A },
{ 0x1D44E, 0x1D454, 0x1D44E },
{ 0x1D456, 0x1D467, 0x1D456 },
{ 0x1D482, 0x1D49B, 0x1D482 },
{ 0x1D4B6, 0x1D4B9, 0x1D4B6 },
{ 0x1D4BD, 0x1D4C3, 0x1D4BD },
{ 0x1D4C5, 0x1D4CF, 0x1D4C5 },
{ 0x1D4EA, 0x1D503, 0x1D4EA },
{ 0x1D51E, 0x1D537, 0x1D51E },
{ 0x1D552, 0x1D56B, 0x1D552 },
{ 0x1D586, 0x1D59F, 0x1D586 },
{ 0x1D5BA, 0x1D5D3, 0x1D5BA },
{ 0x1D5EE, 0x1D607, 0x1D5EE },
{ 0x1D622, 0x1D63B, 0x1D622 },
{ 0x1D656, 0x1D66F, 0x1D656 },
{ 0x1D68A, 0x1D6A5, 0x1D68A },
{ 0x1D6C2, 0x1D6DA, 0x1D6C2 },
{ 0x1D6DC, 0x1D6E1, 0x1D6DC },
{ 0x1D6FC, 0x1D714, 0x1D6FC },
{ 0x1D716, 0x1D71B, 0x1D716 },
{ 0x1D736, 0x1D74E, 0x1D736 },
{ 0x1D750, 0x1D755, 0x1D750 },
{ 0x1D770, 0x1D788, 0x1D770 },
{ 0x1D78A, 0x1D78F, 0x1D78A },
{ 0x1D7AA, 0x1D7C2, 0x1D7AA },
{ 0x1D7C4, 0x1D7C9, 0x1D7C4 },
{ 0x1DF00, 0x1DF09, 0x1DF00 },
{ 0x1DF0B, 0x1DF1E, 0x1DF0B },
{ 0x1DF25, 0x1DF2A, 0x1DF25 },
{ 0x1E922, 0x1E943, 0x1E900 },
};
static const Rune lower1[][2] = {
{ 0x00B5, 0x039C },
{ 0x00DF, 0x00DF },
{ 0x00FF, 0x0178 },
{ 0x0131, 0x0049 },
{ 0x0138, 0x0138 },
{ 0x0149, 0x0149 },
{ 0x017F, 0x0053 },
{ 0x0180, 0x0243 },
{ 0x0188, 0x0187 },
{ 0x018C, 0x018B },
{ 0x018D, 0x018D },
{ 0x0192, 0x0191 },
{ 0x0195, 0x01F6 },
{ 0x0199, 0x0198 },
{ 0x019A, 0x023D },
{ 0x019B, 0x019B },
{ 0x019E, 0x0220 },
{ 0x01A8, 0x01A7 },
{ 0x01AD, 0x01AC },
{ 0x01B0, 0x01AF },
{ 0x01B9, 0x01B8 },
{ 0x01BA, 0x01BA },
{ 0x01BD, 0x01BC },
{ 0x01BE, 0x01BE },
{ 0x01BF, 0x01F7 },
{ 0x01C6, 0x01C4 },
{ 0x01C9, 0x01C7 },
{ 0x01CC, 0x01CA },
{ 0x01DD, 0x018E },
{ 0x01F0, 0x01F0 },
{ 0x01F3, 0x01F1 },
{ 0x01F5, 0x01F4 },
{ 0x0221, 0x0221 },
{ 0x023C, 0x023B },
{ 0x0242, 0x0241 },
{ 0x0250, 0x2C6F },
{ 0x0251, 0x2C6D },
{ 0x0252, 0x2C70 },
{ 0x0253, 0x0181 },
{ 0x0254, 0x0186 },
{ 0x0255, 0x0255 },
{ 0x0258, 0x0258 },
{ 0x0259, 0x018F },
{ 0x025A, 0x025A },
{ 0x025B, 0x0190 },
{ 0x025C, 0xA7AB },
{ 0x0260, 0x0193 },
{ 0x0261, 0xA7AC },
{ 0x0262, 0x0262 },
{ 0x0263, 0x0194 },
{ 0x0264, 0x0264 },
{ 0x0265, 0xA78D },
{ 0x0266, 0xA7AA },
{ 0x0267, 0x0267 },
{ 0x0268, 0x0197 },
{ 0x0269, 0x0196 },
{ 0x026A, 0xA7AE },
{ 0x026B, 0x2C62 },
{ 0x026C, 0xA7AD },
{ 0x026F, 0x019C },
{ 0x0270, 0x0270 },
{ 0x0271, 0x2C6E },
{ 0x0272, 0x019D },
{ 0x0275, 0x019F },
{ 0x027D, 0x2C64 },
{ 0x0280, 0x01A6 },
{ 0x0281, 0x0281 },
{ 0x0282, 0xA7C5 },
{ 0x0283, 0x01A9 },
{ 0x0287, 0xA7B1 },
{ 0x0288, 0x01AE },
{ 0x0289, 0x0244 },
{ 0x028C, 0x0245 },
{ 0x0292, 0x01B7 },
{ 0x0293, 0x0293 },
{ 0x029D, 0xA7B2 },
{ 0x029E, 0xA7B0 },
{ 0x0377, 0x0376 },
{ 0x0390, 0x0390 },
{ 0x03AC, 0x0386 },
{ 0x03B0, 0x03B0 },
{ 0x03C2, 0x03A3 },
{ 0x03CC, 0x038C },
{ 0x03D0, 0x0392 },
{ 0x03D1, 0x0398 },
{ 0x03D5, 0x03A6 },
{ 0x03D6, 0x03A0 },
{ 0x03D7, 0x03CF },
{ 0x03F0, 0x039A },
{ 0x03F1, 0x03A1 },
{ 0x03F2, 0x03F9 },
{ 0x03F3, 0x037F },
{ 0x03F5, 0x0395 },
{ 0x03F8, 0x03F7 },
{ 0x03FB, 0x03FA },
{ 0x03FC, 0x03FC },
{ 0x04CF, 0x04C0 },
{ 0x0560, 0x0560 },
{ 0x1C80, 0x0412 },
{ 0x1C81, 0x0414 },
{ 0x1C82, 0x041E },
{ 0x1C85, 0x0422 },
{ 0x1C86, 0x042A },
{ 0x1C87, 0x0462 },
{ 0x1C88, 0xA64A },
{ 0x1D79, 0xA77D },
{ 0x1D7D, 0x2C63 },
{ 0x1D8E, 0xA7C6 },
{ 0x1E9B, 0x1E60 },
{ 0x1E9F, 0x1E9F },
{ 0x1F50, 0x1F50 },
{ 0x1F51, 0x1F59 },
{ 0x1F52, 0x1F52 },
{ 0x1F53, 0x1F5B },
{ 0x1F54, 0x1F54 },
{ 0x1F55, 0x1F5D },
{ 0x1F56, 0x1F56 },
{ 0x1F57, 0x1F5F },
{ 0x1FB2, 0x1FB2 },
{ 0x1FB3, 0x1FBC },
{ 0x1FB4, 0x1FB4 },
{ 0x1FBE, 0x0399 },
{ 0x1FC2, 0x1FC2 },
{ 0x1FC3, 0x1FCC },
{ 0x1FC4, 0x1FC4 },
{ 0x1FE5, 0x1FEC },
{ 0x1FF2, 0x1FF2 },
{ 0x1FF3, 0x1FFC },
{ 0x1FF4, 0x1FF4 },
{ 0x210A, 0x210A },
{ 0x2113, 0x2113 },
{ 0x212F, 0x212F },
{ 0x2134, 0x2134 },
{ 0x2139, 0x2139 },
{ 0x214E, 0x2132 },
{ 0x2184, 0x2183 },
{ 0x2C61, 0x2C60 },
{ 0x2C65, 0x023A },
{ 0x2C66, 0x023E },
{ 0x2C71, 0x2C71 },
{ 0x2C73, 0x2C72 },
{ 0x2C74, 0x2C74 },
{ 0x2C76, 0x2C75 },
{ 0x2CE4, 0x2CE4 },
{ 0x2CF3, 0x2CF2 },
{ 0x2D27, 0x10C7 },
{ 0x2D2D, 0x10CD },
{ 0xA78C, 0xA78B },
{ 0xA78E, 0xA78E },
{ 0xA794, 0xA7C4 },
{ 0xA795, 0xA795 },
{ 0xA7AF, 0xA7AF },
{ 0xA7D1, 0xA7D0 },
{ 0xA7D3, 0xA7D3 },
{ 0xA7D5, 0xA7D5 },
{ 0xA7F6, 0xA7F5 },
{ 0xA7FA, 0xA7FA },
{ 0xAB53, 0xA7B3 },
{ 0x1D4BB, 0x1D4BB },
{ 0x1D7CB, 0x1D7CB },
};
int
islowerrune(Rune r)
{
const Rune *match;
if((match = bsearch(&r, lower4, nelem(lower4), sizeof *lower4, &rune2cmp)))
return !((r - match[0]) % 2);
if(bsearch(&r, lower2, nelem(lower2), sizeof *lower2, &rune2cmp))
return 1;
if(bsearch(&r, lower1, nelem(lower1), sizeof *lower1, &rune1cmp))
return 1;
return 0;
}
int
toupperrune(Rune r)
{
Rune *match;
match = bsearch(&r, lower4, nelem(lower4), sizeof *lower4, &rune2cmp);
if (match)
return ((r - match[0]) % 2) ? r : r - 1;
match = bsearch(&r, lower2, nelem(lower2), sizeof *lower2, &rune2cmp);
if (match)
return match[2] + (r - match[0]);
match = bsearch(&r, lower1, nelem(lower1), sizeof *lower1, &rune1cmp);
if (match)
return match[1];
return r;
}
+240
View File
@@ -0,0 +1,240 @@
# See LICENSE file for copyright and license details.
BEGIN {
FS = ";"
# set up hexadecimal lookup table
for(i = 0; i < 16; i++)
hex[sprintf("%X",i)] = i;
HEADER = "/* Automatically generated by mkrunetype.awk */\n#include <stdlib.h>\n\n#include \"../utf.h\"\n#include \"runetype.h\"\n"
HEADER_OTHER = "/* Automatically generated by mkrunetype.awk */\n#include \"../utf.h\"\n#include \"runetype.h\"\n"
}
$3 ~ /^L/ { alphav[alphac++] = $1; }
($3 ~ /^Z/) || ($5 == "WS") || ($5 == "S") || ($5 == "B") { spacev[spacec++] = $1; }
$3 == "Cc" { cntrlv[cntrlc++] = $1; }
$3 == "Lu" { upperv[upperc++] = $1; tolowerv[uppercc++] = ($14 == "") ? $1 : $14; }
$3 == "Ll" { lowerv[lowerc++] = $1; toupperv[lowercc++] = ($13 == "") ? $1 : $13; }
$3 == "Lt" { titlev[titlec++] = $1; }
$3 == "Nd" { digitv[digitc++] = $1; }
END {
system("rm -f isalpharune.c isspacerune.c iscntrlrune.c upperrune.c lowerrune.c istitlerune.c isdigitrune.c");
mkis("alpha", alphav, alphac, "isalpharune.c", q, "");
mkis("space", spacev, spacec, "isspacerune.c", q, "");
mkis("cntrl", cntrlv, cntrlc, "iscntrlrune.c", q, "");
mkis("upper", upperv, upperc, "upperrune.c", tolowerv, "lower");
mkis("lower", lowerv, lowerc, "lowerrune.c", toupperv, "upper");
mkis("title", titlev, titlec, "istitlerune.c", q, "");
mkis("digit", digitv, digitc, "isdigitrune.c", q, "");
system("rm -f isalnumrune.c isblankrune.c isprintrune.c isgraphrune.c ispunctrune.c isxdigitrune.c");
otheris();
}
# parse hexadecimal rune index to int
function code(s) {
x = 0;
for(i = 1; i <= length(s); i++) {
c = substr(s, i, 1);
x = (x*16) + hex[c];
}
return x;
}
# generate 'is<name>rune' unicode lookup function
function mkis(name, runev, runec, file, casev, casename) {
rune1c = 0;
rune2c = 0;
rune3c = 0;
rune4c = 0;
mode = 1;
#sort rune groups into singletons, ranges and laces
for(j = 0; j < runec; j++) {
# range
if(code(runev[j+1]) == code(runev[j])+1 && ((length(casev) == 0) ||
code(casev[j+1]) == code(casev[j])+1) && j+1 < runec) {
if (mode == 2) {
continue;
} else if (mode == 3) {
rune3v1[rune3c] = runev[j];
rune3c++;
} else if (mode == 4) {
rune4v1[rune4c] = runev[j];
rune4c++;
}
mode = 2;
rune2v0[rune2c] = runev[j];
if(length(casev) > 0) {
case2v[rune2c] = casev[j];
}
continue;
}
# lace 1
if(code(runev[j+1]) == code(runev[j])+2 && ((length(casev) == 0) ||
(code(casev[j+1]) == code(runev[j+1])+1 && code(casev[j]) == code(runev[j])+1)) &&
j+1 < runec) {
if (mode == 3) {
continue;
} else if (mode == 2) {
rune2v1[rune2c] = runev[j];
rune2c++;
} else if (mode == 4) {
rune4v1[rune2c] = runev[j];
rune4c++;
}
mode = 3;
rune3v0[rune3c] = runev[j];
continue;
}
# lace 2
if(code(runev[j+1]) == code(runev[j])+2 && ((length(casev) == 0) ||
(code(casev[j+1]) == code(runev[j+1])-1 && code(casev[j]) == code(runev[j])-1)) &&
j+1 < runec) {
if (mode == 4) {
continue;
} else if (mode == 2) {
rune2v1[rune2c] = runev[j];
rune2c++;
} else if (mode == 3) {
rune3v1[rune2c] = runev[j];
rune3c++;
}
mode = 4;
rune4v0[rune4c] = runev[j];
continue;
}
# terminating case
if (mode == 1) {
rune1v[rune1c] = runev[j];
if (length(casev) > 0) {
case1v[rune1c] = casev[j];
}
rune1c++;
} else if (mode == 2) {
rune2v1[rune2c] = runev[j];
rune2c++;
} else if (mode == 3) {
rune3v1[rune3c] = runev[j];
rune3c++;
} else { #lace 2
rune4v1[rune4c] = runev[j];
rune4c++;
}
mode = 1;
}
print HEADER > file;
#generate list of laces 1
if(rune3c > 0) {
print "static const Rune "name"3[][2] = {" > file;
for(j = 0; j < rune3c; j++) {
print "\t{ 0x"rune3v0[j]", 0x"rune3v1[j]" }," > file;
}
print "};\n" > file;
}
#generate list of laces 2
if(rune4c > 0) {
print "static const Rune "name"4[][2] = {" > file;
for(j = 0; j < rune4c; j++) {
print "\t{ 0x"rune4v0[j]", 0x"rune4v1[j]" }," > file;
}
print "};\n" > file;
}
# generate list of ranges
if(rune2c > 0) {
if(length(casev) > 0) {
print "static const Rune "name"2[][3] = {" > file;
for(j = 0; j < rune2c; j++) {
print "\t{ 0x"rune2v0[j]", 0x"rune2v1[j]", 0x"case2v[j]" }," > file;
}
} else {
print "static const Rune "name"2[][2] = {" > file
for(j = 0; j < rune2c; j++) {
print "\t{ 0x"rune2v0[j]", 0x"rune2v1[j]" }," > file;
}
}
print "};\n" > file;
}
# generate list of singletons
if(rune1c > 0) {
if(length(casev) > 0) {
print "static const Rune "name"1[][2] = {" > file;
for(j = 0; j < rune1c; j++) {
print "\t{ 0x"rune1v[j]", 0x"case1v[j]" }," > file;
}
} else {
print "static const Rune "name"1[] = {" > file;
for(j = 0; j < rune1c; j++) {
print "\t0x"rune1v[j]"," > file;
}
}
print "};\n" > file;
}
# generate lookup function
print "int\nis"name"rune(Rune r)\n{" > file;
if(rune4c > 0 || rune3c > 0)
print "\tconst Rune *match;\n" > file;
if(rune4c > 0) {
print "\tif((match = bsearch(&r, "name"4, nelem("name"4), sizeof *"name"4, &rune2cmp)))" > file;
print "\t\treturn !((r - match[0]) % 2);" > file;
}
if(rune3c > 0) {
print "\tif((match = bsearch(&r, "name"3, nelem("name"3), sizeof *"name"3, &rune2cmp)))" > file;
print "\t\treturn !((r - match[0]) % 2);" > file;
}
if(rune2c > 0) {
print "\tif(bsearch(&r, "name"2, nelem("name"2), sizeof *"name"2, &rune2cmp))\n\t\treturn 1;" > file;
}
if(rune1c > 0) {
print "\tif(bsearch(&r, "name"1, nelem("name"1), sizeof *"name"1, &rune1cmp))\n\t\treturn 1;" > file;
}
print "\treturn 0;\n}" > file;
# generate case conversion function
if(length(casev) > 0) {
print "\nint\nto"casename"rune(Rune r)\n{\n\tRune *match;\n" > file;
if(rune4c > 0) {
print "\tmatch = bsearch(&r, "name"4, nelem("name"4), sizeof *"name"4, &rune2cmp);" > file;
print "\tif (match)" > file;
print "\t\treturn ((r - match[0]) % 2) ? r : r - 1;" > file;
}
if(rune3c > 0) {
print "\tmatch = bsearch(&r, "name"3, nelem("name"3), sizeof *"name"3, &rune2cmp);" > file;
print "\tif (match)" > file;
print "\t\treturn ((r - match[0]) % 2) ? r : r + 1;" > file;
}
if(rune2c > 0) {
print "\tmatch = bsearch(&r, "name"2, nelem("name"2), sizeof *"name"2, &rune2cmp);" > file;
print "\tif (match)" > file;
print "\t\treturn match[2] + (r - match[0]);" > file;
}
if(rune1c > 0) {
print "\tmatch = bsearch(&r, "name"1, nelem("name"1), sizeof *"name"1, &rune1cmp);" > file;
print "\tif (match)" > file;
print "\t\treturn match[1];" > file;
}
print "\treturn r;\n}" > file;
}
}
function otheris() {
print HEADER_OTHER > "isalnumrune.c";
print "int\nisalnumrune(Rune r)\n{\n\treturn isalpharune(r) || isdigitrune(r);\n}" > "isalnumrune.c";
print HEADER_OTHER > "isblankrune.c";
print "int\nisblankrune(Rune r)\n{\n\treturn r == ' ' || r == '\\t';\n}" > "isblankrune.c";
print HEADER_OTHER > "isprintrune.c";
print "int\nisprintrune(Rune r)\n{\n\treturn !iscntrlrune(r) && (r != 0x2028) && (r != 0x2029) &&" > "isprintrune.c";
print "\t ((r < 0xFFF9) || (r > 0xFFFB));\n}" > "isprintrune.c";
print HEADER_OTHER > "isgraphrune.c";
print "int\nisgraphrune(Rune r)\n{\n\treturn !isspacerune(r) && isprintrune(r);\n}" > "isgraphrune.c";
print HEADER_OTHER > "ispunctrune.c";
print "int\nispunctrune(Rune r)\n{\n\treturn isgraphrune(r) && !isalnumrune(r);\n}" > "ispunctrune.c";
print HEADER_OTHER > "isxdigitrune.c";
print "int\nisxdigitrune(Rune r)\n{\n\treturn (r >= '0' && (r - '0') < 10) || (r >= 'a' && (r - 'a') < 6);\n}" > "isxdigitrune.c";
}
+148
View File
@@ -0,0 +1,148 @@
/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com>
*
* 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.
*/
#include "../utf.h"
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define UTFSEQ(x) ((((x) & 0x80) == 0x00) ? 1 /* 0xxxxxxx */ \
: (((x) & 0xC0) == 0x80) ? 0 /* 10xxxxxx */ \
: (((x) & 0xE0) == 0xC0) ? 2 /* 110xxxxx */ \
: (((x) & 0xF0) == 0xE0) ? 3 /* 1110xxxx */ \
: (((x) & 0xF8) == 0xF0) ? 4 /* 11110xxx */ \
: (((x) & 0xFC) == 0xF8) ? 5 /* 111110xx */ \
: (((x) & 0xFE) == 0xFC) ? 6 /* 1111110x */ \
: 0 )
#define BADRUNE(x) ((x) < 0 || (x) > Runemax \
|| ((x) & 0xFFFE) == 0xFFFE \
|| ((x) >= 0xD800 && (x) <= 0xDFFF) \
|| ((x) >= 0xFDD0 && (x) <= 0xFDEF))
int
runetochar(char *s, const Rune *p)
{
Rune r = *p;
switch(runelen(r)) {
case 1: /* 0aaaaaaa */
s[0] = r;
return 1;
case 2: /* 00000aaa aabbbbbb */
s[0] = 0xC0 | ((r & 0x0007C0) >> 6); /* 110aaaaa */
s[1] = 0x80 | (r & 0x00003F); /* 10bbbbbb */
return 2;
case 3: /* aaaabbbb bbcccccc */
s[0] = 0xE0 | ((r & 0x00F000) >> 12); /* 1110aaaa */
s[1] = 0x80 | ((r & 0x000FC0) >> 6); /* 10bbbbbb */
s[2] = 0x80 | (r & 0x00003F); /* 10cccccc */
return 3;
case 4: /* 000aaabb bbbbcccc ccdddddd */
s[0] = 0xF0 | ((r & 0x1C0000) >> 18); /* 11110aaa */
s[1] = 0x80 | ((r & 0x03F000) >> 12); /* 10bbbbbb */
s[2] = 0x80 | ((r & 0x000FC0) >> 6); /* 10cccccc */
s[3] = 0x80 | (r & 0x00003F); /* 10dddddd */
return 4;
default:
return 0; /* error */
}
}
int
chartorune(Rune *p, const char *s)
{
return charntorune(p, s, UTFmax);
}
int
charntorune(Rune *p, const char *s, size_t len)
{
unsigned int i, n;
Rune r;
if(len == 0) /* can't even look at s[0] */
return 0;
switch((n = UTFSEQ(s[0]))) {
case 1: r = s[0]; break; /* 0xxxxxxx */
case 2: r = s[0] & 0x1F; break; /* 110xxxxx */
case 3: r = s[0] & 0x0F; break; /* 1110xxxx */
case 4: r = s[0] & 0x07; break; /* 11110xxx */
case 5: r = s[0] & 0x03; break; /* 111110xx */
case 6: r = s[0] & 0x01; break; /* 1111110x */
default: /* invalid sequence */
*p = Runeerror;
return 1;
}
/* add values from continuation bytes */
for(i = 1; i < MIN(n, len); i++)
if((s[i] & 0xC0) == 0x80) {
/* add bits from continuation byte to rune value
* cannot overflow: 6 byte sequences contain 31 bits */
r = (r << 6) | (s[i] & 0x3F); /* 10xxxxxx */
}
else { /* expected continuation */
*p = Runeerror;
return i;
}
if(i < n) /* must have reached len limit */
return 0;
/* reject invalid or overlong sequences */
if(BADRUNE(r) || runelen(r) < (int)n)
r = Runeerror;
*p = r;
return n;
}
int
runelen(Rune r)
{
if(BADRUNE(r))
return 0; /* error */
else if(r <= 0x7F)
return 1;
else if(r <= 0x07FF)
return 2;
else if(r <= 0xFFFF)
return 3;
else
return 4;
}
size_t
runenlen(const Rune *p, size_t len)
{
size_t i, n = 0;
for(i = 0; i < len; i++)
n += runelen(p[i]);
return n;
}
int
fullrune(const char *s, size_t len)
{
Rune r;
return charntorune(&r, s, len) > 0;
}
+41
View File
@@ -0,0 +1,41 @@
/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com>
* (c) 2015 Laslo Hunhold <dev@frign.de>
*
* 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.
*/
#include "../utf.h"
int
rune1cmp(const void *v1, const void *v2)
{
Rune r1 = *(Rune *)v1, r2 = *(Rune *)v2;
return r1 - r2;
}
int
rune2cmp(const void *v1, const void *v2)
{
Rune r = *(Rune *)v1, *p = (Rune *)v2;
if(r >= p[0] && r <= p[1])
return 0;
else
return r - p[0];
}
+26
View File
@@ -0,0 +1,26 @@
/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com>
* (c) 2015 Laslo Hunhold <dev@frign.de>
*
* 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.
*/
#define nelem(x) (sizeof (x) / sizeof *(x))
int rune1cmp(const void *, const void *);
int rune2cmp(const void *, const void *);
+265
View File
@@ -0,0 +1,265 @@
/* Automatically generated by mkrunetype.awk */
#include <stdlib.h>
#include "../utf.h"
#include "runetype.h"
static const Rune upper3[][2] = {
{ 0x0100, 0x012E },
{ 0x0132, 0x0136 },
{ 0x0139, 0x0147 },
{ 0x014A, 0x0176 },
{ 0x0179, 0x017D },
{ 0x0182, 0x0184 },
{ 0x01A0, 0x01A4 },
{ 0x01B3, 0x01B5 },
{ 0x01CD, 0x01DB },
{ 0x01DE, 0x01EE },
{ 0x01F8, 0x021E },
{ 0x0222, 0x0232 },
{ 0x0246, 0x024E },
{ 0x0370, 0x0372 },
{ 0x03D8, 0x03EE },
{ 0x0460, 0x0480 },
{ 0x048A, 0x04BE },
{ 0x04C1, 0x04CD },
{ 0x04D0, 0x052E },
{ 0x1E00, 0x1E94 },
{ 0x1EA0, 0x1EFE },
{ 0x2C67, 0x2C6B },
{ 0x2C80, 0x2CE2 },
{ 0x2CEB, 0x2CED },
{ 0xA640, 0xA66C },
{ 0xA680, 0xA69A },
{ 0xA722, 0xA72E },
{ 0xA732, 0xA76E },
{ 0xA779, 0xA77B },
{ 0xA77E, 0xA786 },
{ 0xA790, 0xA792 },
{ 0xA796, 0xA7A8 },
{ 0xA7B4, 0xA7C2 },
{ 0xA7C7, 0xA7C9 },
{ 0xA7D6, 0xA7D8 },
};
static const Rune upper2[][3] = {
{ 0x0041, 0x005A, 0x0061 },
{ 0x00C0, 0x00D6, 0x00E0 },
{ 0x00D8, 0x00DE, 0x00F8 },
{ 0x0189, 0x018A, 0x0256 },
{ 0x01B1, 0x01B2, 0x028A },
{ 0x0388, 0x038A, 0x03AD },
{ 0x038E, 0x038F, 0x03CD },
{ 0x0391, 0x03A1, 0x03B1 },
{ 0x03A3, 0x03AB, 0x03C3 },
{ 0x03D2, 0x03D4, 0x03D2 },
{ 0x03FD, 0x03FF, 0x037B },
{ 0x0400, 0x040F, 0x0450 },
{ 0x0410, 0x042F, 0x0430 },
{ 0x0531, 0x0556, 0x0561 },
{ 0x10A0, 0x10C5, 0x2D00 },
{ 0x13A0, 0x13EF, 0xAB70 },
{ 0x13F0, 0x13F5, 0x13F8 },
{ 0x1C90, 0x1CBA, 0x10D0 },
{ 0x1CBD, 0x1CBF, 0x10FD },
{ 0x1F08, 0x1F0F, 0x1F00 },
{ 0x1F18, 0x1F1D, 0x1F10 },
{ 0x1F28, 0x1F2F, 0x1F20 },
{ 0x1F38, 0x1F3F, 0x1F30 },
{ 0x1F48, 0x1F4D, 0x1F40 },
{ 0x1F68, 0x1F6F, 0x1F60 },
{ 0x1FB8, 0x1FB9, 0x1FB0 },
{ 0x1FBA, 0x1FBB, 0x1F70 },
{ 0x1FC8, 0x1FCB, 0x1F72 },
{ 0x1FD8, 0x1FD9, 0x1FD0 },
{ 0x1FDA, 0x1FDB, 0x1F76 },
{ 0x1FE8, 0x1FE9, 0x1FE0 },
{ 0x1FEA, 0x1FEB, 0x1F7A },
{ 0x1FF8, 0x1FF9, 0x1F78 },
{ 0x1FFA, 0x1FFB, 0x1F7C },
{ 0x210B, 0x210D, 0x210B },
{ 0x2110, 0x2112, 0x2110 },
{ 0x2119, 0x211D, 0x2119 },
{ 0x212C, 0x212D, 0x212C },
{ 0x2130, 0x2131, 0x2130 },
{ 0x213E, 0x213F, 0x213E },
{ 0x2C00, 0x2C2F, 0x2C30 },
{ 0x2C7E, 0x2C7F, 0x023F },
{ 0xFF21, 0xFF3A, 0xFF41 },
{ 0x10400, 0x10427, 0x10428 },
{ 0x104B0, 0x104D3, 0x104D8 },
{ 0x10570, 0x1057A, 0x10597 },
{ 0x1057C, 0x1058A, 0x105A3 },
{ 0x1058C, 0x10592, 0x105B3 },
{ 0x10594, 0x10595, 0x105BB },
{ 0x10C80, 0x10CB2, 0x10CC0 },
{ 0x118A0, 0x118BF, 0x118C0 },
{ 0x16E40, 0x16E5F, 0x16E60 },
{ 0x1D400, 0x1D419, 0x1D400 },
{ 0x1D434, 0x1D44D, 0x1D434 },
{ 0x1D468, 0x1D481, 0x1D468 },
{ 0x1D49E, 0x1D49F, 0x1D49E },
{ 0x1D4A5, 0x1D4A6, 0x1D4A5 },
{ 0x1D4A9, 0x1D4AC, 0x1D4A9 },
{ 0x1D4AE, 0x1D4B5, 0x1D4AE },
{ 0x1D4D0, 0x1D4E9, 0x1D4D0 },
{ 0x1D504, 0x1D505, 0x1D504 },
{ 0x1D507, 0x1D50A, 0x1D507 },
{ 0x1D50D, 0x1D514, 0x1D50D },
{ 0x1D516, 0x1D51C, 0x1D516 },
{ 0x1D538, 0x1D539, 0x1D538 },
{ 0x1D53B, 0x1D53E, 0x1D53B },
{ 0x1D540, 0x1D544, 0x1D540 },
{ 0x1D54A, 0x1D550, 0x1D54A },
{ 0x1D56C, 0x1D585, 0x1D56C },
{ 0x1D5A0, 0x1D5B9, 0x1D5A0 },
{ 0x1D5D4, 0x1D5ED, 0x1D5D4 },
{ 0x1D608, 0x1D621, 0x1D608 },
{ 0x1D63C, 0x1D655, 0x1D63C },
{ 0x1D670, 0x1D689, 0x1D670 },
{ 0x1D6A8, 0x1D6C0, 0x1D6A8 },
{ 0x1D6E2, 0x1D6FA, 0x1D6E2 },
{ 0x1D71C, 0x1D734, 0x1D71C },
{ 0x1D756, 0x1D76E, 0x1D756 },
{ 0x1D790, 0x1D7A8, 0x1D790 },
{ 0x1E900, 0x1E921, 0x1E922 },
};
static const Rune upper1[][2] = {
{ 0x0130, 0x0069 },
{ 0x0178, 0x00FF },
{ 0x0181, 0x0253 },
{ 0x0186, 0x0254 },
{ 0x0187, 0x0188 },
{ 0x018B, 0x018C },
{ 0x018E, 0x01DD },
{ 0x018F, 0x0259 },
{ 0x0190, 0x025B },
{ 0x0191, 0x0192 },
{ 0x0193, 0x0260 },
{ 0x0194, 0x0263 },
{ 0x0196, 0x0269 },
{ 0x0197, 0x0268 },
{ 0x0198, 0x0199 },
{ 0x019C, 0x026F },
{ 0x019D, 0x0272 },
{ 0x019F, 0x0275 },
{ 0x01A6, 0x0280 },
{ 0x01A7, 0x01A8 },
{ 0x01A9, 0x0283 },
{ 0x01AC, 0x01AD },
{ 0x01AE, 0x0288 },
{ 0x01AF, 0x01B0 },
{ 0x01B7, 0x0292 },
{ 0x01B8, 0x01B9 },
{ 0x01BC, 0x01BD },
{ 0x01C4, 0x01C6 },
{ 0x01C7, 0x01C9 },
{ 0x01CA, 0x01CC },
{ 0x01F1, 0x01F3 },
{ 0x01F4, 0x01F5 },
{ 0x01F6, 0x0195 },
{ 0x01F7, 0x01BF },
{ 0x0220, 0x019E },
{ 0x023A, 0x2C65 },
{ 0x023B, 0x023C },
{ 0x023D, 0x019A },
{ 0x023E, 0x2C66 },
{ 0x0241, 0x0242 },
{ 0x0243, 0x0180 },
{ 0x0244, 0x0289 },
{ 0x0245, 0x028C },
{ 0x0376, 0x0377 },
{ 0x037F, 0x03F3 },
{ 0x0386, 0x03AC },
{ 0x038C, 0x03CC },
{ 0x03CF, 0x03D7 },
{ 0x03F4, 0x03B8 },
{ 0x03F7, 0x03F8 },
{ 0x03F9, 0x03F2 },
{ 0x03FA, 0x03FB },
{ 0x04C0, 0x04CF },
{ 0x10C7, 0x2D27 },
{ 0x10CD, 0x2D2D },
{ 0x1E9E, 0x00DF },
{ 0x1F59, 0x1F51 },
{ 0x1F5B, 0x1F53 },
{ 0x1F5D, 0x1F55 },
{ 0x1F5F, 0x1F57 },
{ 0x1FEC, 0x1FE5 },
{ 0x2102, 0x2102 },
{ 0x2107, 0x2107 },
{ 0x2115, 0x2115 },
{ 0x2124, 0x2124 },
{ 0x2126, 0x03C9 },
{ 0x2128, 0x2128 },
{ 0x212A, 0x006B },
{ 0x212B, 0x00E5 },
{ 0x2132, 0x214E },
{ 0x2133, 0x2133 },
{ 0x2145, 0x2145 },
{ 0x2183, 0x2184 },
{ 0x2C60, 0x2C61 },
{ 0x2C62, 0x026B },
{ 0x2C63, 0x1D7D },
{ 0x2C64, 0x027D },
{ 0x2C6D, 0x0251 },
{ 0x2C6E, 0x0271 },
{ 0x2C6F, 0x0250 },
{ 0x2C70, 0x0252 },
{ 0x2C72, 0x2C73 },
{ 0x2C75, 0x2C76 },
{ 0x2CF2, 0x2CF3 },
{ 0xA77D, 0x1D79 },
{ 0xA78B, 0xA78C },
{ 0xA78D, 0x0265 },
{ 0xA7AA, 0x0266 },
{ 0xA7AB, 0x025C },
{ 0xA7AC, 0x0261 },
{ 0xA7AD, 0x026C },
{ 0xA7AE, 0x026A },
{ 0xA7B0, 0x029E },
{ 0xA7B1, 0x0287 },
{ 0xA7B2, 0x029D },
{ 0xA7B3, 0xAB53 },
{ 0xA7C4, 0xA794 },
{ 0xA7C5, 0x0282 },
{ 0xA7C6, 0x1D8E },
{ 0xA7D0, 0xA7D1 },
{ 0xA7F5, 0xA7F6 },
{ 0x1D49C, 0x1D49C },
{ 0x1D4A2, 0x1D4A2 },
{ 0x1D546, 0x1D546 },
{ 0x1D7CA, 0x1D7CA },
};
int
isupperrune(Rune r)
{
const Rune *match;
if((match = bsearch(&r, upper3, nelem(upper3), sizeof *upper3, &rune2cmp)))
return !((r - match[0]) % 2);
if(bsearch(&r, upper2, nelem(upper2), sizeof *upper2, &rune2cmp))
return 1;
if(bsearch(&r, upper1, nelem(upper1), sizeof *upper1, &rune1cmp))
return 1;
return 0;
}
int
tolowerrune(Rune r)
{
Rune *match;
match = bsearch(&r, upper3, nelem(upper3), sizeof *upper3, &rune2cmp);
if (match)
return ((r - match[0]) % 2) ? r : r + 1;
match = bsearch(&r, upper2, nelem(upper2), sizeof *upper2, &rune2cmp);
if (match)
return match[2] + (r - match[0]);
match = bsearch(&r, upper1, nelem(upper1), sizeof *upper1, &rune1cmp);
if (match)
return match[1];
return r;
}
+142
View File
@@ -0,0 +1,142 @@
/* MIT/X Consortium Copyright (c) 2012 Connor Lane Smith <cls@lubutu.com>
*
* 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.
*/
#include <string.h>
#include "../utf.h"
char *
utfecpy(char *to, char *end, const char *from)
{
Rune r = Runeerror;
size_t i, n;
/* seek through to find final full rune */
for(i = 0; r != '\0' && (n = charntorune(&r, &from[i], end - &to[i])); i += n)
;
memcpy(to, from, i); /* copy over bytes up to this rune */
if(i > 0 && r != '\0')
to[i] = '\0'; /* terminate if unterminated */
return &to[i];
}
size_t
utflen(const char *s)
{
const char *p = s;
size_t i;
Rune r;
for(i = 0; *p != '\0'; i++)
p += chartorune(&r, p);
return i;
}
size_t
utfnlen(const char *s, size_t len)
{
const char *p = s;
size_t i;
Rune r;
int n;
for(i = 0; (n = charntorune(&r, p, len-(p-s))) && r != '\0'; i++)
p += n;
return i;
}
size_t
utfmemlen(const char *s, size_t len)
{
const char *p = s;
size_t i;
Rune r;
int n;
for(i = 0; (n = charntorune(&r, p, len-(p-s))); i++)
p += n;
return i;
}
char *
utfrune(const char *s, Rune r)
{
if(r < Runeself) {
return strchr(s, r);
}
else if(r == Runeerror) {
Rune r0;
int n;
for(; *s != '\0'; s += n) {
n = chartorune(&r0, s);
if(r == r0)
return (char *)s;
}
}
else {
char buf[UTFmax+1];
int n;
if(!(n = runetochar(buf, &r)))
return NULL;
buf[n] = '\0';
return strstr(s, buf);
}
return NULL;
}
char *
utfrrune(const char *s, Rune r)
{
const char *p = NULL;
Rune r0;
int n;
if(r < Runeself)
return strrchr(s, r);
for(; *s != '\0'; s += n) {
n = chartorune(&r0, s);
if(r == r0)
p = s;
}
return (char *)p;
}
char *
utfutf(const char *s, const char *t)
{
const char *p, *q;
Rune r0, r1, r2;
int n, m;
for(chartorune(&r0, t); (s = utfrune(s, r0)); s++) {
for(p = s, q = t; *q && *p; p += n, q += m) {
n = chartorune(&r1, p);
m = chartorune(&r2, q);
if(r1 != r2)
break;
}
if(!*q)
return (char *)s;
}
return NULL;
}
+27
View File
@@ -0,0 +1,27 @@
/* See LICENSE file for copyright and license details. */
#include "../utf.h"
size_t
utftorunestr(const char *str, Rune *r)
{
size_t i;
int n;
for (i = 0; (n = chartorune(&r[i], str)) && r[i]; i++)
str += n;
return i;
}
size_t
utfntorunestr(const char *str, size_t len, Rune *r)
{
size_t i;
int n;
const char *end = str + len;
for (i = 0; (n = charntorune(&r[i], str, end - str)); i++)
str += n;
return i;
}
+23
View File
@@ -0,0 +1,23 @@
/* See LICENSE file for copyright and license details. */
#include <unistd.h>
#include "../util.h"
int
concat(int f1, const char *s1, int f2, const char *s2)
{
char buf[BUFSIZ];
ssize_t n;
while ((n = read(f1, buf, sizeof(buf))) > 0) {
if (writeall(f2, buf, n) < 0) {
weprintf("write %s:", s2);
return -2;
}
}
if (n < 0) {
weprintf("read %s:", s1);
return -1;
}
return 0;
}
+22
View File
@@ -0,0 +1,22 @@
/* See LICENSE file for copyright and license details. */
#include <stdarg.h>
#include <ctype.h>
#include "../util.h"
int
confirm(const char *fmt, ...)
{
int c, ans;
va_list ap;
va_start(ap, fmt);
xvprintf(fmt, ap);
va_end(ap);
c = getchar();
ans = (c == 'y' || c == 'Y');
while (c != '\n' && c != EOF)
c = getchar();
return ans;
}

Some files were not shown because too many files have changed in this diff Show More