Compare commits

...

27 Commits

Author SHA1 Message Date
Jonas 'Sortie' Termansen 29f14f458b Add nyan(1). 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 312a62508e Draft video-player. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 944ae77da6 Work around pty deadlock. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen e5013cae4c Add cdrom mounting live environment. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 73e2f526f8 Revert "Parallelize driver initialization."
This reverts commit 0fef08bbc4.
2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 2d6f7cde21 Parallelize driver initialization. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 8daa313f75 Speed up ata(4) 400 ns waits.
Waiting for any non-zero duration currently waits for at least one timer
cycle (10 ms), which is especially expensive during early boot.

The current workaround of simply reading the status 14 times seems really
suspicious although the osdev wiki documents it, but let's see how well it
works on real hardware, it's probably good enough.

Try to determine the initial selected drive to save one drive selection.
2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 4905450d1d Decrease PS/2 timeouts. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 58885fedeb Add uptime(1) -pr options. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen c293d96cc3 Add iso9660 filesystem implementation. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 8153e44cd8 Add kernel virtual address space usage debug information. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 06ccbcd9ac Revert "Update to bison-3.8.2."
This reverts commit b82fae810b42c5426d21c4dc153b32f086dd7fde.
2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen bba34e93bf Update to bison-3.8.2. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 8db8515b0b Debug TCP socket state listing. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 264b9927e3 Add kernel heap allocation tracing debug facility. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen f27a8e3958 Add m4, perl, and texinfo to the basic ports set. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen d02c7daac6 Trianglix 4. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen a53819acfc Add tix-check(8). 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen fd0863c808 Volatile release. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 71829e41ef Add tix-upgrade(8). 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen f114453f28 Add pty(1). 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 792df0f9e1 Add signify port. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 1e7d262dcb Add irc(1).
Co-authored-by: Juhani Krekelä <juhani@krekelä.fi>
2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen b50c59ed66 Add getaddrinfo(1). 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen d505b8080f Add host(1). 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 92263fcd45 Enable stack smash protection by default. 2023-06-24 00:55:23 +02:00
Jonas 'Sortie' Termansen 300093400b Enable undefined behavior sanitization by default. 2023-06-24 00:55:23 +02:00
118 changed files with 10741 additions and 125 deletions

View File

@ -21,13 +21,17 @@ dnsconfig \
editor \
ext \
games \
host \
hostname \
ifconfig \
init \
irc \
iso9660 \
kblayout \
kblayout-compiler \
login \
mkinitrd \
nyan \
ping \
regress \
rw \
@ -69,10 +73,13 @@ else
SORTIX_INCLUDE_SOURCE?=yes
endif
ISO_MOUNT?=no
include build-aux/dirs.mak
BUILD_NAME:=sortix-$(RELEASE)-$(MACHINE)
CHAIN_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).chain.initrd
LIVE_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).live.initrd
OVERLAY_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).overlay.initrd
SRC_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).src.initrd
@ -235,6 +242,8 @@ sysroot-system: sysroot-fsh sysroot-base-headers
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
echo /etc/os-release >> "$(SYSROOT)/tix/manifest/system"
find etc | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
cp -RT etc "$(SYSROOT)/etc"
find share | sed -e 's,^,/,' >> "$(SYSROOT)/tix/manifest/system"
cp -RT share "$(SYSROOT)/share"
export SYSROOT="$(SYSROOT)" && \
@ -272,6 +281,7 @@ else ifneq ($(SORTIX_INCLUDE_SOURCE),no)
cp Makefile -t "$(SYSROOT)/src"
cp README -t "$(SYSROOT)/src"
cp -RT build-aux "$(SYSROOT)/src/build-aux"
cp -RT etc "$(SYSROOT)/src/etc"
cp -RT share "$(SYSROOT)/src/share"
(for D in $(MODULES); do (cp -R $$D -t "$(SYSROOT)/src" && $(MAKE) -C "$(SYSROOT)/src/$$D" clean) || exit $$?; done)
endif
@ -444,6 +454,24 @@ release-all-archs:
# Initial ramdisk
$(CHAIN_INITRD).uuid:
mkdir -p `dirname $@`
uuidgen > $@
$(CHAIN_INITRD): $(CHAIN_INITRD).uuid sysroot
mkdir -p `dirname $(CHAIN_INITRD)`
rm -rf $(CHAIN_INITRD).d
mkdir -p $(CHAIN_INITRD).d
mkdir -p $(CHAIN_INITRD).d/etc
echo "UUID=`cat $(CHAIN_INITRD).uuid` / iso9660 ro 0 1" > $(CHAIN_INITRD).d/etc/fstab
mkdir -p $(CHAIN_INITRD).d/etc/init
echo require chain exit-code > $(CHAIN_INITRD).d/etc/init/default
mkdir -p $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/init" $(CHAIN_INITRD).d/sbin
cp "$(SYSROOT)/sbin/iso9660fs" $(CHAIN_INITRD).d/sbin
mkinitrd --format=sortix-initrd-2 $(CHAIN_INITRD).d -o $(CHAIN_INITRD)
rm -rf $(CHAIN_INITRD).d
$(LIVE_INITRD): sysroot
mkdir -p `dirname $(LIVE_INITRD)`
rm -rf $(LIVE_INITRD).d
@ -455,6 +483,9 @@ $(LIVE_INITRD): sysroot
echo "include /etc/default/passwd.d/*" >> $(LIVE_INITRD).d/etc/passwd
echo "root::0:root" > $(LIVE_INITRD).d/etc/group
echo "include /etc/default/group.d/*" >> $(LIVE_INITRD).d/etc/group
(echo 'channel = $(CHANNEL)' && \
echo 'release_key = $(RELEASE_KEY)' && \
echo 'release_sig_url = $(RELEASE_SIG_URL)') > $(LIVE_INITRD).d/etc/upgrade.conf
mkdir -p $(LIVE_INITRD).d/home
mkdir -p $(LIVE_INITRD).d/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(LIVE_INITRD).d/root
@ -487,6 +518,42 @@ $(SORTIX_BUILDS_DIR):
# Bootable images
ifeq ($(ISO_MOUNT),yes)
$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(CHAIN_INITRD) $(CHAIN_INITRD).uuid $(SORTIX_BUILDS_DIR)
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot
cp "$(SYSROOT)/boot/sortix.bin" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.bin
cp $(CHAIN_INITRD) $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/sortix.initrd
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc
echo "UUID=`cat $(CHAIN_INITRD).uuid` / iso9660 ro 0 1" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/fstab
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/init
echo require single-user exit-code > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/init/default
echo "root::0:0:root:/root:sh" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/passwd
echo "include /etc/default/passwd.d/*" >> $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/passwd
echo "root::0:root" > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/group
echo "include /etc/default/group.d/*" >> $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/group
(echo 'channel = $(CHANNEL)' && \
echo 'release_key = $(RELEASE_KEY)' && \
echo 'release_sig_url = $(RELEASE_SIG_URL)') > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/etc/upgrade.conf
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/home
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root -m 700
cp -RT "$(SYSROOT)/etc/skel" $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root
(echo "You can view the documentation for new users by typing:" && \
echo && \
echo " man user-guide" && \
echo && \
echo "You can view the installation instructions by typing:" && \
echo && \
echo " man installation") > $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/root/welcome
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso/boot/grub
build-aux/iso-grub-cfg.sh --platform $(HOST) --version $(VERSION) --mount $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
grub-mkrescue -o $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso sysroot $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso -- -volid Sortix -volset_id `cat $(CHAIN_INITRD).uuid`
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
else
$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso: sysroot $(LIVE_INITRD) $(OVERLAY_INITRD) $(SRC_INITRD) $(SYSTEM_INITRD) $(SORTIX_BUILDS_DIR)
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
mkdir -p $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
@ -527,6 +594,8 @@ else # none
endif
rm -rf $(SORTIX_BUILDS_DIR)/$(BUILD_NAME)-iso
endif
.PHONY: iso
iso: $(SORTIX_BUILDS_DIR)/$(BUILD_NAME).iso

View File

@ -124,9 +124,9 @@ endif
DEFAULT_GENERIC_OPTLEVEL_BASE:=-Os -s
DEFAULT_OPTLEVEL_FOR_BUILD:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
ifeq ($(BUILD_IS_SORTIX),1)
DEFAULT_OPTLEVEL_FOR_BUILD+=
DEFAULT_OPTLEVEL_FOR_BUILD+=-fsanitize=undefined -fstack-protector-all
endif
DEFAULT_OPTLEVEL:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
ifeq ($(HOST_IS_SORTIX),1)
DEFAULT_OPTLEVEL+=
DEFAULT_OPTLEVEL+=-fsanitize=undefined -fstack-protector-all
endif

21
build-aux/config.mak Normal file
View File

@ -0,0 +1,21 @@
.PHONY: clean-tests
clean-tests:
rm -rf tests
rm -f config.h
.PHONY: clean
clean: clean-tests
config.h: $(addprefix tests/,$(addsuffix .h,$(TESTS)))
cat tests/*.h > config.h
tests/%.h: ../build-aux/tests/%.c
@if [ ! -d tests ]; then mkdir -p tests; fi
@ln -sf ../../build-aux/tests/$*.c tests/$*.c
@if $(CC) $(CFLAGS) $(CPPFLAGS) -Werror=incompatible-pointer-types -c tests/$*.c -o /dev/null 2>tests/$*.log; then \
echo "# tests/$*: Yes" && tail -n 1 $< > $@; \
else \
echo "# tests/$*: No" && true > $@; \
fi
-include ../build-aux/tests/*.d

View File

@ -23,8 +23,9 @@ set -e
this=$(which -- "$0")
thisdir=$(dirname -- "$this")
platform=
directory=
mount=false
platform=
version=
dashdash=
@ -44,8 +45,9 @@ for argument do
case $dashdash$argument in
--) dashdash=yes ;;
--platform=*) platform=$parameter ;;
--platform) previous_option=platform ;;
--platform=*) platform=$parameter ;;
--mount) mount=true ;;
--version=*) version=$parameter ;;
--version) previous_option=version ;;
-*) echo "$0: unrecognized option $argument" >&2
@ -111,13 +113,23 @@ isinset() {
cd "$directory"
kernel=$(maybe_compressed boot/sortix.bin)
live_initrd=$(maybe_compressed boot/live.initrd)
overlay_initrd=$(maybe_compressed boot/overlay.initrd)
src_initrd=$(maybe_compressed boot/src.initrd)
system_initrd=$(maybe_compressed boot/system.initrd)
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//')
if $mount; then
initrd=$(maybe_compressed boot/sortix.initrd)
initrds=$initrd
else
live_initrd=$(maybe_compressed boot/live.initrd)
overlay_initrd=$(maybe_compressed boot/overlay.initrd)
src_initrd=$(maybe_compressed boot/src.initrd)
system_initrd=$(maybe_compressed boot/system.initrd)
initrds="$system_initrd $src_initrd $live_initrd $overlay_initrd"
fi
if $mount; then
ports=
else
ports=$(ls repository |
grep -E '\.tix\.tar\.xz$' |
sed -E 's/\.tix\.tar\.xz$//')
fi
mkdir -p boot/grub
mkdir -p boot/grub/init
@ -183,6 +195,12 @@ fi
set version="$version"
set machine="$machine"
set mount=$mount
if \$mount; then
chain='-- /sbin/init --target=chain'
else
chain=
fi
set base_menu_title="Sortix \$version for \$machine"
set menu_title="\$base_menu_title"
set title_single_user='live environment'
@ -204,6 +222,8 @@ set enable_sshd=false
export version
export machine
export mount
export chain
export base_menu_title
export menu_title
export title_single_user
@ -304,9 +324,10 @@ esac
cat << EOF
hook_kernel_pre
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
multiboot /$kernel \$no_random_seed \$enable_network_drivers "\$@"
multiboot /$kernel \$no_random_seed \$enable_network_drivers \$chain "\$@"
echo done
hook_kernel_post
# TODO: Injecting configuration doesn't work for mounted cdroms.
if ! \$enable_dhclient; then
echo -n "Disabling dhclient ... "
module /boot/grub/init/furthermore --create-to /etc/init/network
@ -331,7 +352,7 @@ cat << EOF
echo done
fi
EOF
for initrd in $system_initrd $src_initrd $live_initrd $overlay_initrd; do
for initrd in $initrds; do
if [ "$initrd" = "$src_initrd" ]; then
cat << EOF
if \$enable_src; then
@ -427,9 +448,11 @@ menuentry "\$title_sysupgrade" '-- /sbin/init --target=sysupgrade'
cat << EOF
if ! $mount; then
menuentry "Select ports..." {
configfile /boot/grub/ports.cfg
}
fi
menuentry "Advanced..." {
configfile /boot/grub/advanced.cfg
@ -461,6 +484,7 @@ else
}
fi
if ! $mount; then
if "\$enable_src"; then
menuentry "Disable loading source code" {
enable_src=false
@ -472,6 +496,7 @@ else
configfile /boot/grub/advanced.cfg
}
fi
fi
if [ "\$enable_network_drivers" = --disable-network-drivers ]; then
menuentry "Enable networking drivers" {
@ -485,6 +510,7 @@ else
}
fi
if ! $mount; then
if \$enable_dhclient; then
menuentry "Disable DHCP client" {
enable_dhclient=false
@ -524,6 +550,7 @@ fi
menuentry "Select binary packages..." {
configfile /boot/grub/tix.cfg
}
fi
hook_advanced_menu_post
EOF

View File

@ -1,3 +1,3 @@
set_minimal="cut dash e2fsprogs grep grub mdocml sed xargs"
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libssl libstdc++ nano ntpd make patch pkg-config python ssh tar vim wget xz xorriso"
set_minimal="cut dash e2fsprogs grep grub libssl mdocml sed signify tar wget xargs xorriso xz"
set_basic="$set_minimal binutils bison bzip2 diffutils ed flex gawk gcc git gzip libcurl libcurses libstdc++ make m4 nano ntpd patch perl pkg-config python ssh texinfo vim"
sets="basic minimal"

View File

@ -0,0 +1,8 @@
#include <string.h>
int main(void)
{
void (*ptr)(void*, size_t) = explicit_bzero;
return ptr ? 0 : 1;
}
#define HAVE_EXPLICIT_BZERO 1

View File

@ -0,0 +1,8 @@
#include <stdlib.h>
int main(void)
{
void* (*ptr)(void*, size_t, size_t) = reallocarray;
return ptr ? 0 : 1;
}
#define HAVE_REALLOCARRAY 1

View File

@ -0,0 +1,8 @@
#include <string.h>
int main(void)
{
size_t (*ptr)(char*, const char*, size_t) = strlcat;
return ptr ? 0 : 1;
}
#define HAVE_STRLCAT 1

View File

@ -0,0 +1,8 @@
#include <string.h>
int main(void)
{
size_t (*ptr)(char*, const char*, size_t) = strlcpy;
return ptr ? 0 : 1;
}
#define HAVE_STRLCPY 1

View File

@ -1,2 +1,6 @@
VERSION=1.1dev
CHANNEL?=volatile
RELEASE?=$(VERSION)
RELEASE_MASTER?=https://sortix.org/release
RELEASE_KEY=/etc/signify/sortix-$(VERSION).pub
RELEASE_SIG_URL?=$(RELEASE_MASTER)/$(CHANNEL)/$(HOST_MACHINE).sh.sig

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWQiTQbFzyZJVobf/pn53Jp3njhRB9DgwkMaNakCpDE9RaTABMjlbz9W

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWQnkSm9lj1YIZYpt1Y3mHYzFsaky82gQF6CrW4lme9OoEYzSIl2ZsIC

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWTGrBXmGvl2zUpCa47ui5EyPsnitKLjsCZ2YZphNY8F3b33t6QWYDs1

View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWRTbLQ+3+a9I5yche2BEVP03TRtumGO4Vgq1AQ/5bRj8JAJ1R0+vpxE

1
host/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
host

25
host/Makefile Normal file
View File

@ -0,0 +1,25 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CFLAGS += -Wall -Wextra
BINARIES = host
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARIES) $(DESTDIR)$(BINDIR)
%: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
clean:
rm -f $(BINARIES)

511
host/host.c Normal file
View File

@ -0,0 +1,511 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* host.c
* Domain name system client.
*/
#include <sys/dnsconfig.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <endian.h>
#include <err.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DNS_SIZE 512
#define DNS_NAME_MAX 255
#define DNS_LABEL_MAX 64
struct dns_header
{
uint16_t id;
uint16_t flags;
uint16_t qdcount;
uint16_t ancount;
uint16_t nscount;
uint16_t arcount;
};
struct dns_question
{
uint16_t qtype;
uint16_t qclass;
};
struct dns_record
{
uint16_t type;
uint16_t class;
uint16_t ttl_high;
uint16_t ttl_low;
uint16_t rdlength;
};
#define DNS_HEADER_FLAGS_RCODE_MASK (0xF << 0)
#define DNS_HEADER_FLAGS_RCODE_NO (0 << 0)
#define DNS_HEADER_FLAGS_RCODE_FORMAT (1 << 0)
#define DNS_HEADER_FLAGS_RCODE_SERVER (2 << 0)
#define DNS_HEADER_FLAGS_RCODE_NAME (3 << 0)
#define DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED (4 << 0)
#define DNS_HEADER_FLAGS_RCODE_REFUSED (5 << 0)
#define DNS_HEADER_FLAGS_RA (1 << 7)
#define DNS_HEADER_FLAGS_RD (1 << 8)
#define DNS_HEADER_FLAGS_TC (1 << 9)
#define DNS_HEADER_FLAGS_AA (1 << 10)
#define DNS_HEADER_FLAGS_OPCODE_MASK (0xF << 11)
#define DNS_HEADER_FLAGS_OPCODE_QUERY (0 << 11)
#define DNS_HEADER_FLAGS_OPCODE_IQUERY (1 << 11)
#define DNS_HEADER_FLAGS_OPCODE_STATUS (2 << 11)
#define DNS_HEADER_FLAGS_QR (1 << 15)
#define DNS_TYPE_A 1
#define DNS_TYPE_NS 2
#define DNS_TYPE_MD 3
#define DNS_TYPE_MF 4
#define DNS_TYPE_CNAME 5
#define DNS_TYPE_SOA 6
#define DNS_TYPE_MB 7
#define DNS_TYPE_MG 8
#define DNS_TYPE_MR 9
#define DNS_TYPE_NULL 10
#define DNS_TYPE_WKS 11
#define DNS_TYPE_PTR 12
#define DNS_TYPE_HINFO 13
#define DNS_TYPE_MINFO 14
#define DNS_TYPE_MX 15
#define DNS_TYPE_TXT 16
#define DNS_TYPE_AAAA 28
#define DNS_QTYPE_AXFR 252
#define DNS_QTYPE_MAILB 253
#define DNS_QTYPE_MAILA 254
#define DNS_QTYPE_ANY 255
#define DNS_CLASS_IN 1
#define DNS_CLASS_CS 2
#define DNS_CLASS_CH 3
#define DNS_CLASS_HS 4
#define DNS_QCLASS_ANY 255
static size_t encode_dns_header(unsigned char* msg,
size_t offset,
const struct dns_header* hdrin)
{
struct dns_header hdr;
if ( DNS_SIZE - offset < sizeof(hdr) )
errx(1, "dns message too large");
hdr.id = htobe16(hdrin->id);
hdr.flags = htobe16(hdrin->flags);
hdr.qdcount = htobe16(hdrin->qdcount);
hdr.ancount = htobe16(hdrin->ancount);
hdr.nscount = htobe16(hdrin->nscount);
hdr.arcount = htobe16(hdrin->arcount);
memcpy(msg + offset, &hdr, sizeof(hdr));
return offset + sizeof(hdr);
}
static size_t encode_dns_byte(unsigned char* msg,
size_t offset,
unsigned char byte)
{
if ( DNS_SIZE - offset < 1 )
errx(1, "dns message too large");
msg[offset] = byte;
return offset + 1;
}
static size_t encode_dns_name(unsigned char* msg,
size_t offset,
const char* name)
{
size_t index = 0;
size_t namelen = 0;
if ( !name[0] )
errx(1, "'%s' is not a valid name (unexpected end of input)", name);
while ( name[index] )
{
if ( !strcmp(name + index, ".") )
break;
if ( name[index] == '.' )
errx(1, "'%s' is not a valid name (empty label)", name);
size_t length = strcspn(name + index, ".");
if ( DNS_LABEL_MAX <= length )
errx(1, "'%s' is not a valid name (label too long)", name);
if ( namelen++ == DNS_NAME_MAX )
errx(1, "'%s' is not a valid name (name is too long)", name);
offset = encode_dns_byte(msg, offset, length & 0xFF);
for ( size_t i = 0; i < length; i++ )
{
if ( namelen++ == DNS_NAME_MAX )
errx(1, "'%s' is not a valid name (name is too long)", name);
offset = encode_dns_byte(msg, offset, name[index + i]);
}
index += length;
if ( name[index] == '.' )
index++;
}
if ( namelen++ == DNS_NAME_MAX )
errx(1, "'%s' is not a valid name (name is too long)", name);
offset = encode_dns_byte(msg, offset, 0);
return offset;
}
static size_t encode_dns_question(unsigned char* msg,
size_t offset,
const char* name,
const struct dns_question* qsin)
{
offset = encode_dns_name(msg, offset, name);
struct dns_question qs;
if ( DNS_SIZE - offset < sizeof(qs) )
errx(1, "dns message too large");
qs.qtype = htobe16(qsin->qtype);
qs.qclass = htobe16(qsin->qclass);
memcpy(msg + offset, &qs, sizeof(qs));
return offset + sizeof(qs);
}
static size_t decode_dns_header(const unsigned char* msg,
size_t offset,
size_t msg_size,
struct dns_header* hdrout)
{
struct dns_header hdr;
if ( msg_size - offset < sizeof(hdr) )
errx(1, "dns message too small");
memcpy(&hdr, msg + offset, sizeof(hdr));
hdrout->id = be16toh(hdr.id);
hdrout->flags = be16toh(hdr.flags);
hdrout->qdcount = be16toh(hdr.qdcount);
hdrout->ancount = be16toh(hdr.ancount);
hdrout->nscount = be16toh(hdr.nscount);
hdrout->arcount = be16toh(hdr.arcount);
return offset + sizeof(hdr);
}
static size_t decode_dns_byte(const unsigned char* msg,
size_t offset,
size_t msg_size,
unsigned char* byte)
{
if ( msg_size <= offset || msg_size - offset < 1 )
errx(1, "dns message too small");
*byte = msg[offset];
return offset + 1;
}
static size_t decode_dns_name(const unsigned char* msg,
size_t offset,
size_t msg_size,
char* name)
{
bool real_offset_set = false;
size_t real_offset = 0;
size_t index = 0;
size_t namelen = 0;
uint8_t b;
while ( true )
{
if ( namelen++ == DNS_NAME_MAX )
errx(1, "name too long");
offset = decode_dns_byte(msg, offset, msg_size, &b);
if ( 0xC0 & b )
{
namelen--;
size_t ptr = (b & 0x3F) << 8;
offset = decode_dns_byte(msg, offset, msg_size, &b);
ptr |= b;
if ( !real_offset_set )
{
real_offset = offset;
real_offset_set = true;
}
offset = ptr;
continue;
}
size_t length = b;
if ( DNS_LABEL_MAX <= length )
errx(1, "label too long");
if ( !length )
break;
if ( index )
name[index++] = '.';
for ( size_t i = 0; i < length; i++ )
{
if ( namelen++ == DNS_NAME_MAX )
errx(1, "name too long");
offset = decode_dns_byte(msg, offset, msg_size, &b);
// TODO: Handle if b == '.'.
name[index++] = b;
}
}
name[index++] = '.';
name[index] = '\0';
if ( real_offset_set )
return real_offset;
return offset;
}
static size_t decode_dns_question(const unsigned char* msg,
size_t offset,
size_t msg_size,
char* name,
struct dns_question* qsout)
{
offset = decode_dns_name(msg, offset, msg_size, name);
struct dns_question qs;
if ( msg_size <= offset || msg_size - offset < sizeof(qs) )
errx(1, "dns message too small");
memcpy(&qs, msg + offset, sizeof(qs));
qsout->qtype = be16toh(qs.qtype);
qsout->qclass = be16toh(qs.qclass);
return offset + sizeof(qs);
}
static size_t decode_dns_record(const unsigned char* msg,
size_t offset,
size_t msg_size,
char* name,
struct dns_record* rrout)
{
offset = decode_dns_name(msg, offset, msg_size, name);
struct dns_record rr;
if ( msg_size <= offset || msg_size - offset < sizeof(rr) )
errx(1, "dns message too small");
memcpy(&rr, msg + offset, sizeof(rr));
rrout->type = be16toh(rr.type);
rrout->class = be16toh(rr.class);
rrout->ttl_high = be16toh(rr.ttl_high);
rrout->ttl_low = be16toh(rr.ttl_low);
rrout->rdlength = be16toh(rr.rdlength);
return offset + sizeof(rr);
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
int main(int argc, char* argv[])
{
int ipv = 4;
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
char c;
while ( (c = *++arg) ) switch ( c )
{
case '4': ipv = 4; break;
case '6': ipv = 6; break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
exit(1);
}
}
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
exit(1);
}
}
compact_arguments(&argc, &argv);
if ( argc < 2 )
errx(1, "No host given");
const char* host = argv[1];
char nsipstr[INET_ADDRSTRLEN];
const char* nameserver;
if ( argc < 3 )
{
struct dnsconfig dnscfg;
if ( getdnsconfig(&dnscfg) < 0 )
err(1, "dnsconfig");
bool found = false;
for ( size_t i = 0; !found && i < dnscfg.servers_count; i++ )
{
if ( dnscfg.servers[i].family != AF_INET )
continue;
inet_ntop(AF_INET, &dnscfg.servers[i].addr, nsipstr, sizeof(nsipstr));
found = true;
}
if ( !found )
errx(1, "No nameserver given and no default configured");
nameserver = nsipstr;
}
else
nameserver = argv[2];
int port = 4 <= argc ? atoi(argv[3]) : 53;
if ( 5 <= argc )
errx(1, "Unexpected extra operand");
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if ( fd < 0 )
err(1, "socket");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htobe16(port);
if ( inet_pton(AF_INET, nameserver, &addr.sin_addr) < 1 )
errx(1, "invalid ip address: %s", nameserver);
if ( connect(fd, (const struct sockaddr*) &addr, sizeof(addr)) < 0 )
err(1, "connect");
unsigned char req[DNS_SIZE];
size_t req_size = 0;
struct dns_header hdr;
hdr.id = 0;
hdr.flags = DNS_HEADER_FLAGS_RD;
hdr.qdcount = 1;
hdr.ancount = 0;
hdr.nscount = 0;
hdr.arcount = 0;
req_size = encode_dns_header(req, req_size, &hdr);
struct dns_question qs;
qs.qtype = ipv == 4 ? DNS_TYPE_A : DNS_TYPE_AAAA;
qs.qclass = DNS_CLASS_IN;
req_size = encode_dns_question(req, req_size, host, &qs);
ssize_t amount = send(fd, req, req_size, 0);
if ( amount < 0 )
err(1, "send");
// TODO: Use recvfrom to get the server ip.
unsigned char resp[DNS_SIZE];
ssize_t resp_size = recv(fd, resp, sizeof(resp), 0);
if ( resp_size < 0 )
err(1, "recv");
// TODO: Verify the response came from the correct ip.
size_t offset = 0;
offset = decode_dns_header(resp, offset, resp_size, &hdr);
// TODO: Verify the response has the correct id.
#if 0
printf("id = %u\n", hdr.id);
printf("flags = 0x%X\n", hdr.flags);
printf("qdcount = %u\n", hdr.qdcount);
printf("ancount = %u\n", hdr.ancount);
printf("nscount = %u\n", hdr.nscount);
printf("arcount = %u\n", hdr.arcount);
#endif
uint16_t rcode = hdr.flags & DNS_HEADER_FLAGS_RCODE_MASK;
if ( rcode == DNS_HEADER_FLAGS_RCODE_FORMAT )
errx(1, "format error");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_SERVER )
errx(1, "server error");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NAME )
errx(1, "no such name");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_NOT_IMPLEMENTED )
errx(1, "not implemented error");
else if ( rcode == DNS_HEADER_FLAGS_RCODE_REFUSED )
errx(1, "refused");
else if ( rcode != DNS_HEADER_FLAGS_RCODE_NO )
errx(1, "unknown error (rcode=0x%X)", rcode);
if ( hdr.flags & DNS_HEADER_FLAGS_TC )
errx(1, "truncated");
// TODO: Check query bit.
for ( uint16_t i = 0; i < hdr.qdcount; i++ )
{
char name[DNS_NAME_MAX + 1];
offset = decode_dns_question(resp, offset, resp_size, name, &qs);
//printf("%s type=%u class=%u\n", name, qs.qtype, qs.qclass);
}
for ( uint16_t i = 0; i < hdr.ancount; i++ )
{
char name[DNS_NAME_MAX + 1];
struct dns_record rr;
offset = decode_dns_record(resp, offset, resp_size, name, &rr);
uint32_t ttl = (uint32_t) rr.ttl_high << 16 | rr.ttl_low;
printf("%s type=%u class=%u ttl=%u ", name, rr.type, rr.class, ttl);
if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_A )
{
unsigned char ip[4];
for ( size_t i = 0; i < 4; i++ )
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
printf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
}
else if ( rr.class == DNS_CLASS_IN && rr.type == DNS_TYPE_AAAA )
{
unsigned char ip[16];
for ( size_t i = 0; i < 16; i++ )
offset = decode_dns_byte(resp, offset, resp_size, &ip[i]);
for ( size_t i = 0; i < 16; i++ )
{
if ( i && !(i & 1) )
putchar(':');
printf("%02x", ip[i]);
}
}
else if ( rr.type == DNS_TYPE_CNAME )
{
char cname[DNS_NAME_MAX + 1];
offset = decode_dns_name(resp, offset, resp_size, cname);
printf("CNAME %s", cname);
}
else
{
printf("0x");
fflush(stdout);
for ( size_t i = 0; i < rr.rdlength; i++ )
{
unsigned char b;
offset = decode_dns_byte(resp, offset, resp_size, &b);
if ( isprint(b) && b != '\'' )
printf("'%c'", b);
else
printf("%02X", b);
fflush(stdout);
}
}
printf("\n");
}
return 0;
}

4
irc/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
irc
*.o
config.h
tests

56
irc/Makefile Normal file
View File

@ -0,0 +1,56 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CFLAGS += -Wall -Wextra
CPPFLAGS += -DVERSIONSTR=\"$(VERSION)\"
ifeq ($(HOST_IS_SORTIX),0)
CPPFLAGS+=-D_GNU_SOURCE
endif
BINARY = irc
#MANPAGES1 = irc.1
OBJS=\
compat.o \
connection.o \
database.o \
irc.o \
scrollback.o \
string.o \
ui.o \
all: $(BINARY)
.PHONY: all install clean
$(OBJS): config.h
%.o: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
$(BINARY): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(BINARY) $(LIBS)
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARY) $(DESTDIR)$(BINDIR)
#mkdir -p $(DESTDIR)$(MANDIR)/man1
#install $(MANPAGES1) $(DESTDIR)$(MANDIR)/man1
clean:
rm -f $(BINARY)
rm -f $(OBJS) *.o
TESTS=\
have-explicit_bzero \
have-reallocarray \
have-strlcat \
have-strlcpy \
include ../build-aux/config.mak

65
irc/compat.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* compat.c
* Compatibility.
*/
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "compat.h"
#ifndef HAVE_EXPLICIT_BZERO
void explicit_bzero(void* buffer, size_t size)
{
memset(buffer, 0, size);
}
#endif
#ifndef HAVE_REALLOCARRAY
void* reallocarray(void* ptr, size_t nmemb, size_t size)
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
return realloc(ptr, nmemb * size);
}
#endif
#ifndef HAVE_STRLCAT
size_t strlcat(char* restrict dest, const char* restrict src, size_t size)
{
size_t dest_len = strnlen(dest, size);
if ( size <= dest_len )
return dest_len + strlen(src);
return dest_len + strlcpy(dest + dest_len, src, size - dest_len);
}
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict dest, const char* restrict src, size_t size)
{
if ( !size )
return strlen(src);
size_t result;
for ( result = 0; result < size-1 && src[result]; result++ )
dest[result] = src[result];
dest[result] = '\0';
return result + strlen(src + result);
}
#endif

40
irc/compat.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* combat.h
* Compatibility.
*/
#ifndef COMPAT_H
#define COMPAT_H
#include <stddef.h>
#include "config.h"
#ifndef HAVE_EXPLICIT_BZERO
void explicit_bzero(void*, size_t);
#endif
#ifndef HAVE_REALLOCARRAY
void* reallocarray(void*, size_t, size_t);
#endif
#ifndef HAVE_STRLCAT
size_t strlcat(char* restrict, const char* restrict, size_t);
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict, const char* restrict, size_t);
#endif
#endif

549
irc/connection.c Normal file
View File

@ -0,0 +1,549 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* connection.c
* IRC protocol.
*/
#include <sys/socket.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "compat.h"
#include "connection.h"
void dump_error(const char* message,
size_t message_size,
const struct timespec* when)
{
// TODO: Send the error somewhere appropriate in the UI.
fprintf(stderr, "\e[91m");
struct tm tm;
gmtime_r(&when->tv_sec, &tm);
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
when->tv_nsec);
for ( size_t i = 0; i < message_size; i++ )
{
if ( message[i] == '\r' )
continue;
else if ( message[i] == '\n' )
continue;
else if ( (unsigned char) message[i] < 32 )
{
fprintf(stderr, "\e[31m");
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
fprintf(stderr, "\e[91m");
}
fputc((unsigned char) message[i], stderr);
}
fprintf(stderr, "\e[m\n");
}
void dump_outgoing(const char* message,
size_t message_size,
const struct timespec* when)
{
return; // TODO: Remove this, or adopt it for a logging mechanism.
fprintf(stderr, "\e[92m");
struct tm tm;
gmtime_r(&when->tv_sec, &tm);
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
when->tv_nsec);
for ( size_t i = 0; i < message_size; i++ )
{
if ( message[i] == '\r' )
continue;
else if ( message[i] == '\n' )
continue;
else if ( (unsigned char) message[i] < 32 )
{
fprintf(stderr, "\e[91m");
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
fprintf(stderr, "\e[92m");
continue;
}
fputc((unsigned char) message[i], stderr);
}
fprintf(stderr, "\e[m\n");
}
void dump_incoming(const char* message,
size_t message_size,
const struct timespec* when)
{
return; // TODO: Remove this, or adopt it for a logging mechanism.
fprintf(stderr, "\e[93m");
struct tm tm;
gmtime_r(&when->tv_sec, &tm);
fprintf(stderr, "[%i-%02i-%02i %02i:%02i:%02i %09li] ",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
when->tv_nsec);
for ( size_t i = 0; i < message_size; i++ )
{
if ( message[i] == '\r' )
continue;
else if ( message[i] == '\n' )
continue;
else if ( (unsigned char) message[i] < 32 )
{
fprintf(stderr, "\e[91m");
fprintf(stderr, "\\x%02X", (unsigned char) message[i]);
fprintf(stderr, "\e[93m");
continue;
}
fputc((unsigned char) message[i], stderr);
}
fprintf(stderr, "\e[m\n");
}
void irc_error_vlinef(const char* format, va_list ap_orig)
{
va_list ap;
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
va_copy(ap, ap_orig);
char* string;
if ( 0 <= vasprintf(&string, format, ap) )
{
va_end(ap);
dump_error(string, strlen(string), &now);
free(string);
return;
}
va_end(ap);
char buffer[512];
va_copy(ap, ap_orig);
if ( 0 <= vsnprintf(buffer, sizeof(buffer), format, ap) )
{
va_end(ap);
dump_error(buffer, strlen(buffer), &now);
dump_error("(vasprintf failed printing that line)",
strlen("(vasprintf failed printing that line)"), &now);
return;
}
va_end(ap);
dump_error(format, strlen(format), &now);
dump_error("(vsnprintf failed printing format string)",
strlen("(vsnprintf failed printing that format string)"), &now);
}
void irc_error_linef(const char* format, ...)
{
va_list ap;
va_start(ap, format);
irc_error_vlinef(format, ap),
va_end(ap);
}
void irc_transmit(struct irc_connection* irc_connection,
const char* message,
size_t message_size)
{
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
if ( irc_connection->connectivity_error )
return;
dump_outgoing(message, message_size, &now);
int fd = irc_connection->fd;
while ( message_size )
{
ssize_t amount = 0;
amount = send(fd, message, message_size, MSG_NOSIGNAL);
if ( amount < 0 || amount == 0 )
{
warn("send");
irc_connection->connectivity_error = true;
return;
}
message += amount;
message_size -= amount;
}
}
void irc_transmit_message(struct irc_connection* irc_connection,
const char* message,
size_t message_size)
{
assert(2 <= message_size);
assert(message[message_size - 2] == '\r');
assert(message[message_size - 1] == '\n');
char buffer[512];
if ( 512 < message_size )
{
memcpy(buffer, message, 510);
buffer[510] = '\r';
buffer[511] = '\n';
message = buffer;
message_size = 512;
}
irc_transmit(irc_connection, message, message_size);
explicit_bzero(buffer, sizeof(buffer));
}
void irc_receive_more_bytes(struct irc_connection* irc_connection)
{
if ( irc_connection->connectivity_error )
return;
int fd = irc_connection->fd;
char* buffer = irc_connection->incoming_buffer;
size_t buffer_size = sizeof(irc_connection->incoming_buffer);
size_t buffer_used = irc_connection->incoming_amount;
size_t buffer_free = buffer_size - buffer_used;
if ( buffer_free == 0 )
return;
// TODO: Use non-blocking IO for transmitting as well so O_NONBLOCK can
// always be used.
// TODO: Use MSG_DONTWAIT when supported in Sortix.
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
ssize_t amount = recv(fd, buffer + buffer_used, buffer_free, 0);
fcntl(fd, F_SETFL, flags);
if ( amount < 0 )
{
if ( errno == EAGAIN || errno == EWOULDBLOCK )
return;
warn("recv");
irc_connection->connectivity_error = true;
}
else if ( amount == 0 )
{
// TODO: Gracefully close the connection.
irc_connection->connectivity_error = true;
}
else
{
irc_connection->incoming_amount += amount;
}
}
void irc_receive_pop_bytes(struct irc_connection* irc_connection,
char* buffer,
size_t count)
{
assert(count <= irc_connection->incoming_amount);
memcpy(buffer, irc_connection->incoming_buffer, count);
explicit_bzero(irc_connection->incoming_buffer, count);
memmove(irc_connection->incoming_buffer,
irc_connection->incoming_buffer + count,
irc_connection->incoming_amount - count);
irc_connection->incoming_amount -= count;
explicit_bzero(irc_connection->incoming_buffer + irc_connection->incoming_amount,
count);
}
bool irc_receive_message(struct irc_connection* irc_connection,
char message[512],
struct timespec* when)
{
if ( irc_connection->connectivity_error )
return false;
size_t message_usable = 0;
while ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] != '\r' &&
irc_connection->incoming_buffer[message_usable] != '\n' )
message_usable++;
if ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] == '\r')
{
message_usable++;
if ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] == '\n' )
{
message_usable++;
irc_receive_pop_bytes(irc_connection, message, message_usable);
assert(message[message_usable-2] == '\r');
assert(message[message_usable-1] == '\n');
message[message_usable-2] = '\0';
message[message_usable-1] = '\0';
// TODO: This is not always when the message did arrive.
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
dump_incoming(message, message_usable-2, &now);
*when = now;
return true;
}
else if ( message_usable < irc_connection->incoming_amount )
{
// TODO: Handle bad newline sequence.
warnx("recv: bad IRC newline");
irc_connection->connectivity_error = true;
return false;
}
}
else if ( message_usable < irc_connection->incoming_amount &&
irc_connection->incoming_buffer[message_usable] == '\n' )
{
// TODO: Handle bad newline sequence.
warnx("recv: bad IRC newline");
irc_connection->connectivity_error = true;
return false;
}
if ( message_usable == 512 )
{
// TODO: Handle untruncated lines from the server.
warnx("recv: overlong IRC line from server");
irc_connection->connectivity_error = true;
return false;
}
return false;
}
void irc_transmit_string(struct irc_connection* irc_connection,
const char* string)
{
char message[512];
strncpy(message, string, 510);
message[510] = '\0';
message[511] = '\0';
size_t string_truncated_length = strlen(message);
for ( size_t i = 0; i < string_truncated_length; i++ )
{
if ( message[i] == '\r' )
message[i] = ' ';
if ( message[i] == '\n' )
message[i] = ' ';
}
message[string_truncated_length + 0] = '\r';
message[string_truncated_length + 1] = '\n';
size_t message_length = strnlen(message, 512);
irc_transmit_message(irc_connection, message, message_length);
explicit_bzero(message, sizeof(message));
}
__attribute__((format(printf, 2, 0)))
void irc_transmit_vformat(struct irc_connection* irc_connection,
const char* format,
va_list ap)
{
char* string = NULL;
if ( vasprintf(&string, format, ap) < 0 )
{
// TODO: Hmm, what do we do here.
warn("vasprintf");
// TODO: Should the error condition be set?
return;
}
irc_transmit_string(irc_connection, string);
explicit_bzero(string, strlen(string));
free(string);
}
__attribute__((format(printf, 2, 3)))
void irc_transmit_format(struct irc_connection* irc_connection,
const char* format,
...)
{
va_list ap;
va_start(ap, format);
irc_transmit_vformat(irc_connection, format, ap);
va_end(ap);
}
void irc_command_pass(struct irc_connection* irc_connection,
const char* password)
{
irc_transmit_format(irc_connection, "PASS :%s", password);
}
void irc_command_nick(struct irc_connection* irc_connection,
const char* nick)
{
irc_transmit_format(irc_connection, "NICK :%s", nick);
}
void irc_command_user(struct irc_connection* irc_connection,
const char* nick,
const char* local_hostname,
const char* server_hostname,
const char* real_name)
{
// TODO: What if there are spaces in some of these fields?
irc_transmit_format(irc_connection, "USER %s %s %s :%s",
nick, local_hostname, server_hostname, real_name);
}
void irc_command_join(struct irc_connection* irc_connection,
const char* channel)
{
irc_transmit_format(irc_connection, "JOIN :%s", channel);
}
void irc_command_part(struct irc_connection* irc_connection,
const char* channel)
{
irc_transmit_format(irc_connection, "PART :%s", channel);
}
void irc_command_privmsg(struct irc_connection* irc_connection,
const char* where,
const char* what)
{
// TODO: Ensure where is valid.
irc_transmit_format(irc_connection, "PRIVMSG %s :%s", where, what);
}
void irc_command_privmsgf(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...)
{
va_list ap;
va_start(ap, what_format);
char msg[512];
vsnprintf(msg, sizeof(msg), what_format, ap);
irc_command_privmsg(irc_connection, where, msg);
va_end(ap);
}
void irc_command_notice(struct irc_connection* irc_connection,
const char* where,
const char* what)
{
// TODO: Ensure where is valid.
irc_transmit_format(irc_connection, "NOTICE %s :%s", where, what);
}
void irc_command_noticef(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...)
{
va_list ap;
va_start(ap, what_format);
char msg[512];
vsnprintf(msg, sizeof(msg), what_format, ap);
irc_command_notice(irc_connection, where, msg);
va_end(ap);
}
void irc_command_kick(struct irc_connection* irc_connection,
const char* where,
const char* who,
const char* why)
{
// TODO: Ensure where and who are valid.
if ( why )
irc_transmit_format(irc_connection, "KICK %s %s :%s", where, who, why);
else
irc_transmit_format(irc_connection, "KICK %s %s", where, who);
}
void irc_command_quit(struct irc_connection* irc_connection,
const char* message)
{
if ( message )
irc_transmit_format(irc_connection, "QUIT :%s", message);
else
irc_transmit_string(irc_connection, "QUIT");
shutdown(irc_connection->fd, SHUT_WR);
}
void irc_command_quit_malfunction(struct irc_connection* irc_connection,
const char* message)
{
if ( message )
irc_transmit_format(irc_connection, "QUIT :%s", message);
else
irc_transmit_string(irc_connection, "QUIT");
shutdown(irc_connection->fd, SHUT_RDWR);
}
void irc_parse_message_parameter(char* message,
char* parameters[16],
size_t* num_parameters_ptr)
{
size_t num_parameters = 0;
while ( message[0] != '\0' )
{
if ( message[0] == ':' || num_parameters == (16-1) -1 )
{
message++;
parameters[num_parameters++] = message;
break;
}
parameters[num_parameters++] = message;
size_t usable = 0;
while ( message[usable] != '\0' && message[usable] != ' ' )
usable++;
char lc = message[usable];
message[usable] = '\0';
if ( lc != '\0' )
message += usable + 1;
else
message += usable;
}
*num_parameters_ptr = num_parameters;
}
void irc_parse_who(char* full, const char** who, const char** whomask)
{
size_t bangpos = strcspn(full, "!");
if ( full[bangpos] == '!' )
{
full[bangpos] = '\0';
*who = full;
*whomask = full + bangpos + 1;
}
else
{
*who = full;
*whomask = "";
}
}

102
irc/connection.h Normal file
View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* connection.h
* IRC protocol.
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <time.h>
struct irc_connection
{
int fd;
bool connectivity_error;
char incoming_buffer[512];
size_t incoming_amount;
};
__attribute__((format(printf, 1, 0)))
void irc_error_vlinef(const char* format, va_list ap);
__attribute__((format(printf, 1, 2)))
void irc_error_linef(const char* format, ...);
void irc_transmit(struct irc_connection* irc_connection,
const char* message,
size_t message_size);
void irc_transmit_message(struct irc_connection* irc_connection,
const char* message,
size_t message_size);
void irc_transmit_string(struct irc_connection* irc_connection,
const char* string);
__attribute__((format(printf, 2, 0)))
void irc_transmit_vformat(struct irc_connection* irc_connection,
const char* format,
va_list ap);
__attribute__((format(printf, 2, 3)))
void irc_transmit_format(struct irc_connection* irc_connection,
const char* format,
...);
void irc_receive_more_bytes(struct irc_connection* irc_connection);
bool irc_receive_message(struct irc_connection* irc_connection,
char message[512],
struct timespec* when);
void irc_command_pass(struct irc_connection* irc_connection,
const char* password);
void irc_command_nick(struct irc_connection* irc_connection,
const char* nick);
void irc_command_user(struct irc_connection* irc_connection,
const char* nick,
const char* local_hostname,
const char* server_hostname,
const char* real_name);
void irc_command_join(struct irc_connection* irc_connection,
const char* channel);
void irc_command_part(struct irc_connection* irc_connection,
const char* channel);
void irc_command_privmsg(struct irc_connection* irc_connection,
const char* where,
const char* what);
__attribute__((format(printf, 3, 4)))
void irc_command_privmsgf(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...);
void irc_command_notice(struct irc_connection* irc_connection,
const char* where,
const char* what);
__attribute__((format(printf, 3, 4)))
void irc_command_noticef(struct irc_connection* irc_connection,
const char* where,
const char* what_format,
...);
void irc_command_kick(struct irc_connection* irc_connection,
const char* where,
const char* who,
const char* why);
void irc_command_quit(struct irc_connection* irc_connection,
const char* message);
void irc_command_quit_malfunction(struct irc_connection* irc_connection,
const char* message);
void irc_parse_message_parameter(char* message,
char* parameters[16],
size_t* num_parameters_ptr);
void irc_parse_who(char* full, const char** who, const char** whomask);
#endif

237
irc/database.c Normal file
View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* database.c
* Data structure for keeping track of channels and people.
*/
#include <sys/types.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "database.h"
#include "network.h"
#include "string.h"
struct channel* find_channel(const struct network* state, const char* channel_name)
{
assert(channel_name);
for ( struct channel* channel = state->channels; channel; channel = channel->next_channel )
if ( strchannelcmp(channel->name, channel_name) == 0 )
return channel;
return NULL;
}
struct channel* add_channel(struct network* state, const char* channel_name)
{
assert(channel_name);
assert(!find_channel(state, channel_name));
struct channel* channel = (struct channel*) calloc(sizeof(struct channel), 1);
if ( !channel )
return NULL;
channel->name = strdup(channel_name);
if ( !channel->name )
return free(channel), (struct channel*) NULL;
channel->people = NULL;
channel->prev_channel = NULL;
channel->next_channel = state->channels;
if ( state->channels )
state->channels->prev_channel = channel;
state->channels = channel;
return channel;
}
struct channel* get_channel(struct network* state, const char* channel_name)
{
assert(channel_name);
for ( struct channel* result = find_channel(state, channel_name); result; result = NULL )
return result;
return add_channel(state, channel_name);
}
void remove_channel(struct network* state, struct channel* channel)
{
while ( channel->people )
remove_person_from_channel(state, channel->people);
if ( channel->prev_channel )
channel->prev_channel->next_channel = channel->next_channel;
else
state->channels = channel->next_channel;
if ( channel->next_channel )
channel->next_channel->prev_channel = channel->prev_channel;
free(channel->name);
free(channel);
}
struct person* find_person(const struct network* state, const char* nick)
{
assert(nick);
for ( struct person* person = state->people; person; person = person->next_person )
if ( strnickcmp(person->nick, nick) == 0 )
return person;
return NULL;
}
struct person* add_person(struct network* state, const char* nick)
{
assert(nick);
assert(!find_person(state, nick));
struct person* person = (struct person*) calloc(sizeof(struct person), 1);
if ( !person )
return NULL;
person->nick = strdup(nick);
if ( !person->nick )
return free(person), (struct person*) NULL;
person->prev_person = NULL;
person->next_person = state->people;
if ( state->people )
state->people->prev_person = person;
state->people = person;
return person;
}
struct person* get_person(struct network* state, const char* nick)
{
assert(nick);
for ( struct person* result = find_person(state, nick); result; result = NULL )
return result;
return add_person(state, nick);
}
void remove_person(struct network* state, struct person* person)
{
while ( person->channels )
remove_person_from_channel(state, person->channels);
if ( person->prev_person )
person->prev_person->next_person = person->next_person;
else
state->people = person->next_person;
if ( person->next_person )
person->next_person->prev_person = person->prev_person;
free(person->nick);
free(person);
}
struct channel_person* find_person_in_channel(const struct network* state, const char* nick, const char* channel_name)
{
assert(nick);
assert(channel_name);
struct channel* channel = find_channel(state, channel_name);
if ( !channel )
return NULL;
for ( struct channel_person* channel_person = channel->people; channel_person; channel_person = channel_person->next_person_in_channel )
{
assert(channel_person->person);
assert(channel_person->person->nick);
if ( strnickcmp(channel_person->person->nick, nick) == 0 )
return channel_person;
}
return NULL;
}
struct channel_person* add_person_to_channel(struct network* state, struct person* person, struct channel* channel)
{
assert(person);
assert(channel);
assert(person->nick);
assert(channel->name);
assert(!find_person_in_channel(state, person->nick, channel->name));
struct channel_person* channel_person = (struct channel_person*)
calloc(sizeof(struct channel_person), 1);
if ( !channel_person )
return NULL;
channel_person->channel = channel;
channel_person->person = person;
channel_person->prev_channel_person = NULL;
channel_person->next_channel_person = state->channel_people;
if ( state->channel_people )
state->channel_people->prev_channel_person = channel_person;
state->channel_people = channel_person;
channel_person->prev_person_in_channel = NULL;
channel_person->next_person_in_channel = channel->people;
if ( channel->people )
channel->people->prev_person_in_channel = channel_person;
channel->people = channel_person;
channel_person->prev_channel_for_person = NULL;
channel_person->next_channel_for_person = person->channels;
if ( person->channels )
person->channels->prev_channel_for_person = channel_person;
person->channels = channel_person;
return channel_person;
}
struct channel_person* get_person_in_channel(struct network* state, struct person* person, struct channel* channel)
{
for ( struct channel_person* result =
find_person_in_channel(state, person->nick, channel->name); result; result = NULL )
return result;
return add_person_to_channel(state, person, channel);
}
void remove_person_from_channel(struct network* state, struct channel_person* channel_person)
{
if ( state->channel_people != channel_person )
channel_person->prev_channel_person->next_channel_person = channel_person->next_channel_person;
else
state->channel_people = channel_person->next_channel_person;
if ( channel_person->next_channel_person )
channel_person->next_channel_person->prev_channel_person = channel_person->prev_channel_person;
if ( channel_person->channel->people != channel_person )
channel_person->prev_person_in_channel->next_person_in_channel = channel_person->next_person_in_channel;
else
channel_person->channel->people = channel_person->next_person_in_channel;
if ( channel_person->next_person_in_channel )
channel_person->next_person_in_channel->prev_person_in_channel = channel_person->prev_person_in_channel;
if ( channel_person->person->channels != channel_person )
channel_person->prev_channel_for_person->next_channel_for_person = channel_person->next_channel_for_person;
else
channel_person->person->channels = channel_person->next_channel_for_person;
if ( channel_person->next_channel_for_person )
channel_person->next_channel_for_person->prev_channel_for_person = channel_person->prev_channel_for_person;
free(channel_person);
}

73
irc/database.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* database.h
* Data structure for keeping track of channels and people.
*/
#ifndef DATABASE_H
#define DATABASE_H
struct channel;
struct channel_person;
struct network;
struct person;
struct person
{
struct person* prev_person;
struct person* next_person;
char* nick;
struct channel_person* channels;
bool always_observable; // myself or having private messaged me
};
struct channel_person
{
struct channel_person* prev_channel_person;
struct channel_person* next_channel_person;
struct channel_person* prev_person_in_channel;
struct channel_person* next_person_in_channel;
struct channel_person* prev_channel_for_person;
struct channel_person* next_channel_for_person;
struct channel* channel;
struct person* person;
bool is_operator;
bool is_voiced;
};
struct channel
{
struct channel* prev_channel;
struct channel* next_channel;
char* name;
char* topic;
struct channel_person* people;
};
struct channel* find_channel(const struct network* state, const char* channel_name);
struct channel* add_channel(struct network* state, const char* channel_name);
struct channel* get_channel(struct network* state, const char* channel_name);
void remove_channel(struct network* state, struct channel* channel);
struct person* find_person(const struct network* state, const char* nick);
struct person* add_person(struct network* state, const char* nick);
struct person* get_person(struct network* state, const char* nick);
void remove_person(struct network* state, struct person* person);
struct channel_person* find_person_in_channel(const struct network* state, const char* nick, const char* channel_name);
struct channel_person* add_person_to_channel(struct network* state, struct person* person, struct channel* channel);
struct channel_person* get_person_in_channel(struct network* state, struct person* person, struct channel* channel);
void remove_person_from_channel(struct network* state, struct channel_person* channel_person);
#endif

1007
irc/irc.c Normal file

File diff suppressed because it is too large Load Diff

24
irc/network.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef NETWORK_H
#define NETWORK_H
struct account;
struct channel;
struct channel_person;
struct person;
struct network
{
struct irc_connection* irc_connection;
struct account* accounts;
struct channel* channels;
struct person* people;
struct channel_person* channel_people;
struct scrollback* scrollbacks;
char* nick;
char* real_name;
char* password;
char* server_hostname;
const char* autojoin;
};
#endif

205
irc/scrollback.c Normal file
View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* scrollback.c
* Ordered messages for display.
*/
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "compat.h"
#include "network.h"
#include "scrollback.h"
#include "string.h"
void message_free(struct message* msg)
{
free(msg->who);
free(msg->what);
}
void scrollback_free(struct scrollback* sb)
{
if ( sb->network )
{
if ( sb->scrollback_prev )
sb->scrollback_prev->scrollback_next = sb->scrollback_next;
else
sb->network->scrollbacks = sb->scrollback_next;
if ( sb->scrollback_next )
sb->scrollback_next->scrollback_prev = sb->scrollback_prev;
sb->scrollback_prev = NULL;
sb->scrollback_next = NULL;
sb->network = NULL;
}
for ( size_t i = 0; i < sb->messages_count; i++ )
message_free(&sb->messages[i]);
free(sb->messages);
free(sb->name);
free(sb);
}
struct scrollback* find_scrollback_network(const struct network* network)
{
// TODO: The server hostname can be a valid nick, for instance if the
// hostname doesn't contain any dot characters.
for ( struct scrollback* sb = network->scrollbacks;
sb;
sb = sb->scrollback_next )
{
if ( sb->name[0] == '#' )
continue;
if ( !strnickcmp(network->server_hostname, sb->name) )
return sb;
}
return NULL;
}
struct scrollback* find_scrollback(const struct network* network,
const char* name)
{
assert(name);
for ( struct scrollback* sb = network->scrollbacks;
sb;
sb = sb->scrollback_next )
{
if ( name[0] == '#' && sb->name[0] == '#' )
{
if ( strchannelcmp(name + 1, sb->name + 1) == 0 )
return sb;
}
else if ( name[0] != '#' && sb->name[0] != '#' )
{
if ( strnickcmp(name + 1, sb->name + 1) == 0 )
return sb;
}
}
return NULL;
}
struct scrollback* add_scrollback(struct network* network, const char* name)
{
struct scrollback* sb =
(struct scrollback*) calloc(1, sizeof(struct scrollback));
if ( !sb )
return NULL;
if ( !(sb->name = strdup(name)) )
return scrollback_free(sb), (struct scrollback*) NULL;
sb->network = network;
sb->scrollback_next = sb->network->scrollbacks;
if ( sb->scrollback_next )
sb->scrollback_next->scrollback_prev = sb;
sb->network->scrollbacks = sb;
return sb;
}
struct scrollback* get_scrollback(struct network* network, const char* name)
{
struct scrollback* result = find_scrollback(network, name);
if ( result )
return result;
return add_scrollback(network, name);
}
bool scrollback_add_message(struct scrollback* sb,
enum activity activity,
const struct message* msg)
{
if ( sb->messages_count == sb->messages_allocated )
{
size_t new_allocated = 2 * sb->messages_allocated;
if ( new_allocated == 0 )
new_allocated = 64;
struct message* new_messages = (struct message*)
reallocarray(sb->messages, new_allocated, sizeof(struct message));
if ( !new_messages )
return false;
sb->messages = new_messages;
sb->messages_allocated = new_allocated;
}
sb->messages[sb->messages_count++] = *msg;
size_t who_width = strlen(msg->who); // TODO: Unicode?
if ( sb->who_width < who_width )
sb->who_width = who_width;
if ( sb->activity < activity )
sb->activity = activity;
return true;
}
static void message_timestamp(struct message* msg)
{
struct tm tm;
time_t now = time(NULL);
localtime_r(&now, &tm);
msg->sec = tm.tm_sec;
msg->min = tm.tm_min;
msg->hour = tm.tm_hour;
}
bool scrollback_print(struct scrollback* sb,
enum activity activity,
const char* who,
const char* what)
{
struct message msg;
memset(&msg, 0, sizeof(msg));
message_timestamp(&msg);
if ( (msg.who = strdup(who)) &&
(msg.what = strdup(what)) &&
scrollback_add_message(sb, activity, &msg) )
return true;
message_free(&msg);
return false;
}
bool scrollback_printf(struct scrollback* sb,
enum activity activity,
const char* who,
const char* whatf,
...)
{
struct message msg;
memset(&msg, 0, sizeof(msg));
message_timestamp(&msg);
va_list ap;
va_start(ap, whatf);
int len = vasprintf(&msg.what, whatf, ap);
va_end(ap);
if ( (msg.who = strdup(who)) &&
0 <= len &&
scrollback_add_message(sb, activity, &msg) )
return true;
message_free(&msg);
return false;
}
void scrollback_clear(struct scrollback* sb)
{
for ( size_t i = 0; i < sb->messages_count; i++ )
{
free(sb->messages[i].who);
free(sb->messages[i].what);
}
sb->messages_count = 0;
sb->messages_allocated = 0;
free(sb->messages);
sb->messages = NULL;
}

82
irc/scrollback.h Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* scrollback.c
* Ordered messages for display.
*/
#ifndef SCROLLBACK_H
#define SCROLLBACK_H
#include <stdbool.h>
#include <stddef.h>
struct network;
enum activity
{
ACTIVITY_NONE,
ACTIVITY_NONTALK,
ACTIVITY_TALK,
ACTIVITY_HIGHLIGHT,
};
struct message
{
int hour;
int min;
int sec;
char* who;
char* what;
};
struct scrollback
{
struct network* network;
struct scrollback* scrollback_prev;
struct scrollback* scrollback_next;
char* name;
struct message* messages;
size_t messages_count;
size_t messages_allocated;
size_t who_width;
enum activity activity;
};
void message_free(struct message* msg);
void scrollback_free(struct scrollback* sb);
struct scrollback* find_scrollback_network(const struct network* network);
struct scrollback* find_scrollback(const struct network* network,
const char* name);
struct scrollback* add_scrollback(struct network* network,
const char* name);
struct scrollback* get_scrollback(struct network* network,
const char* name);
bool scrollback_add_message(struct scrollback* sb,
enum activity activity,
const struct message* msg);
bool scrollback_print(struct scrollback* sb,
enum activity activity,
const char* who,
const char* what);
__attribute__((format(printf, 4, 5)))
bool scrollback_printf(struct scrollback* sb,
enum activity activity,
const char* who,
const char* whatf,
...);
void scrollback_clear(struct scrollback* sb);
#endif

34
irc/string.c Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* string.c
* String utility functions and compatibility.
*/
#include <string.h>
#include "string.h"
// TODO: Implement this properly in accordance with IRC RFC rules.
int strchannelcmp(const char* a, const char* b)
{
return strcasecmp(a, b);
}
// TODO: Implement this properly in accordance with IRC RFC rules.
int strnickcmp(const char* a, const char* b)
{
return strcasecmp(a, b);
}

28
irc/string.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* string.h
* String utility functions and compatibility.
*/
#ifndef STRING_H
#define STRING_H
#include <stddef.h>
int strchannelcmp(const char* a, const char* b);
int strnickcmp(const char* a, const char* b);
#endif

545
irc/ui.c Normal file
View File

@ -0,0 +1,545 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ui.c
* User Interface.
*/
#include <sys/ioctl.h>
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <wchar.h>
#include "connection.h"
#include "network.h"
#include "scrollback.h"
#include "ui.h"
struct cell
{
wchar_t c;
int fgcolor;
int bgcolor;
};
static struct termios saved_termios;
void tty_show(struct cell* cells, size_t cols, size_t rows)
{
printf("\e[H");
int fgcolor = -1;
int bgcolor = -1;
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
for ( size_t r = 0; r < rows; r++ )
{
for ( size_t c = 0; c < cols; c++ )
{
struct cell* cell = &cells[r * cols + c];
if ( fgcolor != cell->fgcolor )
{
printf("\e[%im", cell->fgcolor);
fgcolor = cell->fgcolor;
}
if ( bgcolor != cell->bgcolor )
{
printf("\e[%im", cell->bgcolor);
bgcolor = cell->bgcolor;
}
char mb[MB_CUR_MAX];
size_t amount = wcrtomb(mb, cell->c, &ps);
if ( amount == (size_t) -1 )
continue;
fwrite(mb, 1, amount, stdout);
}
if ( r + 1 != rows )
printf("\n");
}
fflush(stdout);
}
void on_sigquit(int sig)
{
// TODO: This is not async signal safe.
ui_destroy(NULL);
// TODO: Use sigaction so the handler only runs once.
//raise(sig);
(void) sig;
raise(SIGKILL);
}
void ui_initialize(struct ui* ui, struct network* network)
{
memset(ui, 0, sizeof(*ui));
ui->network = network;
ui->current = find_scrollback_network(network);
struct winsize ws;
if ( ioctl(1, TIOCGWINSZ, &ws) < 0 )
err(1, "stdout: ioctl: TIOCGWINSZ");
if ( tcgetattr(0, &saved_termios) < 0 )
err(1, "stdin: tcgetattr");
struct termios tcattr;
memcpy(&tcattr, &saved_termios, sizeof(struct termios));
tcattr.c_lflag &= ~(ECHO | ICANON | IEXTEN);
tcattr.c_iflag |= ICRNL | ISIG;
tcattr.c_cc[VMIN] = 1;
tcattr.c_cc[VTIME] = 0;
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, on_sigquit);
tcsetattr(0, TCSADRAIN, &tcattr);
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049h");
fflush(stdout);
}
}
void ui_destroy(struct ui* ui)
{
// TODO.
(void) ui;
// TODO: This should be done in an atexit handler as well.
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049l");
fflush(stdout);
}
tcsetattr(0, TCSADRAIN, &saved_termios);
}
void increment_offset(size_t* o_ptr, size_t* line_ptr, size_t cols)
{
if ( (*o_ptr)++ == cols )
{
*o_ptr = 0;
(*line_ptr)++;
}
}
void ui_render(struct ui* ui)
{
mbstate_t ps;
struct winsize ws;
if ( ioctl(1, TIOCGWINSZ, &ws) < 0 )
err(1, "stdout: ioctl: TIOCGWINSZ");
size_t cols = ws.ws_col;
size_t rows = ws.ws_row;
struct cell* cells = calloc(sizeof(struct cell) * cols, rows);
if ( !cells )
err(1, "calloc");
for ( size_t r = 0; r < rows; r++ )
{
for ( size_t c = 0; c < cols; c++ )
{
struct cell* cell = &cells[r * cols + c];
cell->c = L' ';
cell->fgcolor = 0;
cell->bgcolor = 0;
}
}
// TODO: What if the terminal isn't large enough?
struct scrollback* sb = ui->current;
sb->activity = ACTIVITY_NONE;
size_t title_from = 0;
size_t when_offset = 0;
size_t when_width = 2 + 1 + 2 + 1 + 2;
size_t who_offset = when_offset + when_width + 1;
size_t who_width = sb->who_width;
size_t div_offset = who_offset + who_width + 1;
size_t what_offset = div_offset + 2;
size_t what_width = cols - what_offset;
size_t input_width = cols;
size_t input_num_lines = 1;
for ( size_t i = 0, o = 0; i < ui->input_used; i++ )
{
wchar_t wc = ui->input[i];
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( input_width <= o )
{
input_num_lines++;
o = 0;
}
o += w;
}
char* title;
if ( asprintf(&title, "%s @ %s / %s", ui->network->nick,
ui->network->server_hostname, ui->current->name) < 0 )
err(1, "asprintf");
size_t title_len = strlen(title);
size_t title_how_many = cols < title_len ? cols : title_len;
size_t title_offset = (cols - title_how_many) / 2;
for ( size_t i = 0; i < title_how_many; i++ )
{
char c = title[i];
size_t cell_r = title_from;
size_t cell_c = title_offset + i;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = btowc((unsigned char) c);
}
free(title);
size_t scrollbacks_from = title_from + 1;
size_t scrollbacks_lines = 1;
size_t scrollbacks_o = 0;
for ( struct scrollback* iter = ui->network->scrollbacks;
iter;
iter = iter->scrollback_next )
{
if ( iter->scrollback_prev )
{
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
}
for ( size_t i = 0; iter->name[i]; i++ )
{
char c = iter->name[i];
size_t cell_r = scrollbacks_from + (scrollbacks_lines - 1);
size_t cell_c = scrollbacks_o;
struct cell* cell = &cells[cell_r * cols + cell_c];
int fgcolor = 0;
if ( iter == sb )
fgcolor = 1; // TODO: Boldness should be its own property.
else if ( iter->activity == ACTIVITY_NONTALK )
fgcolor = 31;
else if ( iter->activity == ACTIVITY_TALK )
fgcolor = 91;
else if ( iter->activity == ACTIVITY_HIGHLIGHT )
fgcolor = 94;
cell->c = btowc((unsigned char) c);
cell->fgcolor = fgcolor;
increment_offset(&scrollbacks_o, &scrollbacks_lines, cols);
}
}
size_t horhigh_from = scrollbacks_from + scrollbacks_lines;
for ( size_t c = 0; c < cols; c++ )
{
size_t cell_r = horhigh_from;
size_t cell_c = c;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = c == div_offset ? L'' : L'';
}
size_t sb_from = horhigh_from + 1;
// TODO: What if the input is too big?
size_t input_bottom = rows - input_num_lines;
size_t input_offset = 0;
for ( size_t i = 0, o = 0, line = 0; i < ui->input_used; i++ )
{
wchar_t wc = ui->input[i];
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( input_width <= o )
{
line++;
o = 0;
}
// TODO: If 1 < w.
size_t cell_r = input_bottom + line;
size_t cell_c = input_offset + o;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = wc;
o += w;
}
size_t horlow_from = input_bottom - 1;
for ( size_t c = 0; c < cols; c++ )
{
size_t cell_r = horlow_from;
size_t cell_c = c;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = c == div_offset ? L'' : L'';
}
size_t sb_to = horlow_from;
for ( size_t r = sb_to - 1; r != sb_from - 1; r-- )
{
size_t cell_r = r;
size_t cell_c = div_offset;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = L'';
}
for ( size_t r = sb_to - 1, m = sb->messages_count - 1;
r != (sb_from - 1) && m != SIZE_MAX;
r--, m-- )
{
struct message* msg = &sb->messages[m];
size_t num_lines = 1;
size_t max_lines = sb_from - r + 1;
memset(&ps, 0, sizeof(ps));
for ( size_t i = 0, o = 0; msg->what[i]; )
{
wchar_t wc;
size_t amount = mbrtowc(&wc, msg->what + i, SIZE_MAX, &ps);
if ( amount == (size_t) -1 || amount == (size_t) -2 )
{
// TODO.
memset(&ps, 0, sizeof(ps));
continue;
}
i += amount;
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( what_width <= o )
{
num_lines++;
o = 0;
}
o += w;
}
size_t how_many_lines = max_lines < num_lines ? max_lines : num_lines;
size_t first_line = num_lines - how_many_lines;
if ( 1 < how_many_lines )
r -= how_many_lines - 1;
if ( first_line == 0 )
{
char when[2 + 1 + 2 + 1 + 2 + 1 + 1];
snprintf(when, sizeof(when), "%02i:%02i:%02i ",
msg->hour, msg->min, msg->sec);
for ( size_t i = 0; when[i]; i++ )
{
size_t cell_r = r;
size_t cell_c = when_offset + i;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = btowc((unsigned char) when[i]);
}
memset(&ps, 0, sizeof(ps));
size_t msg_who_width = strlen(msg->who);
size_t msg_who_how_many = who_width < msg_who_width ? who_width : msg_who_width;
size_t msg_who_first = msg_who_width - msg_who_how_many;
size_t msg_who_offset = who_width - msg_who_how_many;
for ( size_t i = 0; i < msg_who_how_many; i++ )
{
char c = msg->who[msg_who_first + i];
size_t cell_r = r;
size_t cell_c = who_offset + msg_who_offset + i;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = btowc((unsigned char) c);
}
}
for ( size_t i = 0, o = 0, line = 0; msg->what[i]; )
{
wchar_t wc;
size_t amount = mbrtowc(&wc, msg->what + i, SIZE_MAX, &ps);
if ( amount == (size_t) -1 || amount == (size_t) -2 )
{
// TODO.
memset(&ps, 0, sizeof(ps));
continue;
}
i += amount;
int w = wcwidth(wc);
if ( w < 0 || w == 0 )
continue;
if ( what_width <= o )
{
line++;
o = 0;
}
// TODO: If 1 < w.
if ( first_line <= line )
{
size_t cell_r = r + line - first_line;
size_t cell_c = what_offset + o;
struct cell* cell = &cells[cell_r * cols + cell_c];
cell->c = wc;
}
o += w;
}
}
(void) ui;
tty_show(cells, cols, rows);
free(cells);
}
static bool is_command(const char* input,
const char* cmd,
const char** param)
{
size_t cmdlen = strlen(cmd);
if ( strncmp(input, cmd, cmdlen) != 0 )
return false;
if ( !input[cmdlen] )
{
if ( param )
*param = NULL;
return true;
}
if ( input[cmdlen] != ' ' )
return false;
if ( !param )
return false;
*param = input + cmdlen + 1;
return true;
}
static bool is_command_param(const char* input,
const char* cmd,
const char** param)
{
if ( !is_command(input, cmd, param) )
return false;
if ( !*param )
return false; // TODO: Help message in scrollback.
return true;
}
void ui_input_char(struct ui* ui, char c)
{
wchar_t wc;
size_t amount = mbrtowc(&wc, &c, 1, &ui->input_ps);
if ( amount == (size_t) -2 )
return;
if ( amount == (size_t) -1 )
{
// TODO.
memset(&ui->input_ps, 0, sizeof(ui->input_ps));
return;
}
if ( wc == L'\b' || wc == 127 )
{
if ( 0 < ui->input_used )
ui->input_used--;
}
else if ( wc == L'\f' /* ^L */ )
{
scrollback_clear(ui->current);
// TODO: Schedule full redraw?
}
else if ( wc == L'\n' )
{
char input[4 * sizeof(ui->input) / sizeof(ui->input[0])];
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
const wchar_t* wcs = ui->input;
size_t amount = wcsnrtombs(input, &wcs, ui->input_used, sizeof(input), &ps);
ui->input_used = 0;
if ( amount == (size_t) -1 )
return;
input[amount < sizeof(input) ? amount : amount - 1] = '\0';
struct irc_connection* conn = ui->network->irc_connection;
const char* who = ui->network->nick;
const char* where = ui->current->name;
const char* param;
if ( input[0] == '/' && input[1] != '/' )
{
if ( !input[1] )
return;
if ( is_command_param(input, "/w", &param) ||
is_command_param(input, "/window", &param) )
{
struct scrollback* sb = find_scrollback(ui->network, param);
if ( sb )
ui->current = sb;
}
else if ( is_command_param(input, "/query", &param) )
{
if ( param[0] == '#' )
return; // TODO: Help in scrollback.
struct scrollback* sb = get_scrollback(ui->network, param);
if ( sb )
ui->current = sb;
}
else if ( is_command_param(input, "/join", &param) )
{
irc_command_join(conn, param);
struct scrollback* sb = get_scrollback(ui->network, param);
if ( sb )
ui->current = sb;
}
// TODO: Make it default to the current channel if any.
else if ( is_command_param(input, "/part", &param) )
{
irc_command_part(conn, param);
}
else if ( is_command(input, "/quit", &param) )
{
irc_command_quit(conn, param ? param : "Quiting");
}
else if ( is_command_param(input, "/nick", &param) )
{
irc_command_nick(conn, param);
}
else if ( is_command_param(input, "/raw", &param) )
{
irc_transmit_string(conn, param);
}
else if ( is_command_param(input, "/me", &param) )
{
scrollback_printf(ui->current, ACTIVITY_NONE, "*", "%s %s",
who, param);
irc_command_privmsgf(conn, where, "\x01""ACTION %s""\x01",
param);
}
else if ( is_command(input, "/clear", &param) )
{
scrollback_clear(ui->current);
}
// TODO: /ban
// TODO: /ctcp
// TODO: /deop
// TODO: /devoice
// TODO: /kick
// TODO: /mode
// TODO: /op
// TODO: /quiet
// TODO: /topic
// TODO: /voice
else
{
scrollback_printf(ui->current, ACTIVITY_NONE, "*",
"%s :Unknown command", input + 1);
}
}
else
{
const char* what = input;
if ( what[0] == '/' )
what++;
scrollback_print(ui->current, ACTIVITY_NONE, who, what);
irc_command_privmsg(conn, where, what);
}
}
else
{
if ( ui->input_used < sizeof(ui->input) / sizeof(ui->input[0]) )
ui->input[ui->input_used++] = wc;
}
}

42
irc/ui.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ui.h
* User Interface.
*/
#ifndef UI_H
#define UI_H
#include <wchar.h>
struct network;
struct scrollback;
struct ui
{
struct network* network;
struct scrollback* current;
wchar_t input[512];
size_t input_used;
mbstate_t input_ps;
};
void ui_initialize(struct ui* ui, struct network* network);
void ui_render(struct ui* ui);
void ui_input_char(struct ui* ui, char c);
void ui_destroy(struct ui* ui);
#endif

2
iso9660/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
iso9660fs
*.o

33
iso9660/Makefile Normal file
View File

@ -0,0 +1,33 @@
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CXXFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -fcheck-new
LIBS:=$(LIBS)
ifeq ($(HOST_IS_SORTIX),0)
LIBS:=$(LIBS) -lfuse
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
endif
BINARIES:=iso9660fs
all: $(BINARIES)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(SBINDIR)
install $(BINARIES) $(DESTDIR)$(SBINDIR)
iso9660fs: *.cpp *.h
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
clean:
rm -f $(BINARIES) *.o

108
iso9660/block.cpp Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* block.cpp
* Blocks in the filesystem.
*/
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Block::Block()
{
this->block_data = NULL;
}
Block::Block(Device* device, uint32_t block_id)
{
Construct(device, block_id);
}
void Block::Construct(Device* device, uint32_t block_id)
{
this->prev_block = NULL;
this->next_block = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->device = device;
this->reference_count = 1;
this->block_id = block_id;
}
Block::~Block()
{
Destruct();
delete[] block_data;
}
void Block::Destruct()
{
Unlink();
}
void Block::Refer()
{
reference_count++;
}
void Block::Unref()
{
if ( !--reference_count )
{
#if 0
device->block_count--;
delete this;
#endif
}
}
void Block::Use()
{
Unlink();
Prelink();
}
void Block::Unlink()
{
(prev_block ? prev_block->next_block : device->mru_block) = next_block;
(next_block ? next_block->prev_block : device->lru_block) = prev_block;
size_t bin = block_id % DEVICE_HASH_LENGTH;
(prev_hashed ? prev_hashed->next_hashed : device->hash_blocks[bin]) = next_hashed;
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
}
void Block::Prelink()
{
prev_block = NULL;
next_block = device->mru_block;
if ( device->mru_block )
device->mru_block->prev_block = this;
device->mru_block = this;
if ( !device->lru_block )
device->lru_block = this;
size_t bin = block_id % DEVICE_HASH_LENGTH;
prev_hashed = NULL;
next_hashed = device->hash_blocks[bin];
device->hash_blocks[bin] = this;
if ( next_hashed )
next_hashed->prev_hashed = this;
}

53
iso9660/block.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* block.h
* Blocks in the filesystem.
*/
#ifndef BLOCK_H
#define BLOCK_H
class Device;
class Block
{
public:
Block();
Block(Device* device, uint32_t block_id);
~Block();
void Construct(Device* device, uint32_t block_id);
void Destruct();
public:
Block* prev_block;
Block* next_block;
Block* prev_hashed;
Block* next_hashed;
Device* device;
size_t reference_count;
uint32_t block_id;
uint8_t* block_data;
public:
void Refer();
void Unref();
void Use();
void Unlink();
void Prelink();
};
#endif

107
iso9660/device.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* device.cpp
* Block device.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "block.h"
#include "device.h"
#include "ioleast.h"
Device::Device(int fd, const char* path, uint32_t block_size)
{
this->mru_block = NULL;
this->lru_block = NULL;
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
hash_blocks[i] = NULL;
struct stat st;
fstat(fd, &st);
this->device_size = st.st_size;
this->path = path;
this->block_size = block_size;
this->fd = fd;
this->block_count = 0;
#ifdef __sortix__
// TODO: This isn't scaleable if there's multiple filesystems mounted.
size_t memory;
memstat(NULL, &memory);
this->block_limit = (memory / 10) / block_size;
#else
this->block_limit = 32768;
#endif
}
Device::~Device()
{
while ( mru_block )
delete mru_block;
close(fd);
}
Block* Device::AllocateBlock()
{
if ( block_limit <= block_count )
{
for ( Block* block = lru_block; block; block = block->prev_block )
{
if ( block->reference_count )
continue;
block->Destruct(); // Syncs.
return block;
}
}
uint8_t* data = new uint8_t[block_size];
if ( !data ) // TODO: Use operator new nothrow!
return NULL;
Block* block = new Block();
if ( !block ) // TODO: Use operator new nothrow!
return delete[] data, (Block*) NULL;
block->block_data = data;
block_count++;
return block;
}
Block* Device::GetBlock(uint32_t block_id)
{
if ( Block* block = GetCachedBlock(block_id) )
return block;
Block* block = AllocateBlock();
if ( !block )
return NULL;
block->Construct(this, block_id);
off_t file_offset = (off_t) block_size * (off_t) block_id;
preadall(fd, block->block_data, block_size, file_offset);
block->Prelink();
return block;
}
Block* Device::GetCachedBlock(uint32_t block_id)
{
size_t bin = block_id % DEVICE_HASH_LENGTH;
for ( Block* iter = hash_blocks[bin]; iter; iter = iter->next_hashed )
if ( iter->block_id == block_id )
return iter->Refer(), iter;
return NULL;
}

51
iso9660/device.h Normal file
View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* device.h
* Block device.
*/
#ifndef DEVICE_H
#define DEVICE_H
class Block;
static const size_t DEVICE_HASH_LENGTH = 1 << 16;
class Device
{
public:
Device(int fd, const char* path, uint32_t block_size);
~Device();
public:
Block* mru_block;
Block* lru_block;
Block* hash_blocks[DEVICE_HASH_LENGTH];
off_t device_size;
const char* path;
uint32_t block_size;
int fd;
size_t block_count;
size_t block_limit;
public:
Block* AllocateBlock();
Block* GetBlock(uint32_t block_id);
Block* GetCachedBlock(uint32_t block_id);
};
#endif

87
iso9660/filesystem.cpp Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* filesystem.cpp
* ISO 9660 filesystem implementation.
*/
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <stdio.h> // DEBUG
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "util.h"
Filesystem::Filesystem(Device* device, const char* mount_path,
uint64_t pvd_offset)
{
// TODO: This should be replaced by the . entry within the root directory
// so the Rock Ridge extensions are available.
uint32_t pvd_block_id = pvd_offset / device->block_size;
this->pvd_block = device->GetBlock(pvd_block_id);
assert(pvd_block); // TODO: This can fail.
this->pvd = (struct iso9660_pvd*)
(pvd_block->block_data + pvd_offset % device->block_size);
this->root_ino = pvd_offset + offsetof(struct iso9660_pvd, root_dirent);
this->device = device;
this->mount_path = mount_path;
this->block_size = device->block_size;
this->mru_inode = NULL;
this->lru_inode = NULL;
for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ )
this->hash_inodes[i] = NULL;
}
Filesystem::~Filesystem()
{
while ( mru_inode )
delete mru_inode;
pvd_block->Unref();
}
Inode* Filesystem::GetInode(iso9660_ino_t inode_id)
{
size_t bin = inode_id % INODE_HASH_LENGTH;
for ( Inode* iter = hash_inodes[bin]; iter; iter = iter->next_hashed )
if ( iter->inode_id == inode_id )
return iter->Refer(), iter;
uint32_t block_id = inode_id / block_size;
uint32_t offset = inode_id % block_size;
Block* block = device->GetBlock(block_id);
if ( !block )
return (Inode*) NULL;
Inode* inode = new Inode(this, inode_id);
if ( !inode )
return block->Unref(), (Inode*) NULL;
inode->data_block = block;
uint8_t* buf = inode->data_block->block_data + offset;
inode->data = (struct iso9660_dirent*) buf;
inode->Prelink();
inode->Parse();
return inode;
}

52
iso9660/filesystem.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* filesystem.h
* ISO 9660 filesystem implementation.
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include "iso9660.h"
class Device;
class Inode;
static const size_t INODE_HASH_LENGTH = 1 << 16;
class Filesystem
{
public:
Filesystem(Device* device, const char* mount_path, uint64_t pvd_offset);
~Filesystem();
public:
Block* pvd_block;
struct iso9660_pvd* pvd;
Device* device;
const char* mount_path;
iso9660_ino_t root_ino;
uint32_t block_size;
Inode* mru_inode;
Inode* lru_inode;
Inode* hash_inodes[INODE_HASH_LENGTH];
public:
Inode* GetInode(iso9660_ino_t inode_id);
};
#endif

701
iso9660/fsmarshall.cpp Normal file
View File

@ -0,0 +1,701 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fsmarshall.cpp
* Sortix fsmarshall frontend.
*/
#if defined(__sortix__)
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <error.h>
#include <dirent.h>
#include <fcntl.h>
#include <ioleast.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#include <sortix/dirent.h>
#include <fsmarshall.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "fsmarshall.h"
#include "fuse.h"
#include "inode.h"
#include "iso9660fs.h"
bool RespondData(int chl, const void* ptr, size_t count)
{
return writeall(chl, ptr, count) == count;
}
bool RespondHeader(int chl, size_t type, size_t size)
{
struct fsm_msg_header hdr;
hdr.msgtype = type;
hdr.msgsize = size;
return RespondData(chl, &hdr, sizeof(hdr));
}
bool RespondMessage(int chl, size_t type, const void* ptr, size_t count)
{
return RespondHeader(chl, type, count) &&
RespondData(chl, ptr, count);
}
bool RespondError(int chl, int errnum)
{
struct fsm_resp_error body;
body.errnum = errnum;
return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body));
}
bool RespondSuccess(int chl)
{
struct fsm_resp_success body;
return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body));
}
bool RespondStat(int chl, struct stat* st)
{
struct fsm_resp_stat body;
body.st = *st;
return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body));
}
bool RespondStatVFS(int chl, struct statvfs* stvfs)
{
struct fsm_resp_statvfs body;
body.stvfs = *stvfs;
return RespondMessage(chl, FSM_RESP_STATVFS, &body, sizeof(body));
}
bool RespondSeek(int chl, off_t offset)
{
struct fsm_resp_lseek body;
body.offset = offset;
return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body));
}
bool RespondRead(int chl, const uint8_t* buf, size_t count)
{
struct fsm_resp_read body;
body.count = count;
return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) &&
RespondData(chl, buf, count);
}
bool RespondReadlink(int chl, const uint8_t* buf, size_t count)
{
struct fsm_resp_readlink body;
body.targetlen = count;
return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) &&
RespondData(chl, buf, count);
}
bool RespondWrite(int chl, size_t count)
{
struct fsm_resp_write body;
body.count = count;
return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body));
}
bool RespondOpen(int chl, ino_t ino, mode_t type)
{
struct fsm_resp_open body;
body.ino = ino;
body.type = type;
return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body));
}
bool RespondMakeDir(int chl, ino_t ino)
{
struct fsm_resp_mkdir body;
body.ino = ino;
return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body));
}
bool RespondReadDir(int chl, struct dirent* dirent)
{
struct fsm_resp_readdirents body;
body.ino = dirent->d_ino;
body.type = dirent->d_type;
body.namelen = dirent->d_namlen;
return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) &&
RespondData(chl, dirent->d_name, dirent->d_namlen);
}
bool RespondTCGetBlob(int chl, const void* data, size_t data_size)
{
struct fsm_resp_tcgetblob body;
body.count = data_size;
return RespondMessage(chl, FSM_RESP_TCGETBLOB, &body, sizeof(body)) &&
RespondData(chl, data, data_size);
}
Inode* SafeGetInode(Filesystem* fs, ino_t ino)
{
if ( (iso9660_ino_t) ino != ino )
return errno = EBADF, (Inode*) ino;
return fs->GetInode((iso9660_ino_t) ino);
}
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
{
(void) chl;
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
{
inode->RemoteRefer();
inode->Unref();
}
}
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
{
(void) chl;
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
{
inode->RemoteUnref();
inode->Unref();
}
}
void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondSuccess(chl);
}
void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
struct stat st;
StatInode(inode, &st);
inode->Unref();
RespondStat(chl, &st);
}
void HandleChangeMode(int chl, struct fsm_req_chmod* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleChangeOwner(int chl, struct fsm_req_chown* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleUTimens(int chl, struct fsm_req_utimens* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleTruncate(int chl, struct fsm_req_truncate* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( msg->whence == SEEK_SET )
RespondSeek(chl, msg->offset);
else if ( msg->whence == SEEK_END )
{
off_t inode_size = inode->Size();
if ( (msg->offset < 0 && inode_size + msg->offset < 0) ||
(0 <= msg->offset && OFF_MAX - inode_size < msg->offset) )
RespondError(chl, EOVERFLOW);
else
RespondSeek(chl, msg->offset + inode_size);
}
else
RespondError(chl, EINVAL);
inode->Unref();
}
void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
uint8_t* buf = (uint8_t*) malloc(msg->count);
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset);
inode->Unref();
if ( amount < 0 ) { free(buf); RespondError(chl, errno); return; }
RespondRead(chl, buf, amount);
free(buf);
}
void HandleWriteAt(int chl, struct fsm_req_pwrite* /*msg*/, Filesystem* /*fs*/)
{
RespondError(chl, EROFS);
}
void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
Inode* result = inode->Open(path, msg->flags, FsModeFromHostMode(msg->mode));
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
result->Unref();
}
void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondError(chl, EROFS);
}
void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !S_ISDIR(inode->Mode()) )
{
inode->Unref();
RespondError(chl, ENOTDIR);
return;
}
union
{
struct dirent kernel_entry;
uint8_t padding[sizeof(struct dirent) + 256];
};
memset(&kernel_entry, 0, sizeof(kernel_entry));
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( inode->ReadDirectory(&offset, &block, &block_id,
msg->rec_num ? NULL : name, &file_type,
&inode_id) )
{
if ( !(msg->rec_num--) )
{
size_t name_len = strlen(name);
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
kernel_entry.d_ino = inode_id;
kernel_entry.d_dev = 0;
kernel_entry.d_type = HostDTFromFsDT(file_type);
kernel_entry.d_namlen = name_len;
memcpy(kernel_entry.d_name, name, name_len);
size_t dname_offset = offsetof(struct dirent, d_name);
padding[dname_offset + kernel_entry.d_namlen] = '\0';
block->Unref();
inode->Unref();
RespondReadDir(chl, &kernel_entry);
return;
}
}
int errnum = errno;
if ( block )
block->Unref();
inode->Unref();
if ( errnum )
{
RespondError(chl, errnum);
return;
}
kernel_entry.d_reclen = sizeof(kernel_entry);
RespondReadDir(chl, &kernel_entry);
}
void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
RespondError(chl, ENOTTY);
inode->Unref();
}
void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
bool result = inode->Unlink(path, false);
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
bool result = inode->RemoveDirectory(path);
free(path);
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
Inode* dest = SafeGetInode(fs, msg->linkino);
if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; }
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->namelen+1);
if ( !path )
{
RespondError(chl, errno);
inode->Unref();
return;
}
memcpy(path, pathraw, msg->namelen);
path[msg->namelen] = '\0';
bool result = inode->Link(path, dest);
free(path);
dest->Unref();
inode->Unref();
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->dirino);
if ( !inode ) { RespondError(chl, errno); return; }
inode->Unref();
RespondError(chl, EROFS);
}
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
{
Inode* inode = SafeGetInode(fs, msg->ino);
if ( !inode ) { RespondError(chl, errno); return; }
if ( !ISO9660_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; }
size_t count = inode->Size();
uint8_t* buf = (uint8_t*) malloc(count);
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
ssize_t amount = inode->ReadLink(buf, count);
inode->Unref();
if ( amount < 0 ) { RespondError(chl, errno); return; }
RespondReadlink(chl, buf, amount);
free(buf);
}
void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs)
{
char* pathraw = (char*) &(msg[1]);
char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1);
if ( !path ) { RespondError(chl, errno); return; }
memcpy(path, pathraw, msg->oldnamelen);
path[msg->oldnamelen] = '\0';
memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen);
path[msg->oldnamelen + 1 + msg->newnamelen] = '\0';
const char* oldname = path;
const char* newname = path + msg->oldnamelen + 1;
Inode* olddir = SafeGetInode(fs, msg->olddirino);
if ( !olddir ) { free(path); RespondError(chl, errno); return; }
Inode* newdir = SafeGetInode(fs, msg->newdirino);
if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; }
bool result = newdir->Rename(olddir, oldname, newname);
newdir->Unref();
olddir->Unref();
free(path);
if ( !result ) { RespondError(chl, errno); return; }
RespondSuccess(chl);
}
void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs)
{
(void) msg;
struct statvfs stvfs;
stvfs.f_bsize = fs->block_size;
stvfs.f_frsize = fs->block_size;
stvfs.f_blocks = fs->device->device_size / fs->block_size;
stvfs.f_bfree = 0;
stvfs.f_bavail = 0;
stvfs.f_files = 0;
stvfs.f_ffree = 0;
stvfs.f_favail = 0;
stvfs.f_ffree = 0;
stvfs.f_fsid = 0;
stvfs.f_flag = ST_RDONLY;
stvfs.f_namemax = 255;
RespondStatVFS(chl, &stvfs);
}
void HandleTCGetBlob(int chl, struct fsm_req_tcgetblob* msg, Filesystem* fs)
{
char* nameraw = (char*) &(msg[1]);
char* name = (char*) malloc(msg->namelen + 1);
if ( !name )
return (void) RespondError(chl, errno);
memcpy(name, nameraw, msg->namelen);
name[msg->namelen] = '\0';
//static const char index[] = "device-path\0filesystem-type\0filesystem-uuid\0mount-path\0";
static const char index[] = "device-path\0filesystem-type\0mount-path\0";
if ( !strcmp(name, "") )
RespondTCGetBlob(chl, index, sizeof(index) - 1);
else if ( !strcmp(name, "device-path") )
RespondTCGetBlob(chl, fs->device->path, strlen(fs->device->path));
else if ( !strcmp(name, "filesystem-type") )
RespondTCGetBlob(chl, "iso9660", strlen("iso9660"));
// TODO: Some kind of unique id.
//else if ( !strcmp(name, "filesystem-uuid") )
// RespondTCGetBlob(chl, fs->sb->s_uuid, sizeof(fs->sb->s_uuid));
else if ( !strcmp(name, "mount-path") )
RespondTCGetBlob(chl, fs->mount_path, strlen(fs->mount_path));
else
RespondError(chl, ENOENT);
free(name);
}
void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
{
request_uid = hdr->uid;
request_gid = hdr->gid;
if ( (uint16_t) request_uid != request_uid ||
(uint16_t) request_gid != request_gid )
{
fprintf(stderr, "extfs: id exceeded 16-bit: uid=%ju gid=%ju\n",
(uintmax_t) request_uid, (uintmax_t) request_gid);
RespondError(chl, EOVERFLOW);
return;
}
typedef void (*handler_t)(int, void*, Filesystem*);
handler_t handlers[FSM_MSG_NUM] = { NULL };
handlers[FSM_REQ_SYNC] = (handler_t) HandleSync;
handlers[FSM_REQ_STAT] = (handler_t) HandleStat;
handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode;
handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner;
handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate;
handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek;
handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt;
handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen;
handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir;
handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt;
handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY;
handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens;
handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir;
handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir;
handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink;
handlers[FSM_REQ_LINK] = (handler_t) HandleLink;
handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink;
handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink;
handlers[FSM_REQ_RENAME] = (handler_t) HandleRename;
handlers[FSM_REQ_REFER] = (handler_t) HandleRefer;
handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref;
handlers[FSM_REQ_STATVFS] = (handler_t) HandleStatVFS;
handlers[FSM_REQ_TCGETBLOB] = (handler_t) HandleTCGetBlob;
if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] )
{
fprintf(stderr, "extfs: message type %zu not supported\n", hdr->msgtype);
RespondError(chl, ENOTSUP);
return;
}
uint8_t body_buffer[65536];
uint8_t* body = body_buffer;
if ( sizeof(body_buffer) < hdr->msgsize )
{
body = (uint8_t*) mmap(NULL, hdr->msgsize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if ( (void*) body == MAP_FAILED )
{
RespondError(chl, errno);
return;
}
}
if ( readall(chl, body, hdr->msgsize) == hdr->msgsize )
handlers[hdr->msgtype](chl, body, fs);
else
RespondError(chl, errno);
if ( sizeof(body_buffer) < hdr->msgsize )
munmap(body, hdr->msgsize);
}
static volatile bool should_terminate = false;
void TerminationHandler(int)
{
should_terminate = true;
}
static void ready(void)
{
const char* readyfd_env = getenv("READYFD");
if ( !readyfd_env )
return;
int readyfd = atoi(readyfd_env);
char c = '\n';
write(readyfd, &c, 1);
close(readyfd);
unsetenv("READYFD");
}
int fsmarshall_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev)
{
(void) argv0;
// Stat the root inode.
struct stat root_inode_st;
Inode* root_inode = fs->GetInode(fs->root_ino);
if ( !root_inode )
error(1, errno, "GetInode");
StatInode(root_inode, &root_inode_st);
root_inode->Unref();
// Create a filesystem server connected to the kernel that we'll listen on.
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_inode_st, 0);
if ( serverfd < 0 )
error(1, errno, "%s", mount_path);
// Make sure the server isn't unexpectedly killed and data is lost.
signal(SIGINT, TerminationHandler);
signal(SIGTERM, TerminationHandler);
signal(SIGQUIT, TerminationHandler);
// Become a background process in its own process group by default.
if ( !foreground )
{
pid_t child_pid = fork();
if ( child_pid < 0 )
error(1, errno, "fork");
if ( child_pid )
exit(0);
setpgid(0, 0);
}
else
ready();
// Listen for filesystem messages.
int channel;
while ( 0 <= (channel = accept(serverfd, NULL, NULL)) )
{
if ( should_terminate )
break;
struct fsm_msg_header hdr;
size_t amount;
if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) )
{
//error(0, errno, "incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
errno = 0;
continue;
}
HandleIncomingMessage(channel, &hdr, fs);
close(channel);
}
// Garbage collect all open inode references.
while ( fs->mru_inode )
{
Inode* inode = fs->mru_inode;
if ( inode->remote_reference_count )
inode->RemoteUnref();
else if ( inode->reference_count )
inode->Unref();
}
close(serverfd);
delete fs;
delete dev;
return 0;
}
#endif

31
iso9660/fsmarshall.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fsmarshall.h
* Sortix fsmarshall frontend.
*/
#ifndef FSMARSHALL_H
#define FSMARSHALL_H
class Device;
class Filesystem;
int fsmarshall_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev);
#endif

572
iso9660/fuse.cpp Normal file
View File

@ -0,0 +1,572 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fuse.cpp
* FUSE frontend.
*/
#if !defined(__sortix__)
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define FUSE_USE_VERSION 26
#include <fuse.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "fuse.h"
#include "inode.h"
#include "iso9660fs.h"
#include <stdio.h> // DEBUG
struct iso9660_fuse_ctx
{
Device* dev;
Filesystem* fs;
};
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
#define FUSE_FS (((struct iso9660_fuse_ctx*) (fuse_get_context()->private_data))->fs)
void* iso9660_fuse_init(struct fuse_conn_info* /*conn*/)
{
return fuse_get_context()->private_data;
}
void iso9660_fuse_destroy(void* fs_private)
{
struct iso9660_fuse_ctx* iso9660_fuse_ctx =
(struct iso9660_fuse_ctx*) fs_private;
while ( iso9660_fuse_ctx->fs->mru_inode )
{
Inode* inode = iso9660_fuse_ctx->fs->mru_inode;
if ( inode->remote_reference_count )
inode->RemoteUnref();
else if ( inode->reference_count )
inode->Unref();
}
delete iso9660_fuse_ctx->fs; iso9660_fuse_ctx->fs = NULL;
delete iso9660_fuse_ctx->dev; iso9660_fuse_ctx->dev = NULL;
}
Inode* iso9660_fuse_resolve_path(const char* path)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode(fs->root_ino);
if ( !inode )
return (Inode*) NULL;
while ( path[0] )
{
if ( *path == '/' )
{
if ( !ISO9660_S_ISDIR(inode->Mode()) )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path++;
continue;
}
size_t elem_len = strcspn(path, "/");
char* elem = strndup(path, elem_len);
if ( !elem )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path += elem_len;
Inode* next = inode->Open(elem, O_RDONLY, 0);
free(elem);
inode->Unref();
if ( !next )
return NULL;
inode = next;
}
return inode;
}
// Assumes that the path doesn't end with / unless it's the root directory.
Inode* iso9660_fuse_parent_dir(const char** path_ptr)
{
const char* path = *path_ptr;
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode(fs->root_ino);
if ( !inode )
return (Inode*) NULL;
while ( strchr(path, '/') )
{
if ( *path == '/' )
{
if ( !ISO9660_S_ISDIR(inode->Mode()) )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path++;
continue;
}
size_t elem_len = strcspn(path, "/");
char* elem = strndup(path, elem_len);
if ( !elem )
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
path += elem_len;
Inode* next = inode->Open(elem, O_RDONLY, 0);
free(elem);
inode->Unref();
if ( !next )
return (Inode*) NULL;
inode = next;
}
*path_ptr = *path ? path : ".";
assert(!strchr(*path_ptr, '/'));
return inode;
}
int iso9660_fuse_getattr(const char* path, struct stat* st)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
StatInode(inode, st);
inode->Unref();
return 0;
}
int iso9660_fuse_fgetattr(const char* /*path*/, struct stat* st,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
StatInode(inode, st);
inode->Unref();
return 0;
}
int iso9660_fuse_readlink(const char* path, char* buf, size_t bufsize)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
if ( !ISO9660_S_ISLNK(inode->Mode()) )
return inode->Unref(), -(errno = EINVAL);
if ( !bufsize )
return inode->Unref(), -(errno = EINVAL);
ssize_t amount = inode->ReadLink((uint8_t*) buf, bufsize);
if ( amount < 0 )
return inode->Unref(), -errno;
buf[(size_t) amount < bufsize ? (size_t) amount : bufsize - 1] = '\0';
inode->Unref();
return 0;
}
int iso9660_fuse_mknod(const char* path, mode_t mode, dev_t dev)
{
(void) path;
(void) mode;
(void) dev;
return -(errno = ENOSYS);
}
int iso9660_fuse_mkdir(const char* path, mode_t /*mode*/)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
inode->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_unlink(const char* path)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
bool success = inode->Unlink(path, false);
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_rmdir(const char* path)
{
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
bool success = inode->RemoveDirectory(path);
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_symlink(const char* /*oldname*/, const char* newname)
{
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return -errno;
newdir->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_rename(const char* oldname, const char* newname)
{
Inode* olddir = iso9660_fuse_parent_dir(&oldname);
if ( !olddir )
return -errno;
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return olddir->Unref(), -errno;
bool success = newdir->Rename(olddir, oldname, newname);
newdir->Unref();
olddir->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_link(const char* oldname, const char* newname)
{
Inode* inode = iso9660_fuse_resolve_path(oldname);
if ( !inode )
return -errno;
Inode* newdir = iso9660_fuse_parent_dir(&newname);
if ( !newdir )
return inode->Unref(), -errno;
bool success = inode->Link(newname, inode);
newdir->Unref();
inode->Unref();
return success ? 0 : -errno;
}
int iso9660_fuse_chmod(const char* path, mode_t mode)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) mode;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_chown(const char* path, uid_t owner, gid_t group)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) owner;
(void) group;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_truncate(const char* path, off_t size)
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) size;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_ftruncate(const char* /*path*/, off_t size,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
(void) size;
return inode->Unref(), -(errno = EROFS);
}
int iso9660_fuse_open(const char* path, struct fuse_file_info* fi)
{
int flags = fi->flags;
Inode* dir = iso9660_fuse_parent_dir(&path);
if ( !dir )
return -errno;
Inode* result = dir->Open(path, flags, 0);
dir->Unref();
if ( !result )
return -errno;
fi->fh = (uint64_t) result->inode_id;
fi->keep_cache = 1;
result->RemoteRefer();
result->Unref();
return 0;
}
int iso9660_fuse_access(const char* path, int mode)
{
Inode* dir = iso9660_fuse_parent_dir(&path);
if ( !dir )
return -errno;
Inode* result = dir->Open(path, O_RDONLY, 0);
dir->Unref();
if ( !result )
return -errno;
(void) mode;
result->Unref();
return 0;
}
int iso9660_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi)
{
int flags = fi->flags | O_CREAT;
Inode* inode = iso9660_fuse_parent_dir(&path);
if ( !inode )
return -errno;
Inode* result = inode->Open(path, flags, FsModeFromHostMode(mode));
inode->Unref();
if ( !result )
return -errno;
fi->fh = (uint64_t) result->inode_id;
fi->keep_cache = 1;
result->RemoteRefer();
result->Unref();
return 0;
}
int iso9660_fuse_opendir(const char* path, struct fuse_file_info* fi)
{
return iso9660_fuse_open(path, fi);
}
int iso9660_fuse_read(const char* /*path*/, char* buf, size_t count,
off_t offset, struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
if ( INT_MAX < count )
count = INT_MAX;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset);
inode->Unref();
return 0 <= result ? (int) result : -errno;
}
int iso9660_fuse_write(const char* /*path*/, const char* /*buf*/,
size_t /*count*/, off_t /*offset*/,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return -(errno = EROFS);
}
int iso9660_fuse_statfs(const char* /*path*/, struct statvfs* stvfs)
{
memset(stvfs, 0, sizeof(*stvfs));
Filesystem* fs = FUSE_FS;
stvfs->f_bsize = fs->block_size;
stvfs->f_frsize = fs->block_size;
stvfs->f_blocks = fs->device->device_size / fs->block_size;
stvfs->f_bfree = 0;
stvfs->f_bavail = 0;
stvfs->f_files = 0;
stvfs->f_ffree = 0;
stvfs->f_favail = 0;
stvfs->f_ffree = 0;
stvfs->f_fsid = 0;
stvfs->f_flag = ST_RDONLY;
stvfs->f_namemax = 255;
return 0;
}
int iso9660_fuse_flush(const char* /*path*/, struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return 0;
}
int iso9660_fuse_release(const char* /*path*/, struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->RemoteUnref();
inode->Unref();
return 0;
}
int iso9660_fuse_releasedir(const char* path, struct fuse_file_info* fi)
{
return iso9660_fuse_release(path, fi);
}
int iso9660_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi)
{
(void) data;
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((uint32_t) fi->fh);
if ( !inode )
return -errno;
inode->Unref();
return 0;
}
/*int iso9660_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi)
{
return iso9660_fuse_sync(path, data, fi);
}*/
/*int iso9660_fuse_setxattr(const char *, const char *, const char *, size_t, int)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_getxattr(const char *, const char *, char *, size_t)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_listxattr(const char *, char *, size_t)
{
return -(errno = ENOSYS);
}*/
/*int iso9660_fuse_removexattr(const char *, const char *)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_readdir(const char* /*path*/, void* buf,
fuse_fill_dir_t filler, off_t rec_num,
struct fuse_file_info* fi)
{
Filesystem* fs = FUSE_FS;
Inode* inode = fs->GetInode((iso9660_ino_t) fi->fh);
if ( !inode )
return -errno;
if ( !S_ISDIR(inode->Mode()) )
return inode->Unref(), -(errno = ENOTDIR);
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( inode->ReadDirectory(&offset, &block, &block_id,
rec_num ? NULL : name, &file_type, &inode_id) )
{
if ( !rec_num || !rec_num-- )
{
if ( filler(buf, name, NULL, 0) )
{
block->Unref();
inode->Unref();
return 0;
}
}
}
int errnum = errno;
if ( block )
block->Unref();
inode->Unref();
if ( errnum )
return -errnum;
return 0;
}
/*int iso9660_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_utimens(const char* path, const struct timespec tv[2])
{
Inode* inode = iso9660_fuse_resolve_path(path);
if ( !inode )
return -errno;
(void) tv;
return inode->Unref(), -(errno = EROFS);
}
/*int iso9660_fuse_bmap(const char*, size_t blocksize, uint64_t* idx)
{
return -(errno = ENOSYS);
}*/
int iso9660_fuse_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev)
{
struct fuse_operations operations;
memset(&operations, 0, sizeof(operations));
operations.access = iso9660_fuse_access;
operations.chmod = iso9660_fuse_chmod;
operations.chown = iso9660_fuse_chown;
operations.create = iso9660_fuse_create;
operations.destroy = iso9660_fuse_destroy;
operations.fgetattr = iso9660_fuse_fgetattr;
operations.flush = iso9660_fuse_flush;
operations.fsync = iso9660_fuse_fsync;
operations.ftruncate = iso9660_fuse_ftruncate;
operations.getattr = iso9660_fuse_getattr;
operations.init = iso9660_fuse_init;
operations.link = iso9660_fuse_link;
operations.mkdir = iso9660_fuse_mkdir;
operations.mknod = iso9660_fuse_mknod;
operations.opendir = iso9660_fuse_opendir;
operations.open = iso9660_fuse_open;
operations.readdir = iso9660_fuse_readdir;
operations.read = iso9660_fuse_read;
operations.readlink = iso9660_fuse_readlink;
operations.releasedir = iso9660_fuse_releasedir;
operations.release = iso9660_fuse_release;
operations.rename = iso9660_fuse_rename;
operations.rmdir = iso9660_fuse_rmdir;
operations.statfs = iso9660_fuse_statfs;
operations.symlink = iso9660_fuse_symlink;
operations.truncate = iso9660_fuse_truncate;
operations.unlink = iso9660_fuse_unlink;
operations.utimens = iso9660_fuse_utimens;
operations.write = iso9660_fuse_write;
operations.flag_nullpath_ok = 1;
operations.flag_nopath = 1;
char* argv_fuse[] =
{
(char*) argv0,
(char*) "-s",
(char*) (foreground ? "-f" : mount_path),
(char*) (foreground ? mount_path : NULL),
(char*) NULL,
};
int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1;
struct iso9660_fuse_ctx iso9660_fuse_ctx;
iso9660_fuse_ctx.fs = fs;
iso9660_fuse_ctx.dev = dev;
return fuse_main(argc_fuse, argv_fuse, &operations, &iso9660_fuse_ctx);
}
#endif

32
iso9660/fuse.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* fuse.h
* FUSE frontend.
*/
#ifndef FUSE_H
#define FUSE_H
class Device;
class Filesystem;
int iso9660_fuse_main(const char* argv0,
const char* mount_path,
bool foreground,
Filesystem* fs,
Device* dev);
#endif

614
iso9660/inode.cpp Normal file
View File

@ -0,0 +1,614 @@
/*
* Copyright (c) 2013, 2014, 2015, 2018, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* inode.cpp
* Filesystem inode.
*/
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <endian.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "iso9660fs.h"
#include "util.h"
#ifndef S_SETABLE
#define S_SETABLE 02777
#endif
#ifndef O_WRITE
#define O_WRITE (O_WRONLY | O_RDWR)
#endif
Inode::Inode(Filesystem* filesystem, iso9660_ino_t inode_id)
{
this->prev_inode = NULL;
this->next_inode = NULL;
this->prev_hashed = NULL;
this->next_hashed = NULL;
this->data_block = NULL;
this->data = NULL;
this->filesystem = filesystem;
this->reference_count = 1;
this->remote_reference_count = 0;
this->inode_id = inode_id;
this->parent_inode_id = 0;
}
Inode::~Inode()
{
if ( data_block )
data_block->Unref();
Unlink();
}
void Inode::Parse()
{
const uint8_t* block_data = (const uint8_t*) data;
uid = 0;
gid = 0;
time = 0;
uint8_t file_flags = block_data[25];
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
mode = 0555 | (is_directory ? ISO9660_S_IFDIR : ISO9660_S_IFREG);
uint32_t u32;
memcpy(&u32, block_data + 10, sizeof(u32));
size = le32toh(u32);
nlink = 1;
const uint8_t* time_bytes = block_data + 18;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = time_bytes[0];
tm.tm_mon = time_bytes[1] - 1;
tm.tm_mday = time_bytes[2];
tm.tm_hour = time_bytes[3];
tm.tm_min = time_bytes[4];
tm.tm_sec = time_bytes[5];
// TODO: Is this accurate?
time_t tz_offset = (-48 + time_bytes[6]) * 15 * 60;
// TODO: The timezone offset should've been mixed in with mktime, somehow.
time = mktime(&tm) + tz_offset;
uint8_t dirent_len = block_data[0];
uint8_t name_len = block_data[32];
size_t extended_off = 33 + name_len + !(name_len & 1);
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( len == 36 && field[0] == 'P' && field[1] == 'X' && field[3] == 1 )
{
uint32_t bits;
memcpy(&bits, field + 4, sizeof(bits));
mode = le32toh(bits) & 0xffff;
memcpy(&bits, field + 12, sizeof(bits));
nlink = le32toh(bits);
memcpy(&bits, field + 20, sizeof(bits));
uid = le32toh(bits);
memcpy(&bits, field + 28, sizeof(bits));
gid = le32toh(bits);
}
}
if ( ISO9660_S_ISLNK(mode) )
{
uint8_t buf[256];
size = ReadLink(buf, sizeof(buf));
}
}
uint32_t Inode::Mode()
{
return mode;
}
uint32_t Inode::UserId()
{
return uid;
}
uint32_t Inode::GroupId()
{
return gid;
}
uint64_t Inode::Size()
{
return size;
}
uint64_t Inode::Time()
{
return time;
}
Block* Inode::GetBlock(uint32_t offset)
{
uint32_t lba;
memcpy(&lba, (uint8_t*) data + 2, sizeof(lba));
lba = le32toh(lba);
uint32_t block_id = lba + offset;
return filesystem->device->GetBlock(block_id);
}
bool Inode::FindParentInode(uint64_t parent_lba, uint64_t parent_size)
{
if ( inode_id == filesystem->root_ino )
return parent_inode_id = inode_id, true;
Block* block = NULL;
uint32_t block_size = filesystem->block_size;
uint64_t parent_offset = parent_lba * block_size;
uint64_t block_id = 0;
uint64_t offset = 0;
while ( offset < parent_size )
{
uint64_t entry_block_id = (parent_offset + offset) / block_size;
uint64_t entry_block_offset = (parent_offset + offset) % block_size;
if ( block && block_id != entry_block_id )
{
block->Unref();
block = NULL;
}
if ( !block && !(block = filesystem->device->GetBlock(entry_block_id)) )
return false;
const uint8_t* block_data = block->block_data + entry_block_offset;
uint8_t dirent_len = block_data[0];
if ( !dirent_len )
{
offset = (entry_block_id + 1) * block_size;
continue;
}
uint8_t name_len = block_data[32];
const uint8_t* name_data = block_data + 33;
if ( name_len == 0 || !name_data[0] )
{
parent_inode_id = parent_offset + offset;
return true;
}
// TODO: Can dirent_len be misaligned?
uint64_t reclen = dirent_len + (dirent_len & 1);
if ( !reclen )
return errno = EINVAL, false;
offset += reclen;
}
return errno = EINVAL, false;
}
bool Inode::ReadDirectory(uint64_t* offset_inout,
Block** block_inout,
uint64_t* block_id_inout,
char* name,
uint8_t* file_type_out,
iso9660_ino_t* inode_id_out)
{
uint64_t offset = *offset_inout;
next_block:
uint64_t filesize = Size();
if ( filesize <= offset )
return errno = 0, false;
uint64_t entry_block_id = offset / filesystem->block_size;
uint64_t entry_block_offset = offset % filesystem->block_size;
if ( *block_inout && *block_id_inout != entry_block_id )
{
(*block_inout)->Unref();
(*block_inout) = NULL;
}
if ( !*block_inout &&
!(*block_inout = GetBlock(*block_id_inout = entry_block_id)) )
return false;
const uint8_t* block_data = (*block_inout)->block_data + entry_block_offset;
uint8_t dirent_len = block_data[0];
if ( !dirent_len )
{
offset = (entry_block_id + 1) * filesystem->block_size;
goto next_block;
}
if ( name )
{
uint8_t name_len = block_data[32];
const uint8_t* name_data = block_data + 33;
size_t extended_off = 33 + name_len + !(name_len & 1);
iso9660_ino_t entry_inode_id =
((*block_inout)->block_id * filesystem->block_size) +
entry_block_offset;
// TODO: The root directory inode should be that of its . entry.
if ( name_len == 0 || !name_data[0] )
{
name[0] = '.';
name[1] = '\0';
name_len = 1;
entry_inode_id = inode_id;
}
else if ( name_len == 1 && name_data[0] == 1 )
{
name[0] = '.';
name[1] = '.';
name[2] = '\0';
name_len = 2;
if ( !parent_inode_id )
{
uint32_t parent_lba;
memcpy(&parent_lba, block_data + 2, sizeof(parent_lba));
parent_lba = le32toh(parent_lba);
uint32_t parent_size;
memcpy(&parent_size, block_data + 10, sizeof(parent_size));
parent_size = le32toh(parent_size);
if ( !FindParentInode(parent_lba, parent_size) )
return false;
}
entry_inode_id = parent_inode_id;
}
else
{
for ( size_t i = 0; i < name_len; i++ )
{
if ( name_data[i] == ';' )
{
name_len = i;
break;
}
name[i] = tolower(name_data[i]);
}
name[name_len] = '\0';
}
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( 5 <= len && field[0] == 'N' && field[1] == 'M' &&
field[3] == 1 )
{
uint8_t nm_flags = field[4];
if ( nm_flags & (1 << 0) ) // TODO: Continued names.
break;
name_len = len - 5;
memcpy(name, field + 5, name_len);
name[name_len] = '\0';
}
// TODO: Other extensions.
}
uint8_t file_flags = block_data[25];
// TODO: Rock Ridge.
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
uint8_t file_type = is_directory ? ISO9660_FT_DIR : ISO9660_FT_REG_FILE;
*file_type_out = file_type;
*inode_id_out = entry_inode_id;
}
// TODO: Can dirent_len be misaligned?
uint64_t reclen = dirent_len + (dirent_len & 1);
if ( !reclen )
return errno = EINVAL, false;
offset += reclen;
*offset_inout = offset;
return true;
}
Inode* Inode::Open(const char* elem, int flags, mode_t mode)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, (Inode*) NULL;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
block->Unref();
if ( (flags & O_CREAT) && (flags & O_EXCL) )
return errno = EEXIST, (Inode*) NULL;
if ( (flags & O_DIRECTORY) &&
file_type != ISO9660_FT_UNKNOWN &&
file_type != ISO9660_FT_DIR &&
file_type != ISO9660_FT_SYMLINK )
return errno = ENOTDIR, (Inode*) NULL;
Inode* inode = filesystem->GetInode(inode_id);
if ( !inode )
return (Inode*) NULL;
if ( flags & O_DIRECTORY &&
!ISO9660_S_ISDIR(inode->Mode()) &&
!ISO9660_S_ISLNK(inode->Mode()) )
{
inode->Unref();
return errno = ENOTDIR, (Inode*) NULL;
}
if ( flags & O_WRITE )
{
inode->Unref();
return errno = EROFS, (Inode*) NULL;
}
return inode;
}
}
if ( block )
block->Unref();
if ( flags & O_CREAT )
{
(void) mode;
return errno = EROFS, (Inode*) NULL;
}
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Link(const char* elem, Inode* dest)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, false;
if ( ISO9660_S_ISDIR(dest->Mode()) )
return errno = EISDIR, false;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, false;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
block->Unref();
return errno = EEXIST, false;
}
}
if ( block )
block->Unref();
return errno = EROFS, false;
}
Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force)
{
if ( !ISO9660_S_ISDIR(Mode()) )
return errno = ENOTDIR, (Inode*) NULL;
size_t elem_length = strlen(elem);
if ( elem_length == 0 )
return errno = ENOENT, (Inode*) NULL;
uint64_t offset = 0;
Block* block = NULL;
uint64_t block_id = 0;
char name[256];
uint8_t file_type;
iso9660_ino_t inode_id;
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
&inode_id) )
{
size_t name_len = strlen(name);
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
{
(void) directories;
(void) force;
block->Unref();
return errno = EROFS, (Inode*) NULL;
}
}
if ( block )
block->Unref();
return errno = ENOENT, (Inode*) NULL;
}
bool Inode::Unlink(const char* elem, bool directories, bool force)
{
Inode* result = UnlinkKeep(elem, directories, force);
if ( !result )
return false;
result->Unref();
return true;
}
ssize_t Inode::ReadLink(uint8_t* buf, size_t bufsize)
{
size_t result = 0;
const uint8_t* block_data = (const uint8_t*) data;
uint8_t dirent_len = block_data[0];
uint8_t name_len = block_data[32];
size_t extended_off = 33 + name_len + !(name_len & 1);
bool continued = true;
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
{
const uint8_t* field = block_data + i;
uint8_t len = field[2];
if ( !len || dirent_len - i < len )
break;
i += len;
if ( 5 <= len && field[0] == 'S' && field[1] == 'L' && field[3] == 1 )
{
for ( size_t n = 5; n < len && 2 <= len - n; )
{
uint8_t comp_flags = field[n + 0];
uint8_t comp_len = field[n + 1];
if ( len - (n + 2) < comp_len )
break;
const char* data = (const char*) (field + n + 2);
size_t datalen = comp_len;
// TODO: How is a trailing slash encoded?
if ( !continued || (comp_flags & (1 << 3) /* root */) )
{
buf[result++] = '/';
if ( result == bufsize )
return (ssize_t) result;
}
if ( comp_flags & (1 << 1) )
{
data = ".";
datalen = 1;
}
else if ( comp_flags & (1 << 2) )
{
data = "..";
datalen = 2;
}
size_t possible = bufsize - result;
size_t count = datalen < possible ? datalen : possible;
memcpy(buf + result, data, count);
result += count;
if ( result == bufsize )
return (ssize_t) result;
continued = comp_flags & (1 << 0);
n += 2 + comp_len;
}
}
}
return (ssize_t) result;
}
ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
{
if ( !ISO9660_S_ISREG(Mode()) && !ISO9660_S_ISLNK(Mode()) )
return errno = EISDIR, -1;
if ( o_offset < 0 )
return errno = EINVAL, -1;
if ( SSIZE_MAX < s_count )
s_count = SSIZE_MAX;
uint64_t sofar = 0;
uint64_t count = (uint64_t) s_count;
uint64_t offset = (uint64_t) o_offset;
uint64_t file_size = Size();
if ( file_size <= offset )
return 0;
if ( file_size - offset < count )
count = file_size - offset;
while ( sofar < count )
{
uint64_t block_id = offset / filesystem->block_size;
uint32_t block_offset = offset % filesystem->block_size;
uint32_t block_left = filesystem->block_size - block_offset;
Block* block = GetBlock(block_id);
if ( !block )
return sofar ? sofar : -1;
size_t amount = count - sofar < block_left ? count - sofar : block_left;
memcpy(buf + sofar, block->block_data + block_offset, amount);
sofar += amount;
offset += amount;
block->Unref();
}
return (ssize_t) sofar;
}
bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname)
{
if ( !strcmp(oldname, ".") || !strcmp(oldname, "..") ||
!strcmp(newname, ".") || !strcmp(newname, "..") )
return errno = EPERM, false;
Inode* src_inode = olddir->Open(oldname, O_RDONLY, 0);
if ( !src_inode )
return false;
if ( Inode* dst_inode = Open(newname, O_RDONLY, 0) )
{
bool same_inode = src_inode->inode_id == dst_inode->inode_id;
dst_inode->Unref();
if ( same_inode )
return src_inode->Unref(), true;
}
src_inode->Unref();
return errno = EROFS, false;
}
bool Inode::RemoveDirectory(const char* path)
{
return UnlinkKeep(path, true);
}
void Inode::Refer()
{
reference_count++;
}
void Inode::Unref()
{
assert(0 < reference_count);
reference_count--;
if ( !reference_count && !remote_reference_count )
delete this;
}
void Inode::RemoteRefer()
{
remote_reference_count++;
}
void Inode::RemoteUnref()
{
assert(0 < remote_reference_count);
remote_reference_count--;
if ( !reference_count && !remote_reference_count )
delete this;
}
void Inode::Use()
{
data_block->Use();
Unlink();
Prelink();
}
void Inode::Unlink()
{
(prev_inode ? prev_inode->next_inode : filesystem->mru_inode) = next_inode;
(next_inode ? next_inode->prev_inode : filesystem->lru_inode) = prev_inode;
size_t bin = inode_id % INODE_HASH_LENGTH;
(prev_hashed ? prev_hashed->next_hashed : filesystem->hash_inodes[bin]) = next_hashed;
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
}
void Inode::Prelink()
{
prev_inode = NULL;
next_inode = filesystem->mru_inode;
if ( filesystem->mru_inode )
filesystem->mru_inode->prev_inode = this;
filesystem->mru_inode = this;
if ( !filesystem->lru_inode )
filesystem->lru_inode = this;
size_t bin = inode_id % INODE_HASH_LENGTH;
prev_hashed = NULL;
next_hashed = filesystem->hash_inodes[bin];
filesystem->hash_inodes[bin] = this;
if ( next_hashed )
next_hashed->prev_hashed = this;
}

83
iso9660/inode.h Normal file
View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* inode.h
* Filesystem inode.
*/
#ifndef INODE_H
#define INODE_H
#include "iso9660.h"
class Block;
class Filesystem;
class Inode
{
public:
Inode(Filesystem* filesystem, iso9660_ino_t inode_id);
~Inode();
public:
Inode* prev_inode;
Inode* next_inode;
Inode* prev_hashed;
Inode* next_hashed;
Block* data_block;
struct iso9660_dirent* data;
Filesystem* filesystem;
size_t reference_count;
size_t remote_reference_count;
iso9660_ino_t inode_id;
iso9660_ino_t parent_inode_id;
uint32_t uid;
uint32_t gid;
uint64_t size;
uint64_t time;
uint32_t mode;
uint32_t nlink;
public:
void Parse();
uint32_t Mode();
uint32_t UserId();
uint32_t GroupId();
uint64_t Size();
uint64_t Time();
Block* GetBlock(uint32_t offset);
bool FindParentInode(uint64_t parent_lba, uint64_t parent_size);
bool ReadDirectory(uint64_t* offset_inout, Block** block_inout,
uint64_t* block_id_inout, char* name,
uint8_t* file_type_out, iso9660_ino_t* inode_id_out);
Inode* Open(const char* elem, int flags, mode_t mode);
bool Link(const char* elem, Inode* dest);
bool Unlink(const char* elem, bool directories, bool force=false);
Inode* UnlinkKeep(const char* elem, bool directories, bool force=false);
ssize_t ReadLink(uint8_t* buf, size_t bufsize);
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
bool Rename(Inode* olddir, const char* oldname, const char* newname);
bool RemoveDirectory(const char* path);
void Refer();
void Unref();
void RemoteRefer();
void RemoteUnref();
void Use();
void Unlink();
void Prelink();
};
#endif

150
iso9660/ioleast.h Normal file
View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ioleast.h
* Versions of {,p}{read,write} that don't return until it has returned as much
* data as requested, end of file, or an error occurs. This is sometimes needed
* as read(2) and write(2) is not always guaranteed to fill up the entire
* buffer or write it all.
*/
#ifndef SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
#define SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
#if defined(__sortix__) || defined(__sortix_libc__)
#include_next <ioleast.h>
#else
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#if !defined(EEOF) && defined(EIO)
#define EEOF EIO
#endif
__attribute__((unused)) static inline
size_t readleast(int fd, void* buf_ptr, size_t least, size_t max)
{
assert(least <= max);
unsigned char* buf = (unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = read(fd, buf + done, max - done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t writeleast(int fd, const void* buf_ptr, size_t least, size_t max)
{
assert(least <= max);
const unsigned char* buf = (const unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = write(fd, buf + done, max - done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t preadleast(int fd, void* buf_ptr, size_t least, size_t max, off_t off)
{
assert(least <= max);
unsigned char* buf = (unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = pread(fd, buf + done, max - done, off + done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t pwriteleast(int fd, const void* buf_ptr, size_t least, size_t max, off_t off)
{
assert(least <= max);
const unsigned char* buf = (const unsigned char*) buf_ptr;
size_t done = 0;
do
{
ssize_t amount = pwrite(fd, buf + done, max - done, off + done);
if ( amount < 0 )
return done;
if ( !amount && done < least )
return errno = EEOF, done;
if ( !amount )
break;
done += amount;
} while ( done < least );
return done;
}
__attribute__((unused)) static inline
size_t readall(int fd, void* buf, size_t count)
{
return readleast(fd, buf, count, count);
}
__attribute__((unused)) static inline
size_t writeall(int fd, const void* buf, size_t count)
{
return writeleast(fd, buf, count, count);
}
__attribute__((unused)) static inline
size_t preadall(int fd, void* buf, size_t count, off_t off)
{
return preadleast(fd, buf, count, count, off);
}
__attribute__((unused)) static inline
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
{
return pwriteleast(fd, buf, count, count, off);
}
#endif
#endif

128
iso9660/iso9660.h Normal file
View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660.h
* Data structures for the ISO 9660 filesystem.
*/
#ifndef ISO9660_H
#define ISO9660_H
#include <stdint.h>
const uint8_t TYPE_BOOT_RECORD = 0x00;
const uint8_t TYPE_PRIMARY_VOLUME_DESCRIPTOR = 0x01;
const uint8_t TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR = 0xFF;
struct iso9660_pvd /* primary volume descriptor */
{
uint8_t type;
char standard_identifier[5];
uint8_t version;
uint8_t unused1;
char system_identifier[32];
char volume_identifier[32];
uint8_t unused2[8];
uint32_t volume_space_size_le;
uint32_t volume_space_size_be;
uint8_t unused3[32];
uint16_t volume_set_size_le;
uint16_t volume_set_size_be;
uint16_t volume_sequence_number_le;
uint16_t volume_sequence_number_be;
uint16_t logical_block_size_le;
uint16_t logical_block_size_be;
uint32_t path_table_size_le;
uint32_t path_table_size_be;
uint32_t path_table_lba_le;
uint32_t path_table_opt_lba_le;
uint32_t path_table_lba_be;
uint32_t path_table_opt_lba_be;
uint8_t root_dirent[34];
char volume_set_identifier[128];
char publisher_identifier[128];
char data_preparer_identifier[128];
char application_identifier[128];
char copyright_file_identifier[37];
char abstract_file_identifier[37];
char bibliographic_file_identifier[37];
char creation_datetime[17];
char modification_datetime[17];
char expiration_datetime[17];
char effective_datetime[17];
uint8_t file_structure_version;
uint8_t unused4;
uint8_t application_use[512];
uint8_t reserved[653];
};
static_assert(sizeof(struct iso9660_pvd) == 2048,
"sizeof(struct iso9660_pvd) == 2048");
typedef uint64_t iso9660_ino_t;
struct iso9660_dirent
{
uint8_t unused;
};
#define ISO9660_DIRENT_FLAG_DIR (1 << 1)
#define ISO9660_S_IFMT (0170000)
#define ISO9660_S_IFIFO (0010000)
#define ISO9660_S_IFCHR (0020000)
#define ISO9660_S_IFDIR (0040000)
#define ISO9660_S_IFBLK (0060000)
#define ISO9660_S_IFREG (0100000)
#define ISO9660_S_IFLNK (0120000)
#define ISO9660_S_IFSOCK (0140000)
#define ISO9660_S_ISSOCK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFSOCK)
#define ISO9660_S_ISLNK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFLNK)
#define ISO9660_S_ISREG(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFREG)
#define ISO9660_S_ISBLK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFBLK)
#define ISO9660_S_ISDIR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFDIR)
#define ISO9660_S_ISCHR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFCHR)
#define ISO9660_S_ISFIFO(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFIFO)
static const uint8_t ISO9660_FT_UNKNOWN = 0;
static const uint8_t ISO9660_FT_REG_FILE = 1;
static const uint8_t ISO9660_FT_DIR = 2;
static const uint8_t ISO9660_FT_CHRDEV = 3;
static const uint8_t ISO9660_FT_BLKDEV = 4;
static const uint8_t ISO9660_FT_FIFO = 5;
static const uint8_t ISO9660_FT_SOCK = 6;
static const uint8_t ISO9660_FT_SYMLINK = 7;
static inline uint8_t ISO9660_FT_OF_MODE(mode_t mode)
{
if ( ISO9660_S_ISREG(mode) )
return ISO9660_FT_REG_FILE;
if ( ISO9660_S_ISDIR(mode) )
return ISO9660_FT_DIR;
if ( ISO9660_S_ISCHR(mode) )
return ISO9660_FT_CHRDEV;
if ( ISO9660_S_ISBLK(mode) )
return ISO9660_FT_BLKDEV;
if ( ISO9660_S_ISFIFO(mode) )
return ISO9660_FT_FIFO;
if ( ISO9660_S_ISSOCK(mode) )
return ISO9660_FT_SOCK;
if ( ISO9660_S_ISLNK(mode) )
return ISO9660_FT_SYMLINK;
return ISO9660_FT_UNKNOWN;
}
#endif

279
iso9660/iso9660fs.cpp Normal file
View File

@ -0,0 +1,279 @@
/*
* Copyright (c) 2013, 2014, 2015, 2016, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660fs.cpp
* Implementation of the ISO 9660 filesystem.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <dirent.h>
#include <endian.h>
#include <err.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__sortix__)
#include "fsmarshall.h"
#else
#include "fuse.h"
#endif
#include "block.h"
#include "device.h"
#include "filesystem.h"
#include "inode.h"
#include "ioleast.h"
#include "iso9660.h"
#include "iso9660fs.h"
#include "util.h"
uid_t request_uid;
uid_t request_gid;
mode_t HostModeFromFsMode(uint32_t mode)
{
mode_t hostmode = mode & 07777;
if ( ISO9660_S_ISSOCK(mode) ) hostmode |= S_IFSOCK;
if ( ISO9660_S_ISLNK(mode) ) hostmode |= S_IFLNK;
if ( ISO9660_S_ISREG(mode) ) hostmode |= S_IFREG;
if ( ISO9660_S_ISBLK(mode) ) hostmode |= S_IFBLK;
if ( ISO9660_S_ISDIR(mode) ) hostmode |= S_IFDIR;
if ( ISO9660_S_ISCHR(mode) ) hostmode |= S_IFCHR;
if ( ISO9660_S_ISFIFO(mode) ) hostmode |= S_IFIFO;
return hostmode;
}
uint32_t FsModeFromHostMode(mode_t hostmode)
{
uint32_t mode = hostmode & 07777;
if ( S_ISSOCK(hostmode) ) mode |= ISO9660_S_IFSOCK;
if ( S_ISLNK(hostmode) ) mode |= ISO9660_S_IFLNK;
if ( S_ISREG(hostmode) ) mode |= ISO9660_S_IFREG;
if ( S_ISBLK(hostmode) ) mode |= ISO9660_S_IFBLK;
if ( S_ISDIR(hostmode) ) mode |= ISO9660_S_IFDIR;
if ( S_ISCHR(hostmode) ) mode |= ISO9660_S_IFCHR;
if ( S_ISFIFO(hostmode) ) mode |= ISO9660_S_IFIFO;
return mode;
}
uint8_t HostDTFromFsDT(uint8_t fsdt)
{
switch ( fsdt )
{
case ISO9660_FT_UNKNOWN: return DT_UNKNOWN;
case ISO9660_FT_REG_FILE: return DT_REG;
case ISO9660_FT_DIR: return DT_DIR;
case ISO9660_FT_CHRDEV: return DT_CHR;
case ISO9660_FT_BLKDEV: return DT_BLK;
case ISO9660_FT_FIFO: return DT_FIFO;
case ISO9660_FT_SOCK: return DT_SOCK;
case ISO9660_FT_SYMLINK: return DT_LNK;
}
return DT_UNKNOWN;
}
void StatInode(Inode* inode, struct stat* st)
{
memset(st, 0, sizeof(*st));
st->st_ino = inode->inode_id;
st->st_mode = HostModeFromFsMode(inode->Mode());
st->st_nlink = inode->nlink;
st->st_uid = inode->uid;
st->st_gid = inode->gid;
st->st_size = inode->size;
// TODO: Rock Ridge.
st->st_atim.tv_sec = inode->Time();
st->st_atim.tv_nsec = 0;
// TODO: Rock Ridge.
st->st_ctim.tv_sec = inode->Time();
st->st_ctim.tv_nsec = 0;
// TODO: Rock Ridge.
st->st_mtim.tv_sec = inode->Time();
st->st_mtim.tv_nsec = 0;
st->st_blksize = inode->filesystem->block_size;
st->st_blocks = divup(st->st_size, (off_t) 512);
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
{
while ( i < *argc && !(*argv)[i] )
{
for ( int n = i; n < *argc; n++ )
(*argv)[n] = (*argv)[n+1];
(*argc)--;
}
}
}
static void help(FILE* fp, const char* argv0)
{
fprintf(fp, "Usage: %s [OPTION]... DEVICE [MOUNT-POINT]\n", argv0);
}
static void version(FILE* fp, const char* argv0)
{
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
}
int main(int argc, char* argv[])
{
const char* argv0 = argv[0];
const char* pretend_mount_path = NULL;
bool foreground = false;
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
if ( arg[0] != '-' || !arg[1] )
continue;
argv[i] = NULL;
if ( !strcmp(arg, "--") )
break;
if ( arg[1] != '-' )
{
while ( char c = *++arg ) switch ( c )
{
case 'r': break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
help(stderr, argv0);
exit(1);
}
}
else if ( !strcmp(arg, "--help") )
help(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--version") )
version(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--background") )
foreground = false;
else if ( !strcmp(arg, "--foreground") )
foreground = true;
else if ( !strcmp(arg, "--read") )
{
}
else if ( !strncmp(arg, "--pretend-mount-path=", strlen("--pretend-mount-path=")) )
pretend_mount_path = arg + strlen("--pretend-mount-path=");
else if ( !strcmp(arg, "--pretend-mount-path") )
{
if ( i+1 == argc )
{
fprintf(stderr, "%s: --pretend-mount-path: Missing operand\n", argv0);
exit(1);
}
pretend_mount_path = argv[++i];
argv[i] = NULL;
}
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
help(stderr, argv0);
exit(1);
}
}
if ( argc == 1 )
{
help(stdout, argv0);
exit(0);
}
compact_arguments(&argc, &argv);
const char* device_path = 2 <= argc ? argv[1] : NULL;
const char* mount_path = 3 <= argc ? argv[2] : NULL;
if ( !device_path )
{
help(stderr, argv0);
exit(1);
}
if ( !pretend_mount_path )
pretend_mount_path = mount_path;
int fd = open(device_path, O_RDONLY);
if ( fd < 0 )
error(1, errno, "`%s'", device_path);
// Search for the Primary Volume Descriptor.
off_t block_size = 2048;
struct iso9660_pvd pvd;
off_t pvd_offset;
for ( uint32_t i = 16; true; i++ )
{
pvd_offset = block_size * i;
if ( preadall(fd, &pvd, sizeof(pvd), pvd_offset) != sizeof(pvd) )
{
if ( errno == EEOF )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
else
err(1, "read: %s", device_path);
}
if ( memcmp(pvd.standard_identifier, "CD001", 5) != 0 )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
if ( pvd.type == TYPE_PRIMARY_VOLUME_DESCRIPTOR )
break;
if ( pvd.type == TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR )
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
}
if ( pvd.version != 1 || pvd.file_structure_version != 1 )
errx(1, "Unsupported ISO 9660 filesystem version: %s", device_path);
// TODO: Test if appropriate power of two and allow.
if ( le32toh(pvd.logical_block_size_le) != block_size )
errx(1, "Unsupported ISO 9660 block size: %s", device_path);
block_size = le32toh(pvd.logical_block_size_le);
Device* dev = new Device(fd, device_path, block_size);
if ( !dev )
error(1, errno, "malloc");
Filesystem* fs = new Filesystem(dev, pretend_mount_path, pvd_offset);
if ( !fs )
error(1, errno, "malloc");
// Change the root inode to be its own . entry instead of the pvd record, so
// parent directories correctly .. to it, and so Rock Ridge extensions work.
Inode* root = fs->GetInode(fs->root_ino);
if ( !root )
error(1, errno, "GetInode");
fs->root_ino = 0;
uint32_t root_lba;
uint32_t root_size;
memcpy(&root_lba, pvd.root_dirent + 2, sizeof(root_lba));
memcpy(&root_size, pvd.root_dirent + 10, sizeof(root_size));
root->FindParentInode(root_lba, root_size);
fs->root_ino = root->parent_inode_id;
root->Unref();
if ( !mount_path )
return 0;
#if defined(__sortix__)
return fsmarshall_main(argv0, mount_path, foreground, fs, dev);
#else
return iso9660_fuse_main(argv0, mount_path, foreground, fs, dev);
#endif
}

33
iso9660/iso9660fs.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660fs.h
* Implementation of the ISO 9660 filesystem.
*/
#ifndef ISO9660FS_H
#define ISO9660FS_H
extern uid_t request_uid;
extern gid_t request_gid;
class Inode;
mode_t HostModeFromFsMode(uint32_t fsmode);
uint32_t FsModeFromHostMode(mode_t hostmode);
uint8_t HostDTFromFsDT(uint8_t fsdt);
void StatInode(Inode* inode, struct stat* st);
#endif

49
iso9660/util.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* util.h
* Utility functions for the filesystem implementation.
*/
#ifndef UTIL_H
#define UTIL_H
template <class T> T divup(T a, T b)
{
return a/b + (a % b ? 1 : 0);
}
template <class T> T roundup(T a, T b)
{
return a % b ? a + b - a % b : a;
}
static inline bool checkbit(const uint8_t* bitmap, size_t bit)
{
uint8_t bits = bitmap[bit / 8UL];
return bits & (1U << (bit % 8UL));
}
static inline void setbit(uint8_t* bitmap, size_t bit)
{
bitmap[bit / 8UL] |= 1U << (bit % 8UL);
}
static inline void clearbit(uint8_t* bitmap, size_t bit)
{
bitmap[bit / 8UL] &= ~(1U << (bit % 8UL));
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -29,6 +29,7 @@
namespace Sortix {
size_t aux_allocated = 0;
size_t aux_leaked = 0;
size_t heap_allocated = 0;
kthread_mutex_t alloc_lock = KTHREAD_MUTEX_INITIALIZER;
@ -69,6 +70,8 @@ void FreeKernelAddress(addralloc_t* alloc)
addr_t aux_reached = kmem_from + kmem_size - aux_allocated;
if ( alloc->from == aux_reached )
aux_allocated -= alloc->size;
else
aux_leaked += alloc->size;
alloc->from = 0;
alloc->size = 0;
@ -98,6 +101,17 @@ void ShrinkHeap(size_t decrease)
heap_allocated -= decrease;
}
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked,
size_t* total)
{
ScopedLock lock(&alloc_lock);
addr_t kmem_from;
Memory::GetKernelVirtualArea(&kmem_from, total);
*heap = heap_allocated;
*aux = aux_allocated - aux_leaked;
*leaked = aux_leaked;
}
// No need for locks in these three functions, since only the heap calls these
// and it already uses an internal lock, and heap_allocated will not change
// unless the heap calls ExpandHeap.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2016, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -49,11 +49,17 @@ static unsigned long AllocateDiskNumber()
return InterlockedIncrement(&next_disk_number).o;
}
static void sleep_400_nanoseconds()
static void sleep_400_nanoseconds(uint16_t port_base)
{
// TODO: The clock granularity of 10 ms slows down the early boot.
#if 0
struct timespec delay = timespec_make(0, 400);
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
clock->SleepDelay(delay);
#else
for ( int i = 0; i < 14; i++ )
inport8(port_base + REG_STATUS);
#endif
}
Channel::Channel()
@ -105,7 +111,7 @@ void Channel::SelectDrive(unsigned int drive_index) // hw_lock locked
outport8(port_base + REG_DRIVE_SELECT, value);
//outport8(port_control, value); // TODO: Or is it port_control we use?
sleep_400_nanoseconds();
sleep_400_nanoseconds(port_base);
// TODO: Do we need to wait for non-busy now? Can this operation fail?
@ -229,7 +235,7 @@ bool Channel::Initialize(Ref<Descriptor> dev, const char* devpath)
busmaster_base = busmasterbar.addr() + 8 * channel_index;
current_drive = (unsigned int) -1; // We don't know.
current_drive = (inport8(port_base + REG_DRIVE_SELECT) >> 4) & 1;
for ( unsigned int i = 0; i < 2; i++ )
{

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2016, 2018, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2016, 2018, 2021, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -56,11 +56,17 @@ static void copy_ata_string(char* dest, const char* src, size_t length)
dest[length] = '\0';
}
static void sleep_400_nanoseconds()
static void sleep_400_nanoseconds(uint16_t port_base)
{
// TODO: The clock granularity of 10 ms slows down the early boot.
#if 0
struct timespec delay = timespec_make(0, 400);
Clock* clock = Time::GetClock(CLOCK_BOOTTIME);
clock->SleepDelay(delay);
#else
for ( int i = 0; i < 14; i++ )
inport8(port_base + REG_STATUS);
#endif
}
Port::Port(Channel* channel, unsigned int port_index)
@ -176,7 +182,7 @@ retry_identify_packet:
outport8(channel->port_base + REG_COMMAND,
is_packet_interface ? CMD_IDENTIFY_PACKET : CMD_IDENTIFY);
sleep_400_nanoseconds();
sleep_400_nanoseconds(channel->port_base);
// TODO: The status polling logic should be double-checked against some
// formal specification telling how this should properly be done.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -39,6 +39,7 @@ void ShrinkHeap(size_t decrease);
addr_t GetHeapLower();
addr_t GetHeapUpper();
size_t GetHeapSize();
void KernelAddressStatistics(size_t* heap, size_t* aux, size_t* leaked, size_t* total);
} // namespace Sortix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -36,4 +36,35 @@ typedef uintptr_t addr_t;
#define CPU X64
#endif
#ifdef __TRACE_ALLOCATION_SITES
struct kernel_allocation_site
{
struct __allocation_site allocation_site;
struct kernel_allocation_site* next;
bool registered;
};
extern struct kernel_allocation_site* first_kernel_allocation_site;
#undef ALLOCATION_SITE
#define ALLOCATION_SITE \
({ \
static struct kernel_allocation_site site = \
{ { __FILE__, __LINE__, __func__, 0, 0 }, NULL, false }; \
if ( !site.registered ) \
{ \
site.registered = true; \
site.next = first_kernel_allocation_site; \
first_kernel_allocation_site = &site; \
} \
&site.allocation_site; \
})
#include <stddef.h>
void* operator new(size_t size, struct __allocation_site* allocation_site);
void* operator new[](size_t size, struct __allocation_site* allocation_site);
#define new new (ALLOCATION_SITE)
#endif
#endif

View File

@ -102,6 +102,8 @@
#include "x86-family/vbox.h"
#endif
struct kernel_allocation_site* first_kernel_allocation_site = NULL;
// Keep the stack size aligned with $CPU/boot.s
const size_t STACK_SIZE = 64*1024;
extern "C" { __attribute__((aligned(16))) size_t stack[STACK_SIZE / sizeof(size_t)]; }

View File

@ -24,6 +24,8 @@
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/syscall.h>
#include "net/tcp.h"
#ifndef VERSIONSTR
#define VERSIONSTR "unknown"
#endif
@ -50,6 +52,77 @@ ssize_t sys_kernelinfo(const char* user_req, char* user_resp, size_t resplen)
char* req = GetStringFromUser(user_req);
if ( !req )
return -1;
// DEBUG
if ( !strcmp(req, "virtual-usage") )
{
delete[] req;
size_t heap;
size_t aux;
size_t leaked;
size_t total;
KernelAddressStatistics(&heap, &aux, &leaked, &total);
char str[4 * (20 + 3 * sizeof(size_t) + 1 + 8)];
snprintf(str, sizeof(str),
"%20zu B heap\n%20zu B aux\n%20zu B leaked\n%20zu B total",
heap, aux, leaked, total);
size_t stringlen = strlen(str);
if ( resplen < stringlen + 1 )
return errno = ERANGE, (ssize_t) stringlen;
if ( !CopyToUser(user_resp, str, sizeof(char) * (stringlen + 1)) )
return -1;
return 0;
}
// DEBUG
if ( !strcmp(req, "tcp") )
{
delete[] req;
return TCP::Info(user_resp, resplen);
}
#ifdef __TRACE_ALLOCATION_SITES
if ( !strcmp(req, "allocations") )
{
delete[] req;
bool exhausted = false;
size_t total_needed = 0;
for ( struct kernel_allocation_site* site = first_kernel_allocation_site;
site;
site = site->next )
{
char str[256];
snprintf(str, sizeof(str), "%20zu B %zu allocations %s:%zu %s %c",
site->allocation_site.current_size,
site->allocation_site.allocations,
site->allocation_site.file,
site->allocation_site.line,
site->allocation_site.function,
site->next ? '\n' : '\0');
size_t stringlen = strlen(str);
total_needed += stringlen;
if ( exhausted )
continue;
if ( resplen < stringlen )
{
exhausted = true;
continue;
}
if ( !CopyToUser(user_resp, str, sizeof(char) * stringlen) )
return -1;
user_resp += stringlen;
resplen -= stringlen;
}
if ( !exhausted && !resplen )
exhausted = true;
if ( !exhausted )
{
char zero = '\0';
if ( !CopyToUser(user_resp, &zero, 1) )
return -1;
}
if ( exhausted )
return errno = ERANGE, (ssize_t) total_needed;
return 0;
}
#endif
const char* str = KernelInfo(req);
delete[] req;
if ( !str )

View File

@ -50,6 +50,7 @@ LineBuffer::~LineBuffer()
bool LineBuffer::Push(uint32_t unicode)
{
// TODO: An infinite line buffer seems like a bad idea.
// Check if we need to allocate or resize the circular queue.
if ( bufferused == bufferlength )
{

View File

@ -36,6 +36,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
@ -120,6 +121,9 @@ static kthread_mutex_t tcp_lock = KTHREAD_MUTEX_INITIALIZER;
static TCPSocket** bindings_v4;
static TCPSocket** bindings_v6;
static TCPSocket* all_first_socket;
static TCPSocket* all_last_socket;
void Init()
{
if ( !(bindings_v4 = new TCPSocket*[65536]) ||
@ -218,6 +222,7 @@ public:
int getsockname(ioctx_t* ctx, uint8_t* addr, size_t* addrsize);
public:
size_t Describe(char* buf, size_t buflen);
void Unreference();
void ProcessPacket(Ref<Packet> pkt, union tcp_sockaddr* pkt_src,
union tcp_sockaddr* pkt_dst);
@ -278,6 +283,12 @@ public:
// The listening socket this socket is in the listening queue for.
TCPSocket* connecting_parent;
// DEBUG: The previous socket of all sockets.
TCPSocket* all_prev_socket;
// DEBUG: The next socket of all sockets.
TCPSocket* all_next_socket;
// Condition variable that is signaled when new data can be received.
kthread_cond_t receive_cond;
@ -461,7 +472,7 @@ void TCPSocket__OnTimer(Clock* /*clock*/, Timer* /*timer*/, void* user)
((TCPSocket*) user)->OnTimer();
}
TCPSocket::TCPSocket(int af)
TCPSocket::TCPSocket(int af) // DEBUG: tcp_lock taken
{
prev_socket = NULL;
next_socket = NULL;
@ -517,9 +528,15 @@ TCPSocket::TCPSocket(int af)
shutdown_receive = false;
memset(incoming, 0, sizeof(incoming));
memset(outgoing, 0, sizeof(outgoing));
// DEBUG
all_prev_socket = all_last_socket;
all_next_socket = NULL;
(all_last_socket ?
all_last_socket->all_next_socket : all_first_socket) = this;
all_last_socket = this;
}
TCPSocket::~TCPSocket()
TCPSocket::~TCPSocket() // DEBUG: tcp_lock taken
{
assert(state == TCP_STATE_CLOSED);
assert(!bound);
@ -539,6 +556,52 @@ TCPSocket::~TCPSocket()
receive_queue = packet->next;
packet->next.Reset();
}
// DEBUG
(all_prev_socket ?
all_prev_socket->all_next_socket : all_first_socket) = all_next_socket;
(all_next_socket ?
all_next_socket->all_prev_socket : all_last_socket) = all_prev_socket;
all_prev_socket = NULL;
all_next_socket = NULL;
}
// DEBUG
size_t TCPSocket::Describe(char* buf, size_t buflen) // tcp_lock taken
{
const char* const STATE_NAMES[] =
{
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RECV",
"ESTAB",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT",
};
const char* state_name = STATE_NAMES[state];
char local_str[INET_ADDRSTRLEN];
char remote_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &local.in.sin_addr, local_str, sizeof(local_str));
inet_ntop(AF_INET, &remote.in.sin_addr, remote_str, sizeof(remote_str));
char timeout[64] = "none";
if ( timer_armed )
{
struct itimerspec its;
timer.Get(&its);
snprintf(timeout, sizeof(timeout), "%ji.%09li",
(intmax_t) its.it_value.tv_sec, its.it_value.tv_nsec);
}
return snprintf(buf, buflen,
"%s %s %u -> %s %u"
" timeout=%s resends=%u sockerr=%i transmit=%i refed=%i\n",
state_name, local_str, be16toh(local.in.sin_port),
remote_str, be16toh(remote.in.sin_port), timeout,
retransmissions, sockerr, transmit_scheduled,
is_referenced);
}
void TCPSocket::Unreference()
@ -2558,6 +2621,7 @@ Ref<Inode> Socket(int af)
{
if ( !IsSupportedAddressFamily(af) )
return errno = EAFNOSUPPORT, Ref<Inode>(NULL);
ScopedLock lock(&tcp_lock); // DEBUG
TCPSocket* socket = new TCPSocket(af);
if ( !socket )
return Ref<Inode>();
@ -2567,5 +2631,45 @@ Ref<Inode> Socket(int af)
return result;
}
// DEBUG
ssize_t Info(char* user_resp, size_t resplen)
{
ScopedLock lock(&tcp_lock); // DEBUG
bool exhausted = false;
size_t total_needed = 0;
for ( TCPSocket* socket = all_first_socket;
socket;
socket = socket->all_next_socket )
{
char str[256];
size_t stringlen = socket->Describe(str, sizeof(str));
if ( !socket->all_next_socket && stringlen )
stringlen--;
total_needed += stringlen;
if ( exhausted )
continue;
if ( resplen < stringlen )
{
exhausted = true;
continue;
}
if ( !CopyToUser(user_resp, str, sizeof(char) * stringlen) )
return -1;
user_resp += stringlen;
resplen -= stringlen;
}
if ( !exhausted && !resplen )
exhausted = true;
if ( !exhausted )
{
char zero = '\0';
if ( !CopyToUser(user_resp, &zero, 1) )
return -1;
}
if ( exhausted )
return errno = ERANGE, (ssize_t) total_needed;
return 0;
}
} // namespace TCP
} // namespace Sortix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2016, 2017, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -34,6 +34,7 @@ void HandleIP(Ref<Packet> pkt,
const struct in_addr* dst,
bool dst_broadcast);
Ref<Inode> Socket(int af);
ssize_t Info(char* user_resp, size_t resplen);
} // namespace TCP
} // namespace Sortix

View File

@ -24,6 +24,19 @@
#warning "security: -fcheck-new might not work on clang"
#endif
#ifdef __TRACE_ALLOCATION_SITES
#undef new
void* operator new(size_t size, struct __allocation_site* allocation_site)
{
return malloc_trace(allocation_site, size);
}
void* operator new[](size_t size, struct __allocation_site* allocation_site)
{
return malloc_trace(allocation_site, size);
}
#else
void* operator new(size_t size)
{
return malloc(size);
@ -33,6 +46,7 @@ void* operator new[](size_t size)
{
return malloc(size);
}
#endif
void operator delete(void* addr)
{

View File

@ -477,6 +477,8 @@ private:
size_t output_offset;
size_t output_used;
static const size_t output_size = 4096;
// TODO: This is not safe because ^W can produce unbounded output.
static const size_t output_probably_safe = 2048;
uint8_t output[output_size];
int ptynum;
@ -538,6 +540,16 @@ ssize_t PTY::master_write(ioctx_t* ctx, const uint8_t* buf, size_t count)
ScopedLockSignal lock(&termlock);
if ( !lock.IsAcquired() )
return errno = EINTR, -1;
// TODO: Work around deadlock by refusing writes when the buffer is starting
// to be too full. This can block / "deadlock" too if the caller
// didn't try to read the data written by a previous call.
while ( output_probably_safe <= output_used )
{
if ( ctx->dflags & O_NONBLOCK )
return errno = EWOULDBLOCK, -1;
if ( !kthread_cond_wait_signal(&output_ready_cond, &termlock) )
return errno = EINTR, -1;
}
size_t sofar = 0;
while ( sofar < count )
{
@ -550,9 +562,13 @@ ssize_t PTY::master_write(ioctx_t* ctx, const uint8_t* buf, size_t count)
{
if ( Signal::IsPending() )
return sofar ? (ssize_t) sofar : (errno = EINTR, -1);
// TODO: Unfortunately sequences like ^W can cause an unbounded
// number of tty_output data causing a deadlock.
ProcessByte(input[i]);
sofar++;
if ( output_probably_safe <= output_used )
return (ssize_t) sofar;
}
sofar += amount;
}
return (ssize_t) sofar;
}
@ -599,7 +615,7 @@ short PTY::PollMasterEventStatus()
short status = 0;
if ( output_used )
status |= POLLIN | POLLRDNORM;
if ( true /* can always write */ )
if ( output_used < output_probably_safe )
status |= POLLOUT | POLLWRNORM;
return status;
}

View File

@ -78,9 +78,7 @@ static const uint8_t DEVICE_CMD_RESET = 0xFF;
static const size_t DEVICE_RETRIES = 5;
// TODO: This is entirely a guess. I don't actually know what timeout is
// suitable. GRUB seems to use 20 ms. I'll pick 50 ms to be safe.
static const unsigned int TIMEOUT_MS = 50;
static const unsigned int TIMEOUT_MS = 20;
static bool WaitInput()
{

View File

@ -60,20 +60,6 @@ malloc/__heap_verify.o \
netinet/if_ether/etheraddr_broadcast.o \
netinet/in/in6addr_any.o \
netinet/in/in6addr_loopback.o \
regex/regcomp.o \
regex/regerror.o \
regex/regexec.o \
regex/regfree.o \
sha2/sha224hl.o \
sha2/sha224.o \
sha2/sha256hl.o \
sha2/sha256.o \
sha2/sha384hl.o \
sha2/sha384.o \
sha2/sha512_256hl.o \
sha2/sha512_256.o \
sha2/sha512hl.o \
sha2/sha512.o \
signal/sigaddset.o \
signal/sigandset.o \
signal/sigdelset.o \
@ -86,7 +72,6 @@ signal/sigorset.o \
ssp/__stack_chk_fail.o \
stdio/asprintf.o \
stdio/cbprintf.o \
stdio/cbscanf.o \
stdio/clearerr.o \
stdio/clearerr_unlocked.o \
stdio/dprintf.o \
@ -116,8 +101,6 @@ stdio/fgets.o \
stdio/fgets_unlocked.o \
stdio/fileno_unlocked.o \
stdio/flockfile.o \
stdio/fmemopen.o \
stdio/fnewfile.o \
stdio/fparsemode.o \
stdio/fprintf_unlocked.o \
stdio/fputc.o \
@ -128,8 +111,6 @@ stdio/fread.o \
stdio/fread_unlocked.o \
stdio/fregister.o \
stdio/fresetfile.o \
stdio/fscanf.o \
stdio/fscanf_unlocked.o \
stdio/fseek.o \
stdio/fseeko.o \
stdio/fseeko_unlocked.o \
@ -142,28 +123,20 @@ stdio/funlockfile.o \
stdio/funregister.o \
stdio/fwrite.o \
stdio/fwrite_unlocked.o \
stdio/getdelim.o \
stdio/getline.o \
stdio/open_memstream.o \
stdio/rewind.o \
stdio/setbuf.o \
stdio/setvbuf.o \
stdio/setvbuf_unlocked.o \
stdio/snprintf.o \
stdio/sprintf.o \
stdio/sscanf.o \
stdio/ungetc.o \
stdio/ungetc_unlocked.o \
stdio/vasprintf.o \
stdio/vcbprintf.o \
stdio/vdprintf.o \
stdio/vfprintf_unlocked.o \
stdio/vfscanf.o \
stdio/vfscanf_unlocked.o \
stdio/vcbscanf.o \
stdio/vsnprintf.o \
stdio/vsprintf.o \
stdio/vsscanf.o \
stdlib/abort.o \
stdlib/abs.o \
stdlib/arc4random_buf.o \
@ -278,7 +251,6 @@ wchar/wcscmp.o \
wchar/wcscoll.o \
wchar/wcscpy.o \
wchar/wcscspn.o \
wchar/wcsdup.o \
wchar/wcsftime.o \
wchar/wcslcat.o \
wchar/wcslcpy.o \
@ -329,6 +301,34 @@ wctype/towupper.o \
wctype/wctype.o \
HOSTEDOBJS=\
regex/regcomp.o \
regex/regerror.o \
regex/regexec.o \
regex/regfree.o \
sha2/sha224hl.o \
sha2/sha224.o \
sha2/sha256hl.o \
sha2/sha256.o \
sha2/sha384hl.o \
sha2/sha384.o \
sha2/sha512_256hl.o \
sha2/sha512_256.o \
sha2/sha512hl.o \
sha2/sha512.o \
stdio/cbscanf.o \
stdio/fscanf.o \
stdio/fscanf_unlocked.o \
stdio/sscanf.o \
stdio/vfscanf.o \
stdio/vfscanf_unlocked.o \
stdio/vcbscanf.o \
stdio/vsscanf.o \
stdio/fmemopen.o \
stdio/open_memstream.o \
stdio/getdelim.o \
stdio/getline.o \
stdio/fnewfile.o \
wchar/wcsdup.o \
blf/blowfish.o \
$(CPUDIR)/fork.o \
$(CPUDIR)/setjmp.o \

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -117,6 +117,16 @@ struct heap_alloc
#warning "You need to implement HEAP_CHUNK_MAGIC for your native word width"
#endif
#ifdef __TRACE_ALLOCATION_SITES
#define MAGIC_IS_ALLOCATION_SITE(magic)( (((size_t) (magic)) & 0x3) == 0x2)
#define ALLOCATION_SITE_OF_MAGIC(magic) ((struct __allocation_site*) ((magic) & ~0x3UL))
#define MAGIC_OF_ALLOCATION_SITE(magic) (((size_t) (magic)) | 0x2)
#else
#define MAGIC_IS_ALLOCATION_SITE(magic) 0
#define ALLOCATION_SITE_OF_MAGIC(magic) NULL
#define MAGIC_OF_ALLOCATION_SITE(magic) NULL
#endif
/* The heap is split into a number of parts that each consists of a number of
of chunks (used and unused). The heap normally is just a single part, but if
the address space gets too fragmented, it may not be possible to extend the
@ -346,7 +356,8 @@ bool heap_chunk_is_used(struct heap_chunk* chunk)
{
assert(HEAP_IS_POINTER_ALIGNED(chunk, chunk->chunk_size));
return chunk->chunk_magic == HEAP_CHUNK_MAGIC;
return chunk->chunk_magic == HEAP_CHUNK_MAGIC ||
MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic);
}
/* Returns the trailing structure following the given chunk. */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -242,8 +242,14 @@ int vdprintf(int fildes, const char* __restrict format, __gnuc_va_list ap)
/* Functions copied from elsewhere. */
#if __USE_SORTIX
#ifdef __TRACE_ALLOCATION_SITES
int asprintf_trace(struct __allocation_site*, char** __restrict, const char* __restrict, ...)
__attribute__((__format__ (printf, 3, 4)));
#define asprintf(a, ...) asprintf_trace(ALLOCATION_SITE, (a), __VA_ARGS__)
#else
int asprintf(char** __restrict, const char* __restrict, ...)
__attribute__((__format__ (printf, 2, 3)));
#endif
void clearerr_unlocked(FILE* stream);
int feof_unlocked(FILE* stream);
int ferror_unlocked(FILE* stream);
@ -255,9 +261,15 @@ int fputc_unlocked(int c, FILE* stream);
int fputs_unlocked(const char* __restrict, FILE* __restrict stream);
size_t fread_unlocked(void* __restrict ptr, size_t size, size_t nitems, FILE* __restrict stream);
size_t fwrite_unlocked(const void* __restrict ptr, size_t size, size_t nitems, FILE* __restrict stream);
#ifdef __TRACE_ALLOCATION_SITES
int vasprintf_trace(struct __allocation_site*, char** __restrict, const char* __restrict, __gnuc_va_list)
__attribute__((__format__ (printf, 3, 0)));
#define vasprintf(a, ...) vasprintf_trace(ALLOCATION_SITE, (a), __VA_ARGS__)
#else
int vasprintf(char** __restrict, const char* __restrict, __gnuc_va_list)
__attribute__((__format__ (printf, 2, 0)));
#endif
#endif
/* Functions that are Sortix extensions. */
#if __USE_SORTIX

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -88,7 +88,12 @@ double atof(const char* value);
int atoi(const char*);
long atol(const char*);
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
#ifdef __TRACE_ALLOCATION_SITES
void* calloc_trace(struct __allocation_site*, size_t, size_t);
#define calloc(a, b) calloc_trace(ALLOCATION_SITE, (a), (b))
#else
void* calloc(size_t, size_t);
#endif
char* canonicalize_file_name(const char* path);
char* canonicalize_file_name_at(int dirfd, const char* path);
int clearenv(void);
@ -99,7 +104,12 @@ void free(void*);
char* getenv(const char*);
long labs(long);
ldiv_t ldiv(long, long);
#ifdef __TRACE_ALLOCATION_SITES
void* malloc_trace(struct __allocation_site*, size_t);
#define malloc(a) malloc_trace(ALLOCATION_SITE, (a))
#else
void* malloc(size_t);
#endif
int mblen(const char*, size_t);
size_t mbstowcs(wchar_t* __restrict, const char* __restrict, size_t);
int mbtowc(wchar_t *__restrict, const char* __restrict, size_t);
@ -116,7 +126,12 @@ void qsort_r(void*, size_t, size_t, int (*)(const void*, const void*, void*), vo
__attribute__((__warning__("rand() isn't random, use arc4random()")))
#endif
int rand(void);
#ifdef __TRACE_ALLOCATION_SITES
void* realloc_trace(struct __allocation_site*, void*, size_t);
#define realloc(a, b) realloc_trace(ALLOCATION_SITE, (a), (b))
#else
void* realloc(void*, size_t);
#endif
char* realpath(const char* __restrict, char* __restrict);
int setenv(const char*, const char*, int);
#if !defined(__is_sortix_libc) /* not a warning inside libc */
@ -189,7 +204,12 @@ int posix_openpt(int);
uint32_t arc4random(void);
void arc4random_buf(void*, size_t);
uint32_t arc4random_uniform(uint32_t);
#ifdef __TRACE_ALLOCATION_SITES
void* reallocarray_trace(struct __allocation_site*, void*, size_t, size_t);
#define reallocarray(a, b, c) reallocarray_trace(ALLOCATION_SITE, (a), (b), (c))
#else
void* reallocarray(void*, size_t, size_t);
#endif
int ptsname_r(int, char*, size_t);
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2017 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2017, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -91,8 +91,13 @@ void* memccpy(void* __restrict, const void* __restrict, int, size_t);
/* Functions from XOPEN 420 gone into POSIX 2008. */
#if __USE_SORTIX || 420 <= __USE_XOPEN || 200809L <= __USE_POSIX
#ifdef __TRACE_ALLOCATION_SITES
char* strdup_trace(struct __allocation_site*, const char*);
#define strdup(a) strdup_trace(ALLOCATION_SITE, (a))
#else
char* strdup(const char*);
#endif
#endif
/* Functions from POSIX 2001. */
#if __USE_SORTIX || 200112L <= __USE_POSIX
@ -111,7 +116,12 @@ char* strtok_r(char* __restrict, const char* __restrict, char** __restrict);
char* stpcpy(char* __restrict, const char* __restrict);
char* stpncpy(char* __restrict, const char* __restrict, size_t);
int strcoll_l(const char*, const char*, locale_t);
#ifdef __TRACE_ALLOCATION_SITES
char* strndup_trace(struct __allocation_site*, const char*, size_t);
#define strndup(a, b) strndup_trace(ALLOCATION_SITE, (a), (b))
#else
char* strndup(const char*, size_t);
#endif
size_t strnlen(const char*, size_t);
#if __USE_SORTIX && __SORTIX_STDLIB_REDIRECTS
const char* strsignal(int signum) __asm__ ("sortix_strsignal");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -128,4 +128,24 @@
is updated to not rely on this macro. */
#undef __SORTIX_HAS_GETSERVBYNAME__
#if (defined(__is_sortix_libk) || defined(__is_sortix_kernel)) && \
defined(__TRACE_KMALLOC)
#undef __TRACE_ALLOCATION_SITES
#define __TRACE_ALLOCATION_SITES
#endif
#ifdef __TRACE_ALLOCATION_SITES
#include <stddef.h>
struct __allocation_site
{
const char* file;
size_t line;
const char* function;
size_t current_size;
size_t allocations;
};
#define __TRACE_ALLOCATION_SITES
#define ALLOCATION_SITE 0
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,13 +20,24 @@
#include <stdarg.h>
#include <stdio.h>
#ifdef __TRACE_ALLOCATION_SITES
int asprintf_trace(struct __allocation_site* allocation_site,
char** restrict result_ptr,
const char* restrict format,
...)
#else
int asprintf(char** restrict result_ptr,
const char* restrict format,
...)
#endif
{
va_list list;
va_start(list, format);
#ifdef __TRACE_ALLOCATION_SITES
int result = vasprintf_trace(allocation_site, result_ptr, format, list);
#else
int result = vasprintf(result_ptr, format, list);
#endif
va_end(list);
return result;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -27,6 +27,9 @@ struct vasprintf
char* buffer;
size_t used;
size_t size;
#ifdef __TRACE_ALLOCATION_SITES
struct __allocation_site* allocation_site;
#endif
};
static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
@ -39,7 +42,12 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
size_t new_size = 2 * state->size;
if ( new_size < needed_size )
new_size = needed_size;
#ifdef __TRACE_ALLOCATION_SITES
char* new_buffer = (char*) realloc_trace(state->allocation_site,
state->buffer, new_size);
#else
char* new_buffer = (char*) realloc(state->buffer, new_size);
#endif
if ( !new_buffer )
{
free(state->buffer);
@ -54,14 +62,26 @@ static size_t vasprintf_callback(void* ctx, const char* string, size_t length)
return length;
}
#ifdef __TRACE_ALLOCATION_SITES
int vasprintf_trace(struct __allocation_site* allocation_site,
char** restrict result_ptr,
const char* restrict format,
va_list list)
#else
int vasprintf(char** restrict result_ptr,
const char* restrict format,
va_list list)
#endif
{
struct vasprintf state;
state.used = 0;
state.size = 32;
#ifdef __TRACE_ALLOCATION_SITES
state.allocation_site = allocation_site;
if ( !(state.buffer = (char*) malloc_trace(allocation_site, state.size)) )
#else
if ( !(state.buffer = (char*) malloc(state.size)) )
#endif
return -1;
int result = vcbprintf(&state, vasprintf_callback, format, list);
if ( !state.buffer )

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -22,12 +22,21 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
void* calloc_trace(struct __allocation_site* allocation_site,
size_t nmemb, size_t size)
#else
void* calloc(size_t nmemb, size_t size)
#endif
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
size_t total = nmemb * size;
#ifdef __TRACE_ALLOCATION_SITES
void* result = malloc_trace(allocation_site, total);
#else
void* result = malloc(total);
#endif
if ( !result )
return NULL;
memset(result, 0, total);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -39,6 +39,16 @@ void free(void* addr)
// Retrieve the chunk that contains this allocation.
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) addr);
#ifdef __TRACE_ALLOCATION_SITES
if ( MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic) )
{
struct __allocation_site* allocation_site =
ALLOCATION_SITE_OF_MAGIC(chunk->chunk_magic);
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
}
#endif
// Return the chunk to the heap.
heap_insert_chunk(chunk);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -31,7 +31,12 @@
#if !defined(HEAP_GUARD_DEBUG)
#ifdef __TRACE_ALLOCATION_SITES
void* malloc_trace(struct __allocation_site* allocation_site,
size_t original_size)
#else
void* malloc(size_t original_size)
#endif
{
if ( !heap_size_has_bin(original_size) )
return errno = ENOMEM, (void*) NULL;
@ -89,6 +94,12 @@ void* malloc(size_t original_size)
if ( heap_can_split_chunk(result_chunk, chunk_size) )
heap_split_chunk(result_chunk, chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += result_chunk->chunk_size;
allocation_site->allocations++;
result_chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -31,10 +31,20 @@
#if !defined(HEAP_GUARD_DEBUG)
#ifdef __TRACE_ALLOCATION_SITES
void* realloc_trace(struct __allocation_site* new_allocation_site,
void* ptr,
size_t requested_size)
#else
void* realloc(void* ptr, size_t requested_size)
#endif
{
if ( !ptr )
#ifdef __TRACE_ALLOCATION_SITES
return malloc_trace(new_allocation_site, requested_size);
#else
return malloc(requested_size);
#endif
if ( !heap_size_has_bin(requested_size) )
return errno = ENOMEM, (void*) NULL;
@ -55,7 +65,14 @@ void* realloc(void* ptr, size_t requested_size)
// Retrieve the chunk that contains this allocation.
struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) ptr);
assert(chunk->chunk_magic == HEAP_CHUNK_MAGIC);
#ifdef __TRACE_ALLOCATION_SITES
assert(MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic));
struct __allocation_site* allocation_site =
ALLOCATION_SITE_OF_MAGIC(chunk->chunk_magic);
#endif
assert(chunk->chunk_magic == HEAP_CHUNK_MAGIC ||
MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic));
assert(heap_chunk_to_post(chunk)->chunk_magic == HEAP_CHUNK_MAGIC);
assert(heap_chunk_to_post(chunk)->chunk_size == chunk->chunk_size);
@ -72,8 +89,17 @@ void* realloc(void* ptr, size_t requested_size)
if ( requested_chunk_size < chunk->chunk_size )
{
assert(requested_chunk_size <= chunk->chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
#endif
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
heap_split_chunk(chunk, requested_chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += chunk->chunk_size;
allocation_site->allocations++;
chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();
return heap_chunk_to_data(chunk);
@ -88,11 +114,20 @@ void* realloc(void* ptr, size_t requested_size)
!heap_chunk_is_used(right) &&
requested_chunk_size <= chunk->chunk_size + right->chunk_size )
{
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size -= chunk->chunk_size;
allocation_site->allocations--;
#endif
heap_remove_chunk(right);
heap_chunk_format((uint8_t*) chunk, chunk->chunk_size + right->chunk_size);
assert(requested_chunk_size <= chunk->chunk_size);
if ( heap_can_split_chunk(chunk, requested_chunk_size) )
heap_split_chunk(chunk, requested_chunk_size);
#ifdef __TRACE_ALLOCATION_SITES
allocation_site->current_size += chunk->chunk_size;
allocation_site->allocations++;
chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site);
#endif
__heap_verify();
__heap_unlock();
return heap_chunk_to_data(chunk);
@ -108,7 +143,11 @@ void* realloc(void* ptr, size_t requested_size)
assert(orignal_ptr_size < requested_size);
#ifdef __TRACE_ALLOCATION_SITES
void* result = malloc_trace(allocation_site, requested_size);
#else
void* result = malloc(requested_size);
#endif
if ( !result )
return (void*) NULL;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -21,9 +21,18 @@
#include <stdint.h>
#include <stdlib.h>
#ifdef __TRACE_ALLOCATION_SITES
void* reallocarray_trace(struct __allocation_site* allocation_site,
void* ptr, size_t nmemb, size_t size)
#else
void* reallocarray(void* ptr, size_t nmemb, size_t size)
#endif
{
if ( size && nmemb && SIZE_MAX / size < nmemb )
return errno = ENOMEM, (void*) NULL;
#ifdef __TRACE_ALLOCATION_SITES
return realloc_trace(allocation_site, ptr, nmemb * size);
#else
return realloc(ptr, nmemb * size);
#endif
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,10 +20,18 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
char* strdup_trace(struct __allocation_site* allocation_site, const char* input)
#else
char* strdup(const char* input)
#endif
{
size_t input_length = strlen(input);
#ifdef __TRACE_ALLOCATION_SITES
char* result = (char*) malloc_trace(allocation_site, input_length + 1);
#else
char* result = (char*) malloc(input_length + 1);
#endif
if ( !result )
return NULL;
memcpy(result, input, input_length + 1);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2011, 2012, 2014, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,10 +20,19 @@
#include <stdlib.h>
#include <string.h>
#ifdef __TRACE_ALLOCATION_SITES
char* strndup_trace(struct __allocation_site* allocation_site,
const char* input, size_t n)
#else
char* strndup(const char* input, size_t n)
#endif
{
size_t input_size = strnlen(input, n);
#ifdef __TRACE_ALLOCATION_SITES
char* result = (char*) malloc_trace(allocation_site, input_size + 1);
#else
char* result = (char*) malloc(input_size + 1);
#endif
if ( !result )
return NULL;
memcpy(result, input, input_size);

View File

@ -20,6 +20,7 @@ extended.o \
filesystem.o \
gpt.o \
harddisk.o \
iso9660.o \
mbr.o \
partition.o \
uuid.o \

View File

@ -32,6 +32,7 @@
#include <mount/ext2.h>
#include <mount/extended.h>
#include <mount/filesystem.h>
#include <mount/iso9660.h>
#include <mount/partition.h>
const char* filesystem_error_string(enum filesystem_error error)
@ -55,6 +56,7 @@ static const struct filesystem_handler* filesystem_handlers[] =
&biosboot_handler,
&extended_handler,
&ext2_handler,
&iso9660_handler,
NULL,
};

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* mount/iso9660.h
* ISO 9660 filesystem.
*/
#ifndef INCLUDE_MOUNT_ISO9660_H
#define INCLUDE_MOUNT_ISO9660_H
#include <stdint.h>
#include <mount/filesystem.h>
#if defined(__cplusplus)
extern "C" {
#endif
extern const struct filesystem_handler iso9660_handler;
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

98
libmount/iso9660.c Normal file
View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* iso9660.c
* iso9660 filesystem.
*/
#include <endian.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <mount/blockdevice.h>
#include <mount/filesystem.h>
#include <mount/iso9660.h>
#include <mount/uuid.h>
#include "util.h"
static size_t iso9660_probe_amount(struct blockdevice* bdev)
{
(void) bdev;
size_t block_size = 2048 /* TODO: Actual block size? */;
return 17 * block_size;
}
static bool iso9660_probe(struct blockdevice* bdev,
const unsigned char* leading,
size_t amount)
{
(void) bdev;
size_t block_size = 2048 /* TODO: Actual block size? */;
if ( amount < 17 * block_size )
return false;
size_t offset = 16 * block_size;
return !memcmp(leading + offset + 1, "CD001", 5);
}
static void iso9660_release(struct filesystem* fs)
{
if ( !fs )
return;
free(fs);
}
static enum filesystem_error iso9660_inspect(struct filesystem** fs_ptr,
struct blockdevice* bdev)
{
*fs_ptr = NULL;
struct filesystem* fs = CALLOC_TYPE(struct filesystem);
if ( !fs )
return FILESYSTEM_ERROR_ERRNO;
fs->bdev = bdev;
fs->handler = &iso9660_handler;
fs->handler_private = NULL;
fs->fstype_name = "iso9660";
fs->driver = "iso9660fs";
size_t block_size = 2048 /* TODO: Actual block size? */;
size_t offset = 16 * block_size;
uint8_t pvd[2048];
if ( blockdevice_preadall(bdev, pvd, sizeof(pvd), offset) != sizeof(pvd) )
return iso9660_release(fs), FILESYSTEM_ERROR_ERRNO;
// TODO: LABEL support using the volume name.
char vol_set[128 + 1];
memcpy(vol_set, pvd + 190, 128);
vol_set[128] = '\0';
for ( size_t i = 128; i && vol_set[i-1] == ' '; i-- )
vol_set[i-1] = '\0';
if ( uuid_validate(vol_set) )
{
fs->flags |= FILESYSTEM_FLAG_UUID;
uuid_from_string(fs->uuid, vol_set);
}
return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
}
const struct filesystem_handler iso9660_handler =
{
.handler_name = "iso9660",
.probe_amount = iso9660_probe_amount,
.probe = iso9660_probe,
.inspect = iso9660_inspect,
.release = iso9660_release,
};

View File

@ -163,6 +163,9 @@ bool blockdevice_probe_partition_table_type(enum partition_table_type* result_ou
memcpy(&mbr, leading, sizeof(mbr));
if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
break;
// Ignore the partition table on live cdrom images.
if ( mbr.partitions[0][4 /*system_id*/] == 0xCD )
break;
result = PARTITION_TABLE_TYPE_MBR;
goto out;
} while ( 0 );

2
nyan/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
nyan
*.o

33
nyan/Makefile Normal file
View File

@ -0,0 +1,33 @@
SOFTWARE_MEANT_FOR_SORTIX=1
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/dirs.mak
OPTLEVEL?=-g -O2
CFLAGS?=$(OPTLEVEL)
CFLAGS:=$(CFLAGS) -Wall -Wextra
PROGRAM=nyan
OBJS=\
nyan.o \
LIBS:=-lui -ldisplay
all: $(PROGRAM)
.PHONY: all install clean
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(PROGRAM) $(DESTDIR)$(BINDIR)
$(PROGRAM): $(OBJS)
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LIBS)
%.o: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) -c $< -o $@
clean:
rm -f $(PROGRAM) *.o

181
nyan/nyan.c Normal file
View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* nyan.c
* Window with animated nyancat.
*/
#include <sys/socket.h>
#include <sys/keycodes.h>
#include <sys/un.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <error.h>
#include <ioleast.h>
#include <locale.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <timespec.h>
#include <unistd.h>
#include <display.h>
#include "nyan.h"
#include "pixel.h"
uint32_t WINDOW_ID = 0;
uint32_t WINDOW_WIDTH = 0;
uint32_t WINDOW_HEIGHT = 0;
bool need_redraw = true;
bool need_show = true;
bool need_exit = false;
void on_disconnect(void* ctx)
{
(void) ctx;
need_exit = true;
}
void on_quit(void* ctx, uint32_t window_id)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
need_exit = true;
}
void on_resize(void* ctx, uint32_t window_id, uint32_t width, uint32_t height)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
need_redraw = true;
WINDOW_WIDTH = width;
WINDOW_HEIGHT = height;
}
void on_keyboard(void* ctx, uint32_t window_id, uint32_t codepoint)
{
(void) ctx;
if ( window_id != WINDOW_ID )
return;
(void) codepoint;
}
int main(int argc, char* argv[])
{
(void) argc;
(void) argv;
setlocale(LC_ALL, "");
setvbuf(stdout, NULL, _IOLBF, 0);
struct display_connection* connection = display_connect_default();
if ( !connection && errno == ECONNREFUSED )
display_spawn(argc, argv);
if ( !connection )
error(1, errno, "Could not connect to display server");
WINDOW_WIDTH = 600;
WINDOW_HEIGHT = 600;
display_create_window(connection, WINDOW_ID);
display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
display_title_window(connection, WINDOW_ID, "Nyanyanyanyanyanyanya...");
struct timespec frame_duration = timespec_make(0, 90 * 1000 * 1000);
struct timespec last_frame;
clock_gettime(CLOCK_MONOTONIC, &last_frame);
int frame_num = 0;
while ( !need_exit )
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec since_last_frame = timespec_sub(now, last_frame);
if ( !need_redraw && timespec_lt(since_last_frame, frame_duration) )
{
struct timespec remainder = timespec_sub(frame_duration, since_last_frame);
if ( timespec_lt(remainder, timespec_make(0, 10 * 1000 * 1000)) )
remainder = timespec_make(0, 10 * 1000 * 1000);
nanosleep(&remainder, NULL);
continue;
}
while ( timespec_le(frame_duration, since_last_frame) )
{
if ( !nyan_frames[++frame_num] )
frame_num = 0;
need_redraw = true;
since_last_frame = timespec_sub(since_last_frame, frame_duration);
}
if ( need_redraw )
{
last_frame = now;
uint32_t* framebuffer = (uint32_t*) malloc(sizeof(uint32_t) * WINDOW_WIDTH * WINDOW_HEIGHT);
const char* const* frame = nyan_frames[frame_num];
for ( size_t y = 0; y < WINDOW_HEIGHT; y++ )
{
int yi = y * NYAN_FRAME_HEIGHT / WINDOW_HEIGHT;
const char* line = frame[yi];
for ( size_t x = 0; x < WINDOW_WIDTH; x++ )
{
int xi = x * NYAN_FRAME_WIDTH / WINDOW_WIDTH;
char elem = line[xi];
const uint8_t* cc = nyan_palette[nyan_palette_of_char(elem)];
framebuffer[y * WINDOW_WIDTH + x] = make_color_a(cc[0], cc[1], cc[2], cc[3]);;
}
}
display_render_window(connection, WINDOW_ID, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, framebuffer);
free(framebuffer);
need_redraw = false;
}
if ( need_show )
{
display_show_window(connection, WINDOW_ID);
need_show = false;
}
struct display_event_handlers handlers;
memset(&handlers, 0, sizeof(handlers));
handlers.disconnect_handler = on_disconnect;
handlers.quit_handler = on_quit;
handlers.resize_handler = on_resize;
handlers.keyboard_handler = on_keyboard;
while ( display_poll_event(connection, &handlers) == 0 );
}
display_disconnect(connection);
return 0;
}

859
nyan/nyan.h Normal file
View File

@ -0,0 +1,859 @@
/*
* Pop Tart Cat animation frames
* Adapted from ToAruOS.
*/
#ifndef INCLUDE_NYAN_H
#define INCLUDE_NYAN_H
#include <stdint.h>
static const char* const nyan_frame0[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&'''++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++**''+'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'**'''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"+++#######++++++++''**''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"####################''''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"###=======########====''@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
"===;;;;;;;.=======;;;;'''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'***''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'**'','*',,,,,'*','**',,,,,,,,,,,,,,,,,,,,,",
";;;,,,,.,,;;;.;;;;,,,'''',,'',,,,,,,'',,'',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame1[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"+++#######++++++++'*''''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################****'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###################''**'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"###=======########==='''@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,,,,,,,,,,,,,,,,,",
";;;,,.,,,,;;;;;;;;,,,,''',,,'',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,..,,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame2[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,..,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"##+++++++########++++++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"######################''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################'''''@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"==#######========#'****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==================='''='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
";;=======;;;;;;;;======'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";.;;;;;;;;;;;;;;;;;;;;;'*'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
".,.;;;;;;,,,,,,,,;;;;;;'**',**',,,,,,**','**',,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame3[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"##+++++++########++++++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"#####################'''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"==#######========##****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"=================='*'=='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=='==='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;;'**','*',,,,,,'*','**',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame4[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'''++'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"+++#######+++++++'**''''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"#################'****''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"##################''''*'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"###=======########==='''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;''@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;''''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***'''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,'',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,..,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame5[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$-$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"+++#######++++++++'*''''@$$$$$$$'*************',,,,,,,,,,,,,,,,,",
"###################****'@$$$$$$-'***.'****.'**',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"###=======########==='''@$$$$$$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$$'***''''''**',,,,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;''@@@$$$$$$$'*********',,,,,,,,,,,,,,,,,,.",
";;;;;;;;;;;;;;;;;;;;;'*''@@@@@@@@@@''''''''',,,,,,,,,,,,,,,,,,,.",
";;;;;;;;;;;;;;;;;;;;'***''''''''''''''''*',,,,,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,'**','**,,,,,,'**,'**',,,,,,,,,,,,,,,,,,..,.",
",,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame6[] = {
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,..,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&'''&&'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*''+'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'**'''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"##+++++++########++'**''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"####################''''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"==#######========#####''@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;====='''@@@@@@@@@'*********',,,,,,,,,,,.,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;'***''''''''''''''''''',,,,,,,,,,.,,,.,,,,,",
";;;;;;;;;;;;;;;;;;;;;'**'','*',,,,,'**,'**',,,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;'''',,'',,,,,,,'',,'',,,,,,,,,,,.,,,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame7[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,..,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"##+++++++########+'*''''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################****'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###################''**'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==#######========####'''@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"======================='@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;======''@@@@@@@@@@'*********',,,.,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**','*',,,,,,**','**',,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;;;''',,,'',,,,,,''',,''',,.,,,,.,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame8[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,..,...,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"+++#######++++++++#####'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"######################''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################'''''@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###=======########'****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"==================='''='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"===;;;;;;;========;;;;;'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;'*'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,,,,'**',**',,,,,,**'.'**',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,"};
static const char* const nyan_frame9[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,.,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,.,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,>>>>>>>,,,,,,,,>>>>>>>''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$$''-$$@',,'',,,,,,,,,,,,,,,,,,",
"&&&+++++++&&&&&&&&+++++'@$$$$$$$$$'**'$$@','**',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$$'***$$@''***',,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,",
"+++#######++++++++#####'@$$$$$$$$$'***********',,,,,,,,,,,,,,,,,",
"#####################'''@$$$$$$-$'*************',,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$$'***.'****.'**',,,,,,,,,,,,,,,,",
"###=======########=****'@$$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,",
"=================='*'=='@@$$$-$$$'*%%********%%',,,,,,,,,,,,,,,,",
"===;;;;;;;========;';;;'@@@$$$$$$$'***''''''**',,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;;''@@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;;'**'''''''''''''''''''',,,,,,,,,,,,,,,,,,,",
";;;,,,,,,,;;;;;;;;,,,,'**','*',,..,.**','**',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,''',,,'',,,,.,''',,''',,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,"};
static const char* const nyan_frame10[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,.,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,.,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$-$$$$@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$$''$-$$@','',,,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$'**'$$$@''**',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++++++'@$$$$$-$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'''++'@$$$$$$$$'***''''****',,,,,,,,,,,,,,,,,,",
"##+++++++########'**''''@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"#################'****''@$$$$$$-'*************',,,,,,,,,,,,,,,,,",
"##################''''*'@$-$$$$$'***.'****.'**',,,,,,,,,,,,,,,,,",
"==#######========####'''@$$$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$'*%%********%%',,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=====''@@@$$$$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;;''''@@@@@@@@@'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***'''''''''''''''''''',,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,,;;;'**'.'**..,,,,'**''**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,,'',,,,,,,''',''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
".,.,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const nyan_frame11[] = {
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
">>,,,,,,,>>>>>>>>,,,,,,,,''''''''''''''',,,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>>'@@@@@@@@@@@@@@@',,,,,,,,,,,,,,,,,,,,,,,",
">>>>>>>>>>>>>>>>>>>>>>>'@@@$$$$$$$$$$$@@@',,,,,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@@$$$$$-$$''$$$@@','',,,,,,,,,,,,,,,,,,,",
"&&&&&&&&&&&&&&&&&&&&&&&'@$$-$$$$$'**'-$$@''**',,,,,,,,,,,,,,,,,,",
"++&&&&&&&++++++++&&&&&&'@$$$$$$$$'***$$$@'***',,,,,,,,,,,,,,,,,,",
"+++++++++++++++++++'+++'@$$$$$-$$'***''''****',,,,,,,,,,,,,,,,,,",
"++++++++++++++++++'*'++'@$$$$$$$$'***********',,,,,,,,,,,,,,,,,,",
"##+++++++########+'*''''@$$$$$$$'*************',,,,,,,,,,,,,,,,,",
"###################****'@$$$$$$-'***.'****.'**',,,,,,,,,,,,,,,,,",
"###################''**'@$-$$$$$'***''**'*''**',,,,,,,,,,,,,,,,,",
"==#######========####'''@$$$$$$$'*%%********%%',,,,,,,,,,,,,,,,,",
"======================='@@$$$-$$$'***''''''**',,,,,,,,,,,,,,,,,,",
";;=======;;;;;;;;=.===''@@@$$$$$$$'*********',,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;.'*''@@@@@@@@@@''''''''',,,,,,,,,,,,,,,,,,,,",
";;;;;;;;;;;;;;;;;;;;'***''''''''''''''''*',,,,,,,,,,,,,,,,,,,,,,",
",,;;;;;;;,,,,,,,.;;;'**','**,,,,,,'**''**',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,''',,''',,,,,,''',,''',,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",.,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,.,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,",
",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"};
static const char* const* const nyan_frames[] = {
nyan_frame0,
nyan_frame1,
nyan_frame2,
nyan_frame3,
nyan_frame4,
nyan_frame5,
nyan_frame6,
nyan_frame7,
nyan_frame8,
nyan_frame9,
nyan_frame10,
nyan_frame11,
NULL
};
#define NYAN_FRAME_WIDTH 64
#define NYAN_FRAME_HEIGHT 64
static inline uint8_t nyan_palette_of_char(char c)
{
if ( c == ',' ) return 1;
if ( c == '.' ) return 2;
if ( c == '\'') return 3;
if ( c == '@' ) return 4;
if ( c == '$' ) return 5;
if ( c == '-' ) return 6;
if ( c == '>' ) return 7;
if ( c == '&' ) return 8;
if ( c == '+' ) return 9;
if ( c == '#' ) return 10;
if ( c == '=' ) return 11;
if ( c == ';' ) return 12;
if ( c == '*' ) return 13;
if ( c == '%' ) return 14;
return 0;
}
static const uint8_t nyan_palette[1+14][4] =
{
{ 0, 0, 0, 0 }, // 0; Unused entry
{ 0, 0, 97, 200 }, // 1: , = Blue background
{ 255, 255, 255, 255 }, // 2: . = White stars
{ 46, 52, 54, 255 }, // 3: ' = Black border
{ 255, 255, 215, 255 }, // 4: @ = Tan poptart
{ 215, 135, 175, 255 }, // 5: $ = Pink potart
{ 215, 0, 135, 255 }, // 6: - = Red poptart
{ 239, 41, 41, 255 }, // 7: > = Red rainbow
{ 255, 95, 0, 255 }, // 8: & = Orange rainbow
{ 252, 233, 79, 255 }, // 9: + = Yellow rainbow
{ 138, 226, 52, 255 }, // 10: # = Green rainbow
{ 0, 135, 255, 255 }, // 11: = = Light blue rainbow
{ 0, 0, 175, 255 }, // 12: ; = Dark blue rainbow
{ 85, 87, 83, 255 }, // 13: * = Grey cat face
{ 85, 87, 83, 255 }, // 14: % = Pink cheeks
};
#endif

View File

@ -0,0 +1,15 @@
diff -Paur --no-dereference -- signify.upstream/Makefile signify/Makefile
--- signify.upstream/Makefile
+++ signify/Makefile
@@ -1,7 +1,7 @@
-include ../../build-aux/platform.mak
-include ../../build-aux/compiler.mak
-include ../../build-aux/version.mak
-include ../../build-aux/dirs.mak
+include ../../../build-aux/platform.mak
+include ../../../build-aux/compiler.mak
+include ../../../build-aux/version.mak
+include ../../../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)

View File

@ -0,0 +1,11 @@
NAME=signify
BUILD_LIBRARIES=
VERSION=2017-04-10
DISTNAME=$NAME-$VERSION
COMPRESSION=tar.xz
ARCHIVE=$DISTNAME.$COMPRESSION
SHA256SUM=0b423119a27a69d417bb67cd38ccfb03d2a7387706911ee04e5d3c3f50e3619b
UPSTREAM_SITE=https://pub.sortix.org/fork/signify
UPSTREAM_ARCHIVE=$ARCHIVE
BUILD_SYSTEM=makefile
VERSION_REGEX='([0-9]{4}-[0-9]{2}-[0-9]{2})'

View File

@ -22,6 +22,10 @@ as part of
.Xr installation 7
to match what was installed.
.Pp
The file also controls the actions of
.Xr tix 8
when upgrading releases and installing ports.
.Pp
Developers may wish to customize what happens to
.Pa /src
on a system upgrade.
@ -45,7 +49,32 @@ Lines are supposed to contain assignments to variables.
An assignment is the name of the variable, whitespace, an equal character,
whitespace, the value, whitespace, and then the end of the line.
.Bl -tag -width "12345678"
.It Sy grub Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
.It Sy channel Ns "=" Ns Ar channel
If the current release has an upgrade path named
.Ar channel
to a new release,
then system upgrades will upgrade to that new release.
If no such release path exists or if this variable is not set, upgrades will
continue to upgrade to the current release series.
Depending on the current release, the offically supported values are
.Sy stable
for stable releases and
.Sy nightly
for development releases.
Downgrading releases is not supported. For instance, if the current system is
a development release, specifying
.Sy stable
will not downgrade the system to the previous stable release, as no such upgrade
path exists.
Instead upgrades will upgrade to the next stable release when it becomes
available.
.It Sy force_mirror Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no )
Use the preferred mirror set with
.Sy mirror
even if the file specified by
.Sy release_sig_url
does not list it.
.It Sy grub Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no )
States GRUB is used as the bootloader.
If either the
.Sy system
@ -57,7 +86,18 @@ then the bootloader is reinstalled
.Xr ( grub-install 8 )
and updated
.Xr ( update-grub 8 ) .
.It Sy newsrc Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
.It Sy mirror Ns "=" Ns Ar mirror
Download releases and ports from this preferred
.Ar mirror ,
a URL to the top level directory of a mirror.
The mirror is only used if the file specified by
.Sy release_sig_url
lists this mirror, unless
.Sy force_mirror
is set to
.Sy yes .
If no mirror is set, a default mirror is used.
.It Sy newsrc Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no )
Place the new source code in
.Pa /newsrc
and move any existing
@ -69,17 +109,39 @@ This preserves the current
directory.
This takes precedence over and disables the behavior described under
.Sy src .
.It Sy ports Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes ) .
.It Sy ports Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes )
Install the new ports.
Ports that don't exist anymore will be removed.
.It Sy src Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
.It Sy release_key Ns "=" Ns Ar release_key
Verify the file specified by
.Sy release_sig_url
with the
.Xr signify 1
public key file at the path
.Ar release_key .
This variable is updated during system upgrades and there is no need to change
this variable manually.
.It Sy release_sig_url Ns "=" Ns Ar release_sig_url
Download the meta-information about the current release from the URL
.Ar release_sig_url .
This file is verified with the
.Xr signify 1
public key in the
.Sy release_key
variable.
The file describes the current release, provides checksums of all the published
files, lists all the supported mirrors, provides instructions on how to upgrade
to this release, and lists all the supported upgrade paths to new releases.
This variable is updated during system upgrades and there is no need to change
this variable manually.
.It Sy src Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no )
Place the new source code in
.Pa /src
and move any existing
.Pa /src
into
.Pa /oldsrc .
.It Sy system Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes ) .
.It Sy system Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes )
Install the new system.
The upgrade hooks are run if needed as described in
.Xr following-development 7 .
@ -96,6 +158,18 @@ then regenerate
The defaults will be used if
.Pa /etc/upgrade.conf
is missing.
If
.Sy release_key
or
.Sy release_sig_url
are absent,
.Xr tix 8
will not be able to upgrade the current system nor install ports.
If
.Sy channel
is absent,
.Xr tix 8
will not upgrade to new releases.
.Sh FILES
.Bl -tag -width "/etc/upgrade.conf" -compact
.It Pa /etc/upgrade.conf
@ -103,13 +177,14 @@ Upgrade configuration.
.El
.Sh EXAMPLES
.Bd -literal
system = yes
grub = yes
ports = yes
src = no
grub = yes
system = yes
.Ed
.Sh SEE ALSO
.Xr autoinstall.conf 5 ,
.Xr autoupgrade.conf 5 ,
.Xr upgrade 7 ,
.Xr sysupgrade 8
.Xr sysupgrade 8 ,
.Xr tix 8

View File

@ -127,6 +127,9 @@ ext2 extensions.
You can make a compatible filesystem with:
.Pp
.Dl $ mkfs.ext2 -O none,large_file,filetype
.Pp
You can mount ISO 9660 filesystems for e.g. CD-ROMs using
.Xr iso9660fs 8 .
.Ss Networking
Internet Protocol version 4
.Pq Xr ip 4

View File

@ -37,6 +37,10 @@ void conf_init(struct conf* conf)
void conf_free(struct conf* conf)
{
free(conf->channel);
free(conf->mirror);
free(conf->release_key);
free(conf->release_sig_url);
conf_init(conf);
}
@ -60,12 +64,43 @@ static bool conf_assign(struct conf* conf,
const char* path,
off_t line_number)
{
if ( !strcmp(name, "grub") )
char* new_value;
if ( !strcmp(name, "channel") )
{
if ( !(new_value = strdup(value)) )
return false;
free(conf->channel);
conf->channel = new_value;
}
else if ( !strcmp(name, "force_mirror") )
conf->force_mirror = conf_boolean(name, value, path, line_number);
else if ( !strcmp(name, "grub") )
conf->grub = conf_boolean(name, value, path, line_number);
else if ( !strcmp(name, "mirror") )
{
if ( !(new_value = strdup(value)) )
return false;
free(conf->mirror);
conf->mirror = new_value;
}
else if ( !strcmp(name, "newsrc") )
conf->newsrc = conf_boolean(name, value, path, line_number);
else if ( !strcmp(name, "ports") )
conf->ports = conf_boolean(name, value, path, line_number);
else if ( !strcmp(name, "release_key") )
{
if ( !(new_value = strdup(value)) )
return false;
free(conf->release_key);
conf->release_key = new_value;
}
else if ( !strcmp(name, "release_sig_url") )
{
if ( !(new_value = strdup(value)) )
return false;
free(conf->release_sig_url);
conf->release_sig_url = new_value;
}
else if ( !strcmp(name, "src") )
conf->src = conf_boolean(name, value, path, line_number);
else if ( !strcmp(name, "system") )

View File

@ -22,9 +22,14 @@
struct conf
{
char* channel;
bool force_mirror;
bool grub;
char* mirror;
bool newsrc;
bool ports;
char* release_key;
char* release_sig_url;
bool src;
bool system;
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, 2017, 2020, 2021 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2017, 2020, 2021, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -221,3 +221,17 @@ char** read_lines_file(const char* path, size_t* out_count)
*out_count = count;
return lines;
}
// TODO: Hack to support installing from a read-only root filesystem.
#undef mkdtemp
char* mkdtemp_hack(char* templ)
{
if ( mkdtemp(templ) )
return templ;
if ( errno != EROFS )
return NULL;
memcpy(templ + 1, "dev", 3);
mkdir("/dev/tmp", 01777);
setenv("TMPDIR", "/dev/tmp", 1);
return mkdtemp(templ);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, 2020 Jonas 'Sortie' Termansen.
* Copyright (c) 2015, 2016, 2020, 2022 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -28,4 +28,7 @@ void write_random_seed(const char* path);
char* read_string_file(const char* path);
char** read_lines_file(const char* path, size_t* out_count);
char* mkdtemp_hack(char* templ);
#define mkdtemp mkdtemp_hack
#endif

View File

@ -551,6 +551,21 @@ int main(void)
// TODO: You can leave this program by pressing ^C but it can leave your
// system in an inconsistent state.
if ( conf.channel )
install_configurationf("upgrade.conf", "a", "channel = %s\n",
conf.channel);
if ( conf.force_mirror != false )
install_configurationf("upgrade.conf", "a", "force_mirror = %s\n",
conf.force_mirror ? "yes" : "no");
if ( conf.mirror )
install_configurationf("upgrade.conf", "a", "mirror = %s\n",
conf.mirror);
if ( conf.release_key )
install_configurationf("upgrade.conf", "a", "release_key = %s\n",
conf.release_key);
if ( conf.release_sig_url )
install_configurationf("upgrade.conf", "a", "release_sig_url = %s\n",
conf.release_sig_url);
install_configurationf("upgrade.conf", "a", "src = yes\n");
bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0) ||

View File

@ -330,6 +330,10 @@ int main(int argc, char* argv[])
if ( copy_files )
{
// TODO: Update /etc/upgrade.conf with new release values.
// TODO: What about native upgrades using make sysmerge? Should those
// values be updated then? Should there be an option to control
// this behavior?
const char* target = "";
if ( wait )
{

View File

@ -904,6 +904,7 @@ int main(void)
}
if ( conf.system )
upgrade_finalize(target_release, &new_release, "", ".");
// TODO: Update /etc/upgrade.conf with new release values.
if ( conf.system )
{
printf(" - Creating initrd...\n");

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