Add more documentation to redbean

This change also improves the unix module, adding a reboot() system call
for fun and profit, fixing the execve() api, and a printimage release.
This commit is contained in:
Justine Tunney 2022-04-21 04:01:42 -07:00
parent 87396f43bc
commit 9bfa6ec06e
16 changed files with 498 additions and 133 deletions

View file

@ -121,7 +121,7 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) {
if (rc == -1 && errno == EFAULT) {
STRACE("pwritev(%d, %p, %d, %'ld) → %'zd% m", fd, iov, iovlen, off, rc);
} else {
kprintf(STRACE_PROLOGUE "readv(%d, ", fd);
kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd);
__strace_iov(iov, iovlen, rc != -1 ? rc : 0);
kprintf(", %d, %'ld) → %'ld% m%n", iovlen, off, rc);
}

View file

@ -77,7 +77,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
if (rc == -1 && errno == EFAULT) {
STRACE("writev(%d, %p, %d) → %'zd% m", fd, iov, iovlen, rc);
} else {
kprintf(STRACE_PROLOGUE "readv(%d, ", fd);
kprintf(STRACE_PROLOGUE "writev(%d, ", fd);
__strace_iov(iov, iovlen, rc != -1 ? rc : 0);
kprintf(", %d) → %'ld% m%n", iovlen, rc);
}

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_DISABLE_CAD,0,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff
.syscon reboot,RB_DISABLE_CAD,0,-1,-1,-1,-1,-1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_ENABLE_CAD,0x89abcdef,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff
.syscon reboot,RB_ENABLE_CAD,0x89abcdef,-1,-1,-1,-1,-1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_KEXEC,0x45584543,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff
.syscon reboot,RB_KEXEC,0x45584543,-1,-1,-1,-1,-1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_POWERDOWN,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8
.syscon reboot,RB_POWERDOWN,0x4321fedc,-1,0x4000,0x1000,0x808,8

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_POWEROFF,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8
.syscon reboot,RB_POWEROFF,0x4321fedc,-1,0x4000,0x1000,0x808,8

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_POWER_OFF,0x4321fedc,0xffffffff,0x4000,0x1000,0x808,8
.syscon reboot,RB_POWER_OFF,0x4321fedc,-1,0x4000,0x1000,0x808,8

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon reboot,RB_SW_SUSPEND,0xd000fce2,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xd000fce2
.syscon reboot,RB_SW_SUSPEND,0xd000fce2,-1,-1,-1,-1,0xd000fce2

View file

@ -4,17 +4,17 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const uint32_t RB_AUTOBOOT;
extern const uint32_t RB_POWER_OFF;
extern const uint32_t RB_POWERDOWN;
extern const uint32_t RB_POWEROFF;
extern const uint32_t RB_HALT_SYSTEM;
extern const uint32_t RB_HALT;
extern const uint32_t RB_SW_SUSPEND;
extern const uint32_t RB_KEXEC;
extern const uint32_t RB_ENABLE_CAD;
extern const uint32_t RB_DISABLE_CAD;
extern const uint32_t RB_NOSYNC;
extern const int RB_AUTOBOOT;
extern const int RB_POWER_OFF;
extern const int RB_POWERDOWN;
extern const int RB_POWEROFF;
extern const int RB_HALT_SYSTEM;
extern const int RB_HALT;
extern const int RB_SW_SUSPEND;
extern const int RB_KEXEC;
extern const int RB_ENABLE_CAD;
extern const int RB_DISABLE_CAD;
extern const int RB_NOSYNC;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

26
tool/net/demo/unix2.lua Normal file
View file

@ -0,0 +1,26 @@
-- example of how to run the ls command
-- and pipe its output to the http user
local unix = require "unix"
ls = unix.commandv("ls")
reader, writer = unix.pipe()
if unix.fork() == 0 then
unix.close(1)
unix.dup(writer)
unix.close(writer)
unix.close(reader)
unix.execve(ls, {ls, "-Shal"})
unix.exit(127)
else
unix.close(writer)
SetHeader('Content-Type', 'text/plain')
while true do
data = unix.read(reader)
if data ~= "" then
Write(data)
else
break
end
end
unix.close(reader)
unix.wait(-1)
end

View file

@ -24,6 +24,17 @@ OVERVIEW
itself is written as a single .c file. It embeds the Lua programming
language and SQLite which let you write dynamic pages.
FEATURES
- Lua v5.4
- SQLite 3.35.5
- TLS v1.2 / v1.1 / v1.0
- HTTP v1.1 / v1.0 / v0.9
- Chromium-Zlib Compression
- Statusz Monitoring Statistics
- Self-Modifying PKZIP Object Store
- Linux + Windows + Mac + FreeBSD + OpenBSD + NetBSD
FLAGS
-h or -? help
@ -65,16 +76,53 @@ FLAGS
--strace enables system call tracing
--ftrace enables function call tracing
FEATURES
KEYBOARD
- Lua v5.4
- SQLite 3.35.5
- TLS v1.2 / v1.1 / v1.0
- HTTP v1.1 / v1.0 / v0.9
- Chromium-Zlib Compression
- Statusz Monitoring Statistics
- Self-Modifying PKZIP Object Store
- Linux + Windows + Mac + FreeBSD + OpenBSD + NetBSD
CTRL-D EXIT
CTRL-C CTRL-C EXIT
CTRL-E END
CTRL-A START
CTRL-B BACK
CTRL-F FORWARD
CTRL-L CLEAR
CTRL-H BACKSPACE
CTRL-D DELETE
CTRL-N NEXT HISTORY
CTRL-P PREVIOUS HISTORY
CTRL-R SEARCH HISTORY
CTRL-G CANCEL SEARCH
ALT-< BEGINNING OF HISTORY
ALT-> END OF HISTORY
ALT-F FORWARD WORD
ALT-B BACKWARD WORD
CTRL-K KILL LINE FORWARDS
CTRL-U KILL LINE BACKWARDS
ALT-H KILL WORD BACKWARDS
CTRL-W KILL WORD BACKWARDS
CTRL-ALT-H KILL WORD BACKWARDS
ALT-D KILL WORD FORWARDS
CTRL-Y YANK
ALT-Y ROTATE KILL RING AND YANK AGAIN
CTRL-T TRANSPOSE
ALT-T TRANSPOSE WORD
ALT-U UPPERCASE WORD
ALT-L LOWERCASE WORD
ALT-C CAPITALIZE WORD
CTRL-\ QUIT PROCESS
CTRL-S PAUSE OUTPUT
CTRL-Q UNPAUSE OUTPUT (IF PAUSED)
CTRL-Q ESCAPED INSERT
CTRL-ALT-F FORWARD EXPR
CTRL-ALT-B BACKWARD EXPR
ALT-RIGHT FORWARD EXPR
ALT-LEFT BACKWARD EXPR
ALT-SHIFT-B BARF EXPR
ALT-SHIFT-S SLURP EXPR
CTRL-SPACE SET MARK
CTRL-X CTRL-X GOTO MARK
CTRL-Z SUSPEND PROCESS
ALT-\ SQUEEZE ADJACENT WHITESPACE
PROTIP REMAP CAPS LOCK TO CTRL
USAGE
@ -1303,23 +1351,139 @@ UNIX MODULE
`prog` contains slashes then it's not path searched either and
will be returned if it exists.
unix.realpath(filename:str) → abspath:str[, errno:int]
Returns absolute path of filename, with `.` and `..` components
removed, and symlinks will be resolved.
unix.execve(prog, argv[, envp]) → errno
unix.execve(prog:str[, args:List<*>, env:Map<str,*>]) → errno:int
Exits current process, replacing it with a new instance of the
specified program. `prog` needs to be an absolute path, see
commandv(). `envp` defaults to to the current `environ`. Both
`prog` and `envp` are arrays of strings.
commandv(). `env` defaults to to the current `environ`. Here's
a basic usage example:
unix.execve("/bin/ls", {"/bin/ls", "-hal"})
unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {PATH="/bin"})
unix.exit(127)
The first element in `argv` should be `prog`. This function is
normally called after forking.
`prog` needs to be the resolved pathname of your executable. You
can use commandv() to search your `PATH`.
`args` is a string list table. The first element in `args`
should be `prog`. Values are coerced to strings. This parameter
defaults to `{prog}`.
`env` is a key/value table. Keys that aren't strings are
ignored. Values are coerced to strings. This parameter defaults
to environ() in a way that avoids copying.
execve() function is normally called after fork() returns 0. If
that isn't the case, then your redbean worker will be destroyed.
This function never returns on success.
`EAGAIN` is returned if you've enforced a max number of
processes using `setrlimit(RLIMIT_NPROC)`.
unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, errno:int
Duplicates file descriptor.
`newfd` defaults to the lowest number available file descriptor.
If the new number is specified and it's already open, then it'll
be silently closed before the duplication happens.
`flags` can have `O_CLOEXEC` which means the returned file
descriptors will be automatically closed upon execve().
unix.pipe([flags]) → reader, writer, errno:int
Creates fifo which enables communication between processes.
Returns two file descriptors: one for reading and one for
writing. `flags` can have `O_CLOEXEC`. On error, `reader` and
`writer` will be `nil` and `errno` will be set to non-nil.
Here's an example of how pipe(), fork(), dup(), etc. may be used
to serve an HTTP response containing the output of a subprocess.
local unix = require "unix"
ls = unix.commandv("ls")
reader, writer = unix.pipe()
if unix.fork() == 0 then
unix.close(1)
unix.dup(writer)
unix.close(writer)
unix.close(reader)
unix.execve(ls, {ls, "-Shal"})
unix.exit(127)
else
unix.close(writer)
SetHeader('Content-Type', 'text/plain')
while true do
data = unix.read(reader)
if data ~= "" then
Write(data)
else
break
end
end
unix.close(reader)
unix.wait(-1)
end
unix.wait([pid:int, options:int])
→ pid:int, wstatus:int, nil, errno:int
Waits for subprocess to terminate.
`pid` defaults to `-1` which means any child process. Setting
`pid` to `0` is equivalent to `-getpid()`. If `pid < -1` then
that means wait for any pid in the process group `-pid`. Then
lastly if `pid > 0` then this waits for a specific process id
Options may have `WNOHANG` which means don't block, check for
the existence of processes that are already dead (technically
speaking zombies) and if so harvest them immediately.
Returns the process id of the child that terminated. In other
cases, the returned `pid` is nil and `errno` is non-nil.
The returned `wstatus` contains information about the process
exit status. It's a complicated integer and there's functions
that can help interpret it. For example:
-- wait for zombies
-- traditional technique for SIGCHLD handlers
while true do
pid, wstatus, errno = unix.wait(-1, unix.WNOHANG)
if pid then
if unix.WIFEXITED(wstatus) then
print('child', pid, 'exited with',
unix.WEXITSTATUS(wstatus))
elseif unix.WIFSIGNALED(wstatus) then
print('child', pid, 'crashed with',
unix.strsignal(unix.WTERMSIG(wstatus)))
end
elseif errno == unix.ECHILD then
print('no more zombies')
break
elseif errno == unix.ECHILD then
print('wait failed', unix.strerror(errno))
break
end
end
unix.getpid() → pid
Returns process id of current process.
unix.getppid() → pid
Returns process id of parent process.
unix.kill(pid, sig) → rc:int[, errno:int]
Returns process id of current process.
unix.raise(sig) → rc:int[, errno:int]
Triggers signal in current process.
This is pretty much the same as `kill(getpid(), sig)`.
unix.access(path:str, how) → rc:int[, errno:int]
@ -1347,17 +1511,6 @@ UNIX MODULE
Changes root directory. Raises `ENOSYS` on Windows.
unix.dup(oldfd[, newfd[, flags]]) → newfd:int, errno:int
Duplicates file descriptor. `flags` can have `O_CLOEXEC`.
unix.pipe([flags]) → reader, writer, errno:int
Creates fifo which enables communication between processes.
Returns two file descriptors: one for reading and one for
writing. `flags` can have `O_CLOEXEC`. On error, `reader` and
`writer` will be `nil` and `errno` will be set to non-nil.
unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int]
Renames file.
@ -1370,28 +1523,79 @@ UNIX MODULE
Creates soft link, or a symbolic link.
unix.realpath(filename:str) → abspath:str[, errno:int]
Returns absolute path of filename, with `.` and `..` components
removed, and symlinks will be resolved.
unix.chown(path:str, uid, gid) → rc:int[, errno:int]
Changes user and gorup on file.
unix.chmod(path:str, mode) → rc:int[, errno:int]
Changes mode bits on file.
unix.getcwd(path:str, mode) → rc:int[, errno:int]
unix.getpid() → pid
unix.getppid() → pid
unix.kill(pid, sig) → rc:int[, errno:int]
unix.raise(sig) → rc:int[, errno:int]
unix.wait(pid[, options]) → pid, wstatus, nil, errno:int
unix.fcntl(fd:int, cmd[, arg]) → rc:int[, errno:int]
Returns current working directory.
unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int]
Manipulates file descriptor.
Setting `cmd` to `F_GETFD`, `F_SETFD`, `F_GETFL` or `F_SETFL`
lets you query and/or change the status of file descriptors. For
example, it's possible using this to change `FD_CLOEXEC`.
POSIX advisory locks can be controlled by setting `cmd` to
`F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`.
unix.getsid(pid) → sid, errno:int
Gets session id.
unix.getpgrp() → pgid, errno:int
unix.getpgid(pid) → pgid, errno:int
Gets process group id.
unix.setpgrp() → pgid, errno:int
Sets process group id. This is the same as `setpgid(0,0)`.
unix.setpgid(pid, pgid) → pgid, errno:int
Sets process group id the modern way.
unix.getpgid(pid) → pgid, errno:int
Gets process group id the modern wayp.
unix.setsid() → sid, errno:int
unix.getuid() → uid, errno:int
Sets session id.
This function can be used to create daemons.
unix.getuid() → uid:int
Gets user id.
unix.getgid() → gid:int
Sets group id.
unix.setuid(uid:int) → rc:int[, errno:int]
unix.getgid() → gid, errno:int
Sets user id.
unix.setgid(gid:int) → rc:int[, errno:int]
unix.umask(mask) → rc:int[, errno:int]
Sets group id.
unix.umask(mask) → oldmask:int
Sets file permission mask and returns the old one.
unix.syslog(priority:int, msg:str)
@ -1455,17 +1659,42 @@ UNIX MODULE
`type` defaults to `SOCK_STREAM`
`protocol` defaults to `IPPROTO_TCP`
unix.bind(fd:int, ip, port) → rc:int[, errno:int]
unix.bind(fd:int[, ip:uint32, port:uint16]) → rc:int[, errno:int]
unix.connect(fd:int, ip, port) → rc:int[, errno:int]
Binds socket.
`ip` and `port` are in host endian order. For example, if you
wanted to listen on `10.0.0.1:31337` you could do:
unix.bind(sock, 10 << 24 | 0 << 16 | 0 << 8 | 1, 31337)
By default, `ip` and `port` are set to zero, which means to
listen on all interfaces with a kernel-assigned port number that
can be retrieved and used as follows:
local sock = unix.socket()
unix.bind(sock)
ip, port = unix.getsockname(sock)
print("listening on ip", FormatIp(ip), "port", port)
unix.listen(sock)
unix.accept(sock)
while true do
client, clientip, clientport = unix.accept(sock)
print("got client ip", FormatIp(clientip), "port", clientport)
unix.close(client)
end
unix.listen(fd:int[, backlog]) → rc:int[, errno:int]
unix.getsockname(fd:int) → ip, port, errno:int
Listens for incoming connections on bound socket.
unix.getpeername(fd:int) → ip, port, errno:int
unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int]
unix.accept(serverfd) → clientfd, ip, port, errno:int
unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int]
unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int]
unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int]
unix.recv(fd:int[, bufsiz[, flags]]) → data, errno:int
@ -1496,7 +1725,8 @@ UNIX MODULE
`how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK`
unix.sigaction(sig[, handler[, flags[, mask]]]) → handler, flags, mask, errno:int
unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]])
→ oldhandler:func|int, flags:int, mask:int, errno:int
`handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua
function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc.
@ -1539,6 +1769,20 @@ UNIX MODULE
unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND)
unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0)
unix.reboot(how:int) → str
Changes power status of system.
`how` may be `RB_AUTOBOOT` to reboot, `RB_POWER_OFF` to power
down, `RB_HALT_SYSTEM` to literally just stop the processor, or
`RB_SW_SUSPEND` to put the machine into a suspend state. These
magnums will be set to -1 if the method isn't supported on the
host platform.
By default, an implicit sync() is performed. That's to help
prevent you from losing data. If you don't want to shutdown
gracefully, then you can bitwise or `RB_NOSYNC` into `how`.
unix.strerrno(errno:int) → str
Turns `errno` code into its symbolic name, e.g. `"EINTR"`.

View file

@ -52,6 +52,7 @@
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/reboot.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/shut.h"
@ -92,6 +93,11 @@ static dontinline int ReturnInteger(lua_State *L, lua_Integer x) {
return 1;
}
static dontinline int ReturnString(lua_State *L, const char *x) {
lua_pushstring(L, x);
return 1;
}
static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) {
lua_pushinteger(L, ts->tv_sec);
lua_pushinteger(L, ts->tv_nsec);
@ -132,6 +138,23 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) {
return p;
}
static char **ConvertLuaTableToEnvList(lua_State *L, int i) {
int j, n;
char **p, *s;
luaL_checktype(L, i, LUA_TTABLE);
p = xcalloc((n = 0) + 1, sizeof(char *));
lua_pushnil(L);
for (n = 0; lua_next(L, i);) {
if (lua_type(L, -2) == LUA_TSTRING) {
p = xrealloc(p, (++n + 1) * sizeof(char *));
p[n - 1] = xasprintf("%s=%s", lua_tostring(L, -2), lua_tostring(L, -1));
}
lua_pop(L, 1);
}
p[n] = 0;
return p;
}
static void FreeStringList(char **p) {
int i;
if (p) {
@ -145,6 +168,31 @@ static void FreeStringList(char **p) {
////////////////////////////////////////////////////////////////////////////////
// System Calls
// unix.getpid() → pid:int
static int LuaUnixGetpid(lua_State *L) {
return ReturnInteger(L, getpid());
}
// unix.getppid() → pid:int
static int LuaUnixGetppid(lua_State *L) {
return ReturnInteger(L, getppid());
}
// unix.getuid() → uid:int
static int LuaUnixGetuid(lua_State *L) {
return ReturnInteger(L, getuid());
}
// unix.getgid() → gid:int
static int LuaUnixGetgid(lua_State *L) {
return ReturnInteger(L, getgid());
}
// unix.umask(mask:int) → oldmask:int
static int LuaUnixUmask(lua_State *L) {
return ReturnInteger(L, umask(luaL_checkinteger(L, 1)));
}
// unix.exit([exitcode:int]) → ⊥
static wontreturn int LuaUnixExit(lua_State *L) {
_Exit(luaL_optinteger(L, 1, 0));
@ -290,12 +338,11 @@ static int LuaUnixFork(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.execve(prog, argv[, envp]) → errno
// unix.exit(127)
// unix.execve(prog:str[, args:List<*>, env:Map<str,*>]) → errno:int
//
// unix = require "unix"
// prog = unix.commandv("ls")
// unix.execve(prog, {prog, "-hal", "."})
// unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"})
// unix.exit(127)
//
// prog needs to be absolute, see commandv()
@ -303,20 +350,30 @@ static int LuaUnixFork(lua_State *L) {
static int LuaUnixExecve(lua_State *L) {
int olderr;
const char *prog;
char **argv, **envp, **freeme;
char **argv, **envp, **freeme1, **freeme2, *ezargs[2];
olderr = errno;
prog = luaL_checkstring(L, 1);
argv = ConvertLuaArrayToStringList(L, 2);
if (!lua_isnoneornil(L, 3)) {
envp = ConvertLuaArrayToStringList(L, 3);
freeme = envp;
if (!lua_isnoneornil(L, 2)) {
argv = ConvertLuaArrayToStringList(L, 2);
freeme1 = argv;
if (!lua_isnoneornil(L, 3)) {
envp = ConvertLuaTableToEnvList(L, 3);
freeme2 = envp;
} else {
envp = environ;
freeme2 = 0;
}
} else {
ezargs[0] = prog;
ezargs[1] = 0;
argv = ezargs;
envp = environ;
freeme = 0;
freeme1 = 0;
freeme2 = 0;
}
execve(prog, argv, envp);
FreeStringList(freeme);
FreeStringList(argv);
FreeStringList(freeme1);
FreeStringList(freeme2);
lua_pushinteger(L, errno);
errno = olderr;
return 1;
@ -401,18 +458,6 @@ static int LuaUnixGetrlimit(lua_State *L) {
}
}
// unix.getpid() → pid:int
static int LuaUnixGetpid(lua_State *L) {
lua_pushinteger(L, getpid());
return 1;
}
// unix.getppid() → pid:int
static int LuaUnixGetppid(lua_State *L) {
lua_pushinteger(L, getppid());
return 1;
}
// unix.kill(pid, sig) → rc:int[, errno:int]
static int LuaUnixKill(lua_State *L) {
int rc, pid, sig, olderr;
@ -432,11 +477,11 @@ static int LuaUnixRaise(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.wait(pid[, options]) → pid, wstatus, nil, errno
// unix.wait([pid, options]) → pid, wstatus, nil, errno
static int LuaUnixWait(lua_State *L) {
int rc, pid, olderr, options, wstatus;
olderr = errno;
pid = luaL_checkinteger(L, 1);
pid = luaL_optinteger(L, 1, -1);
options = luaL_optinteger(L, 2, 0);
rc = wait4(pid, &wstatus, options, 0);
if (rc != -1) {
@ -517,15 +562,6 @@ static int LuaUnixGetpgid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.umask(mask:int) → rc:int[, errno:int]
static int LuaUnixUmask(lua_State *L) {
int rc, mask, olderr;
olderr = errno;
mask = luaL_checkinteger(L, 1);
rc = umask(mask);
return ReturnRc(L, rc, olderr);
}
// unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int]
static int LuaUnixSetpgid(lua_State *L) {
int rc, pid, pgid, olderr;
@ -554,28 +590,12 @@ static int LuaUnixSetsid(lua_State *L) {
return ReturnRc(L, rc, olderr);
}
// unix.getuid() → uid[, errno]
static int LuaUnixGetuid(lua_State *L) {
int rc, olderr;
olderr = errno;
rc = getuid();
return ReturnRc(L, rc, olderr);
}
// unix.setuid(uid:int) → rc:int[, errno:int]
static int LuaUnixSetuid(lua_State *L) {
int olderr = errno;
return ReturnRc(L, setuid(luaL_checkinteger(L, 1)), olderr);
}
// unix.getgid() → gid:int[, errno:int]
static int LuaUnixGetgid(lua_State *L) {
int rc, olderr;
olderr = errno;
rc = getgid();
return ReturnRc(L, rc, olderr);
}
// unix.setgid(gid:int) → rc:int[, errno:int]
static int LuaUnixSetgid(lua_State *L) {
int olderr = errno;
@ -848,7 +868,7 @@ static int LuaUnixSocketpair(lua_State *L) {
}
}
// unix.bind(fd, ip, port) → rc:int[, errno:int]
// unix.bind(fd[, ip, port]) → rc:int[, errno:int]
// SOCK_CLOEXEC may be or'd into type
// family defaults to AF_INET
// type defaults to SOCK_STREAM
@ -859,8 +879,8 @@ static int LuaUnixBind(lua_State *L) {
bzero(&sa, sizeof(sa));
olderr = errno;
fd = luaL_checkinteger(L, 1);
sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2));
sa.sin_port = htons(luaL_checkinteger(L, 3));
sa.sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0));
sa.sin_port = htons(luaL_optinteger(L, 3, 0));
rc = bind(fd, &sa, sizeof(sa));
return ReturnRc(L, rc, olderr);
}
@ -928,7 +948,7 @@ static int LuaUnixGetpeername(lua_State *L) {
}
}
// unix.accept(serverfd) → clientfd, ip, port, errno
// unix.accept(serverfd:int) → clientfd:int, ip:uint32, port:uint16, errno
static int LuaUnixAccept(lua_State *L) {
uint32_t addrsize;
struct sockaddr_in sa;
@ -1237,20 +1257,42 @@ static int LuaUnixSetitimer(lua_State *L) {
// unix.strerror(errno) → str
static int LuaUnixStrerror(lua_State *L) {
lua_pushstring(L, strerror(luaL_checkinteger(L, 1)));
return 1;
return ReturnString(L, strerror(luaL_checkinteger(L, 1)));
}
// unix.strerrno(errno) → str
static int LuaUnixStrerrno(lua_State *L) {
lua_pushstring(L, strerror_short(luaL_checkinteger(L, 1)));
return 1;
return ReturnString(L, strerror_short(luaL_checkinteger(L, 1)));
}
// unix.strsignal(sig) → str
static int LuaUnixStrsignal(lua_State *L) {
lua_pushstring(L, strsignal(luaL_checkinteger(L, 1)));
return 1;
return ReturnString(L, strsignal(luaL_checkinteger(L, 1)));
}
// unix.WIFEXITED(wstatus) → int
static int LuaUnixWifexited(lua_State *L) {
return ReturnInteger(L, WIFEXITED(luaL_checkinteger(L, 1)));
}
// unix.WEXITSTATUS(wstatus) → int
static int LuaUnixWexitstatus(lua_State *L) {
return ReturnInteger(L, WEXITSTATUS(luaL_checkinteger(L, 1)));
}
// unix.WIFSIGNALED(wstatus) → int
static int LuaUnixWifsignaled(lua_State *L) {
return ReturnInteger(L, WIFSIGNALED(luaL_checkinteger(L, 1)));
}
// unix.WTERMSIG(wstatus) → int
static int LuaUnixWtermsig(lua_State *L) {
return ReturnInteger(L, WTERMSIG(luaL_checkinteger(L, 1)));
}
// unix.reboot(how:int) → rc:int[, errno:int]
static int LuaUnixReboot(lua_State *L) {
return ReturnInteger(L, reboot(luaL_checkinteger(L, 1)));
}
////////////////////////////////////////////////////////////////////////////////
@ -1549,9 +1591,14 @@ static const luaL_Reg kLuaUnix[] = {
{"sigprocmask", LuaUnixSigprocmask}, // change signal mask
{"sigsuspend", LuaUnixSigsuspend}, // wait for signal
{"setitimer", LuaUnixSetitimer}, // set alarm clock
{"reboot", LuaUnixReboot}, // reboots system
{"strerror", LuaUnixStrerror}, // turn errno into string
{"strerrno", LuaUnixStrerrno}, // turn errno into string
{"strsignal", LuaUnixStrsignal}, // turn signal into string
{"WIFEXITED", LuaUnixWifexited}, // gets exit code from wait status
{"WEXITSTATUS", LuaUnixWexitstatus}, // gets exit status from wait status
{"WIFSIGNALED", LuaUnixWifsignaled}, // determines if died due to signal
{"WTERMSIG", LuaUnixWtermsig}, // gets the signal code
{0}, //
};
@ -1643,6 +1690,7 @@ int LuaUnix(lua_State *L) {
// wait() options
LuaSetIntField(L, "WNOHANG", WNOHANG);
LuaSetIntField(L, "WNOHANG", WNOHANG);
// gettime() clocks
LuaSetIntField(L, "CLOCK_REALTIME", CLOCK_REALTIME); // portable
@ -1721,5 +1769,12 @@ int LuaUnix(lua_State *L) {
LuaSetIntField(L, "LOG_INFO", LOG_INFO);
LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG);
// reboot() howto
LuaSetIntField(L, "RB_AUTOBOOT", RB_AUTOBOOT);
LuaSetIntField(L, "RB_POWER_OFF", RB_POWER_OFF);
LuaSetIntField(L, "RB_HALT_SYSTEM", RB_HALT_SYSTEM);
LuaSetIntField(L, "RB_SW_SUSPEND", RB_SW_SUSPEND);
LuaSetIntField(L, "RB_NOSYNC", RB_NOSYNC);
return 1;
}

View file

@ -212,6 +212,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
o/$(MODE)/tool/net/net.pkg \
o/$(MODE)/tool/net/demo/sql.lua.zip.o \
o/$(MODE)/tool/net/demo/unix.lua.zip.o \
o/$(MODE)/tool/net/demo/unix2.lua.zip.o \
o/$(MODE)/tool/net/demo/fetch.lua.zip.o \
o/$(MODE)/tool/net/demo/hello.lua.zip.o \
o/$(MODE)/tool/net/demo/redbean.lua.zip.o \

View file

@ -53,12 +53,15 @@
#include "third_party/stb/stb_image.h"
#include "tool/viz/lib/graphic.h"
STATIC_YOINK("__zipos_get");
static struct Flags {
const char *out;
bool subpixel;
bool unsharp;
bool dither;
bool ruler;
bool magikarp;
bool trailingnewline;
long half;
bool full;
@ -85,6 +88,7 @@ FLAGS\n\
-f display full size\n\
-s unsharp sharpening\n\
-x xterm256 color mode\n\
-m use magikarp scaling\n\
-d hilbert curve dithering\n\
-r display pixel ruler on sides\n\
-p convert to subpixel layout\n\
@ -94,7 +98,6 @@ FLAGS\n\
EXAMPLES\n\
\n\
printimage.com -sxd lemurs.jpg # 256-color dither unsharp\n\
\n\
\n");
exit(rc);
}
@ -118,7 +121,7 @@ static void GetOpts(int *argc, char *argv[]) {
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) {
PrintUsage(EXIT_SUCCESS, stdout);
}
while ((opt = getopt(*argc, argv, "?vpfrtxads234o:w:h:")) != -1) {
while ((opt = getopt(*argc, argv, "?vpmfrtxads234o:w:h:")) != -1) {
switch (opt) {
case 'o':
g_flags.out = optarg;
@ -146,6 +149,9 @@ static void GetOpts(int *argc, char *argv[]) {
case 'r':
g_flags.ruler = true;
break;
case 'm':
g_flags.magikarp = true;
break;
case 'p':
g_flags.subpixel = true;
break;
@ -173,10 +179,6 @@ static void GetOpts(int *argc, char *argv[]) {
PrintUsage(EX_USAGE, stderr);
}
}
if (optind == *argc) {
if (!g_flags.out) g_flags.out = "-";
argv[(*argc)++] = "-";
}
if (!g_flags.full && (!g_flags.width || !g_flags.width)) {
ws.ws_col = 80;
ws.ws_row = 24;
@ -398,6 +400,22 @@ void WithImageFile(const char *path,
sxn = xn;
dyn = g_flags.height;
dxn = g_flags.width;
if (g_flags.magikarp) {
while (HALF(syn) > dyn || HALF(sxn) > dxn) {
if (HALF(sxn) > dxn) {
Magikarp2xX(yn, xn, data, syn, sxn);
Magikarp2xX(yn, xn, (char *)data + yn * xn, syn, sxn);
Magikarp2xX(yn, xn, (char *)data + yn * xn * 2, syn, sxn);
sxn = HALF(sxn);
}
if (HALF(syn) > dyn) {
Magikarp2xY(yn, xn, data, syn, sxn);
Magikarp2xY(yn, xn, (char *)data + yn * xn, syn, sxn);
Magikarp2xY(yn, xn, (char *)data + yn * xn * 2, syn, sxn);
syn = HALF(syn);
}
}
}
data = EzGyarados(3, dyn, dxn, gc(memalign(32, dyn * dxn * 3)), cn, yn, xn,
data, 0, cn, dyn, dxn, syn, sxn, 0, 0, 0, 0);
yn = dyn;
@ -410,6 +428,7 @@ int main(int argc, char *argv[]) {
int i;
ShowCrashReports();
GetOpts(&argc, argv);
if (optind == argc) PrintUsage(0, stdout);
stbi_set_unpremultiply_on_load(true);
for (i = optind; i < argc; ++i) {
WithImageFile(argv[i], ProcessImage);

View file

@ -45,6 +45,7 @@ TOOL_VIZ_DIRECTDEPS = \
LIBC_TINYMATH \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
NET_HTTP \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA \
@ -71,6 +72,25 @@ o/$(MODE)/tool/viz/%.com.dbg: \
$(APE)
@$(APELINK)
o/$(MODE)/tool/viz/printimage.com.dbg: \
$(TOOL_VIZ_DEPS) \
o/$(MODE)/tool/viz/printimage.o \
o/$(MODE)/tool/viz/viz.pkg \
o/$(MODE)/LICENSE.zip.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/viz/printimage.com: \
o/$(MODE)/tool/viz/printimage.com.dbg \
o/$(MODE)/third_party/zip/zip.com \
o/$(MODE)/tool/build/symtab.com
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/viz/.printimage/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/viz/.printimage/.symtab
o/$(MODE)/tool/viz/printvideo.com: \
o/$(MODE)/tool/viz/printvideo.com.dbg \
o/$(MODE)/third_party/zip/zip.com \