Compare commits

..

33 Commits

Author SHA1 Message Date
Jonas 'Sortie' Termansen 7c8e0a7886 Support booting with EFI. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen d7635d5cf2 Add kernel(7) --firmware option. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 5b65ecdead Add fatfs(8). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 74ac590aed Add getty(8). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 46c01935d8 Add terminal and interrupt support to com(4). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 8466846a40 Add nyan(1). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 452d39ce40 Work around pty deadlock. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen b53a9906d1 Add cdrom mounting live environment. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 1ba606d22c Revert "Parallelize driver initialization."
This reverts commit 0fef08bbc4.
2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen e3d3364a6c Parallelize driver initialization. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 767eba0bc7 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-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 3cabee13d8 Decrease PS/2 timeouts. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 4ed3949e21 Add uptime(1) -pr options. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen c4d02f6bc2 Revert "Update to bison-3.8.2."
This reverts commit b82fae810b42c5426d21c4dc153b32f086dd7fde.
2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 87644b4ecf Update to bison-3.8.2. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen d1119c7a2c Add iso9660 filesystem implementation. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 0ded1e1909 Add kernel virtual address space usage debug information. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 663bb5e4a3 Debug TCP socket state listing. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 49177ba80d Add kernel heap allocation tracing debug facility. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 99e334e6be Trianglix 4. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen fd53eb7239 Add tix-check(8). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 396a74fbbe Volatile release. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 274f428082 Add tix-upgrade(8). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen d90accadac Add tix-repository(8).
Support renaming, splitting, and deleting ports via RENAMES.
2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 6e68a0a265 Add signify port. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 04e3dcdb21 Add pty(1). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen cbe56d04eb Add getaddrinfo(1). 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 5b61f81a28 Enable stack smash protection by default. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen b2eeb6c3f5 Enable undefined behavior sanitization by default. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 0c93ea4f97 Add dosfstools port. 2023-12-26 21:54:48 +01:00
Jonas 'Sortie' Termansen 43adfbb82e Add mtools port. 2023-12-26 21:54:48 +01:00
Juhani Krekelä 50f5e88722 Add irssi port. 2023-12-26 19:28:49 +02:00
Juhani Krekelä fc20a4f6a3 Support CSI n L and CSI n M in console and terminal(1).
With these escapes supported, include definitions for line deletion
(dl1, dl) and insertion (il1, il) in terminfo for "sortix".
2023-12-26 19:24:00 +02:00
34 changed files with 279 additions and 3214 deletions

View File

@ -25,7 +25,6 @@ games \
hostname \
ifconfig \
init \
irc \
iso9660 \
kblayout \
kblayout-compiler \

View File

@ -1,21 +0,0 @@
.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

@ -325,7 +325,7 @@ esac
cat << EOF
hook_kernel_pre
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
multiboot /$kernel \$no_random_seed \$enable_network_drivers \$chain "\$@"
multiboot /$kernel --firmware=\$grub_platform \$no_random_seed \$enable_network_drivers \$chain "\$@"
echo done
hook_kernel_post
# TODO: Injecting configuration doesn't work for mounted cdroms.

View File

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

View File

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

View File

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

View File

@ -1,8 +0,0 @@
#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

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

4
irc/.gitignore vendored
View File

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

View File

@ -1,56 +0,0 @@
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

View File

@ -1,65 +0,0 @@
/*
* 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

View File

@ -1,40 +0,0 @@
/*
* 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

View File

@ -1,549 +0,0 @@
/*
* 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 = "";
}
}

View File

@ -1,102 +0,0 @@
/*
* 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

View File

@ -1,237 +0,0 @@
/*
* 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);
}

View File

@ -1,73 +0,0 @@
/*
* 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

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
#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

View File

@ -1,205 +0,0 @@
/*
* 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;
}

View File

@ -1,82 +0,0 @@
/*
* 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

View File

@ -1,34 +0,0 @@
/*
* 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);
}

View File

@ -1,28 +0,0 @@
/*
* 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
View File

@ -1,545 +0,0 @@
/*
* 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;
}
}

View File

@ -1,42 +0,0 @@
/*
* 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

View File

@ -663,16 +663,16 @@ void TextTerminal::RunAnsiCommand(TextBuffer* textbuf, char c)
case 'L': // Append lines before current line.
{
column = 0;
unsigned lines = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( height < line + lines )
lines = height - line;
unsigned count = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( height - line < count )
count = height - line;
TextPos from(0, line);
TextPos move_to(0, line + lines);
unsigned move_lines = height - line - lines;
TextPos move_to(0, line + count);
unsigned move_lines = height - (line + count);
textbuf->Move(move_to, from, move_lines * width);
if ( 0 < lines )
if ( 0 < count )
{
TextPos fill_to(width - 1, line + lines - 1);
TextPos fill_to(width - 1, line + count - 1);
uint32_t fill_fg = attr & ATTR_INVERSE ? bgcolor : fgcolor;
uint32_t fill_bg = attr & ATTR_INVERSE ? fgcolor : bgcolor;
TextChar fill_char(' ', vgacolor, 0, fill_fg, fill_bg);
@ -682,19 +682,22 @@ void TextTerminal::RunAnsiCommand(TextBuffer* textbuf, char c)
case 'M': // Delete lines starting from beginning of current line.
{
column = 0;
unsigned lines = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( height < line + lines )
lines = height - line;
TextPos move_from(0, line + lines);
unsigned count = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( height - line < count )
count = height - line;
TextPos move_from(0, line + count);
TextPos move_to(0, line);
unsigned move_lines = height - line - lines;
unsigned move_lines = height - (line + count);
textbuf->Move(move_to, move_from, move_lines * width);
TextPos fill_from(0, height - lines);
TextPos fill_to(width - 1, height - 1);
uint32_t fill_fg = attr & ATTR_INVERSE ? bgcolor : fgcolor;
uint32_t fill_bg = attr & ATTR_INVERSE ? fgcolor : bgcolor;
TextChar fill_char(' ', vgacolor, 0, fill_fg, fill_bg);
textbuf->Fill(fill_from, fill_to, fill_char);
if ( 0 < count )
{
TextPos fill_from(0, height - count);
TextPos fill_to(width - 1, height - 1);
uint32_t fill_fg = attr & ATTR_INVERSE ? bgcolor : fgcolor;
uint32_t fill_bg = attr & ATTR_INVERSE ? fgcolor : bgcolor;
TextChar fill_char(' ', vgacolor, 0, fill_fg, fill_bg);
textbuf->Fill(fill_from, fill_to, fill_char);
}
} break;
// TODO: CSI Ps P Delete Ps Character(s) (default = 1) (DCH).
// (delete those characters and move the rest of the line leftward).

View File

@ -0,0 +1,7 @@
NAME=grub-i386-efi
EDITION=2
BUILD_LIBRARIES='libiconv? libintl? libfreetype? liblzma?'
SOURCE_PORT=grub
BUILD_SYSTEM=configure
CONFIGURE_ARGS='--disable-werror --program-prefix= --with-platform=efi --target=i686-sortix'
MAKE_ARGS='-C grub-core'

View File

@ -0,0 +1,8 @@
NAME=grub-i386-pc
EDITION=2
RENAMES="grub@1: grub-i386-pc@2"
BUILD_LIBRARIES='libiconv? libintl? libfreetype? liblzma?'
SOURCE_PORT=grub
BUILD_SYSTEM=configure
CONFIGURE_ARGS='--disable-werror --program-prefix= --with-platform=pc'
MAKE_ARGS='-C grub-core'

View File

@ -0,0 +1,8 @@
NAME=grub-x86_64-efi
EDITION=2
BUILD_LIBRARIES='libiconv? libintl? libfreetype? liblzma?'
SOURCE_PORT=grub
BUILD_SYSTEM=configure
# TODO: Unfortunately 32-bit sortix gcc fails due to no 64-bit support.
CONFIGURE_ARGS='--disable-werror --program-prefix= --with-platform=efi --target=x86_64-sortix'
MAKE_ARGS='-C grub-core'

View File

@ -253,6 +253,36 @@ diff -Paur --no-dereference -- grub.upstream/grub-core/lib/libgcrypt-grub/cipher
{
k[i >> 2][i & 3] = key[i];
}
diff -Paur --no-dereference -- grub.upstream/grub-core/osdep/basic/platform.c grub/grub-core/osdep/basic/platform.c
--- grub.upstream/grub-core/osdep/basic/platform.c
+++ grub/grub-core/osdep/basic/platform.c
@@ -18,9 +18,25 @@
#include <grub/util/install.h>
+#ifdef __sortix__
+#include <sys/kernelinfo.h>
+
+#include <string.h>
+#endif
+
const char *
grub_install_get_default_x86_platform (void)
-{
+{
+#ifdef __sortix__
+ char firmware[16];
+ if (!kernelinfo("firmware", firmware, sizeof(firmware)) &&
+ !strcmp(firmware, "efi"))
+#ifdef __i386__
+ return "i386-efi";
+#else
+ return "x86_64-efi";
+#endif
+#endif
return "i386-pc";
}
diff -Paur --no-dereference -- grub.upstream/grub-core/osdep/getroot.c grub/grub-core/osdep/getroot.c
--- grub.upstream/grub-core/osdep/getroot.c
+++ grub/grub-core/osdep/getroot.c
@ -564,6 +594,41 @@ diff -Paur --no-dereference -- grub.upstream/grub-core/osdep/sortix/random.c gru
+ arc4random_buf(out, len);
+ return 0;
+}
diff -Paur --no-dereference -- grub.upstream/grub-core/osdep/unix/config.c grub/grub-core/osdep/unix/config.c
--- grub.upstream/grub-core/osdep/unix/config.c
+++ grub/grub-core/osdep/unix/config.c
@@ -35,8 +35,7 @@
{
static char *value = NULL;
if (!value)
- value = grub_util_path_concat (3, GRUB_SYSCONFDIR,
- "default", "grub");
+ value = grub_util_path_concat (2, GRUB_SYSCONFDIR, "grub");
return value;
}
@@ -78,6 +77,10 @@
if (v && v[0] == 'y' && v[1] == '\0')
cfg->is_cryptodisk_enabled = 1;
+ v = getenv ("GRUB_REMOVABLE");
+ if (v && grub_strcmp (v, "true") == 0)
+ cfg->is_removable = 1;
+
v = getenv ("GRUB_DISTRIBUTOR");
if (v)
cfg->grub_distributor = xstrdup (v);
@@ -105,8 +108,8 @@
*ptr++ = *iptr;
}
- strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" "
- "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\"");
+ strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_REMOVABLE=%s\\nGRUB_DISTRIBUTOR=%s\\n\" "
+ "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_REMOVABLE\" \"$GRUB_DISTRIBUTOR\"");
argv[2] = script;
argv[3] = '\0';
diff -Paur --no-dereference -- grub.upstream/grub-core/osdep/unix/getroot.c grub/grub-core/osdep/unix/getroot.c
--- grub.upstream/grub-core/osdep/unix/getroot.c
+++ grub/grub-core/osdep/unix/getroot.c
@ -640,6 +705,17 @@ diff -Paur --no-dereference -- grub.upstream/grub-core/osdep/unix/platform.c gru
bootnum = line + sizeof ("Boot") - 1;
bootnum[4] = '\0';
if (!verbosity)
diff -Paur --no-dereference -- grub.upstream/include/grub/emu/config.h grub/include/grub/emu/config.h
--- grub.upstream/include/grub/emu/config.h
+++ grub/include/grub/emu/config.h
@@ -36,6 +36,7 @@
struct grub_util_config
{
int is_cryptodisk_enabled;
+ int is_removable;
char *grub_distributor;
};
diff -Paur --no-dereference -- grub.upstream/include/grub/emu/getroot.h grub/include/grub/emu/getroot.h
--- grub.upstream/include/grub/emu/getroot.h
+++ grub/include/grub/emu/getroot.h
@ -670,6 +746,27 @@ diff -Paur --no-dereference -- grub.upstream/util/bash-completion.d/Makefile.in
bashcompletion_DATA = $(bash_completion_script)
all: all-am
diff -Paur --no-dereference -- grub.upstream/util/config.c grub/util/config.c
--- grub.upstream/util/config.c
+++ grub/util/config.c
@@ -42,6 +42,17 @@
cfg->is_cryptodisk_enabled = 1;
continue;
}
+ if (grub_strncmp (ptr, "GRUB_REMOVABLE=",
+ sizeof ("GRUB_REMOVABLE=") - 1) == 0)
+ {
+ ptr += sizeof ("GRUB_REMOVABLE=") - 1;
+ if (*ptr == '"' || *ptr == '\'')
+ ptr++;
+ if (grub_strncmp (ptr, "true", sizeof ("true") - 1) == 0)
+ cfg->is_removable = 1;
+ continue;
+ }
+
if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=",
sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0)
{
diff -Paur --no-dereference -- grub.upstream/util/getroot.c grub/util/getroot.c
--- grub.upstream/util/getroot.c
+++ grub/util/getroot.c
@ -724,6 +821,44 @@ diff -Paur --no-dereference -- grub.upstream/util/grub-fstest.c grub/util/grub-f
root = alloc_root;
}
}
diff -Paur --no-dereference -- grub.upstream/util/grub-install.c grub/util/grub-install.c
--- grub.upstream/util/grub-install.c
+++ grub/util/grub-install.c
@@ -848,6 +848,9 @@
grub_util_load_config (&config);
+ if (config.is_removable)
+ removable = 1;
+
if (!bootloader_id && config.grub_distributor)
{
char *ptr;
@@ -860,7 +863,7 @@
if (!bootloader_id || bootloader_id[0] == '\0')
{
free (bootloader_id);
- bootloader_id = xstrdup ("grub");
+ bootloader_id = xstrdup ("sortix");
}
if (!grub_install_source_directory)
@@ -1803,6 +1806,7 @@
break;
case GRUB_INSTALL_PLATFORM_I386_EFI:
+#ifndef __sortix__
if (!efidir_is_mac)
{
char *dst = grub_util_path_concat (2, efidir, "grub.efi");
@@ -1810,6 +1814,7 @@
grub_install_copy_file (imgfile, dst, 1);
free (dst);
}
+#endif
/* Fallthrough. */
case GRUB_INSTALL_PLATFORM_X86_64_EFI:
if (efidir_is_mac)
diff -Paur --no-dereference -- grub.upstream/util/grub-mkconfig.in grub/util/grub-mkconfig.in
--- grub.upstream/util/grub-mkconfig.in
+++ grub/util/grub-mkconfig.in
@ -924,7 +1059,7 @@ diff -Paur --no-dereference -- grub.upstream/util/grub.d/10_sortix.in grub/util/
+ insmod part_$PARTMAP
+ insmod $FS
+ search --no-floppy --fs-uuid --set=root $HINTS_STRING $FS_UUID
+ multiboot $BOOT_REL/sortix.bin $GRUB_CMDLINE_SORTIX
+ multiboot $BOOT_REL/sortix.bin --firmware=\$grub_platform $GRUB_CMDLINE_SORTIX
+ module $OLD_BOOT_REL/random.seed --random-seed
+ module $BOOT_REL/sortix.initrd
+}

View File

@ -1,4 +1,6 @@
NAME=grub
EDITION=2
RENAMES="grub@1: grub@2"
BUILD_LIBRARIES='libiconv? libintl? libfreetype? liblzma?'
VERSION=1.0-rc1
DISTNAME=sortix-grub-$VERSION
@ -8,6 +10,6 @@ SHA256SUM=82ac8faf257fb3476969a0b79a0b5fd53d4cdefb2e2aa5941381477e38c5f9c5
UPSTREAM_SITE=https://pub.sortix.org/sortix/toolchain
UPSTREAM_ARCHIVE=$ARCHIVE
BUILD_SYSTEM=configure
CONFIGURE_ARGS='--disable-werror --program-prefix='
CONFIGURE_ARGS='--disable-werror --program-prefix= --with-platform=none'
POST_INSTALL=../grub.post-install
VERSION_REGEX='([0-9]+\.[0-9]+(\.[0-9]+)*(-rc[0-9]+)?)'
VERSION_REGEX='([0-9]+\.[0-9]+(\.[0-9]+)*(-rc[0-9]+)?)

View File

@ -7,3 +7,6 @@ if [ ! -e "$TIX_INSTALL_DIR$PREFIX/share/grub/unicode.pf2" ]; then
[ -e /usr/share/grub/unicode.pf2 ] &&
cp /usr/share/grub/unicode.pf2 "$TIX_INSTALL_DIR$PREFIX/share/grub/unicode.pf2"
fi
rm -rf "$TIX_INSTALL_DIR$PREFIX/lib/grub/"*"-none"
rmdir --ignore-fail-on-non-empty "$TIX_INSTALL_DIR$PREFIX/lib/grub"
rmdir --ignore-fail-on-non-empty "$TIX_INSTALL_DIR$PREFIX/lib"

View File

@ -21,30 +21,32 @@ diff -Paur --no-dereference -- irssi.upstream/configure irssi/configure
diff -Paur --no-dereference -- irssi.upstream/irssi.conf irssi/irssi.conf
--- irssi.upstream/irssi.conf
+++ irssi/irssi.conf
@@ -1,4 +1,5 @@
servers = (
@@ -12,6 +12,7 @@
{ address = "irc.quakenet.org"; chatnet = "QuakeNet"; port = "6667"; },
{ address = "irc.rizon.net"; chatnet = "Rizon"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; },
+ { address = "irc.sortix.org"; chatnet = "sortix"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
{ address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; },
{ address = "ssl.efnet.org"; chatnet = "EFNet"; port = "9999"; use_tls = "yes"; tls_verify = "no"; },
{ address = "irc.esper.net"; chatnet = "EsperNet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
@@ -16,6 +17,9 @@
{ address = "irc.undernet.org"; chatnet = "Undernet"; port = "6667"; }
);
chatnets = {
@@ -85,6 +86,9 @@
SILC = {
type = "SILC";
};
+ sortix = {
+ type = "IRC";
+ };
DALnet = {
Undernet = {
type = "IRC";
max_kicks = "4";
@@ -94,6 +98,7 @@
};
channels = (
max_kicks = "1";
@@ -103,6 +107,7 @@
{ name = "#netfuze"; chatnet = "NetFuze"; autojoin = "No"; },
{ name = "#oftc"; chatnet = "OFTC"; autojoin = "No"; },
{ name = "silc"; chatnet = "SILC"; autojoin = "No"; }
+ { name = "#sortix"; chatnet = "sortix"; autojoin = "No"; },
{ name = "#lobby"; chatnet = "EsperNet"; autojoin = "No"; },
{ name = "#libera"; chatnet = "liberachat";autojoin = "No"; },
{ name = "#irssi"; chatnet = "liberachat";autojoin = "No"; },
);
aliases = {
diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.c
--- irssi.upstream/src/core/log.c
+++ irssi/src/core/log.c
@ -53,7 +55,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
{
char *dir;
+/* PATCH: Sortix doesn't implement flock */
+#ifndef __sortix__
+#ifdef F_SETLK
struct flock lock;
+#endif
@ -64,7 +66,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
return FALSE;
}
+/* PATCH: Sortix doesn't implement flock */
+#ifndef __sortix__
+#ifdef F_SETLK
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
if (fcntl(log->handle, F_SETLK, &lock) == -1 && errno == EACCES) {
@ -81,7 +83,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
void log_stop_logging(LOG_REC *log)
{
+/* PATCH: Sortix doesn't implement flock */
+#ifndef __sortix__
+#ifdef F_SETLK
struct flock lock;
+#endif
@ -92,7 +94,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
"\n", time(NULL));
+/* PATCH: Sortix doesn't implement flock */
+#ifndef __sortix__
+#ifdef F_SETLK
memset(&lock, 0, sizeof(lock));
lock.l_type = F_UNLCK;
fcntl(log->handle, F_SETLK, &lock);

View File

@ -69,11 +69,6 @@ const char* prompt_man_page = "installation";
static struct partition_table* search_bios_boot_pt(struct filesystem* root_fs)
{
char firmware[64];
if ( kernelinfo("firmware", firmware, sizeof(firmware)) != 0 )
return NULL;
if ( strcmp(firmware, "bios") != 0 )
return NULL;
struct blockdevice* bdev = root_fs->bdev;
while ( bdev->p )
bdev = bdev->p->parent_bdev;
@ -194,6 +189,10 @@ static bool should_install_bootloader_bdev(struct blockdevice* bdev)
static bool should_install_bootloader(void)
{
char firmware[16];
if ( !kernelinfo("firmware", firmware, sizeof(firmware)) &&
!strcmp(firmware, "efi") )
return true;
bool any_systems = false;
for ( size_t i = 0; i < hds_count; i++ )
{
@ -354,6 +353,9 @@ static void grub_hash_password(char* buffer, size_t buffer_size, const char* pw)
static const char* const ignore_kernel_options[] =
{
"--firmware=bios",
"--firmware=efi",
"--firmware=pc",
"--no-random-seed",
"--random-seed",
NULL,
@ -511,7 +513,12 @@ int main(void)
err(2, "chdir: %s", etc);
struct utsname uts;
uname(&uts);
if ( uname(&uts) < 0 )
err(1, "uname");
char firmware[16];
if ( kernelinfo("firmware", firmware, sizeof(firmware)) != 0 )
err(1, "kernelinfo");
struct conf conf;
conf_init(&conf);
@ -572,6 +579,7 @@ int main(void)
missing_program("fsck.ext2") |
missing_program("grub-install") |
missing_program("man") |
(!strcmp(firmware, "efi") && missing_program("mkfs.fat")) |
missing_program("sed") |
missing_program("xargs") )
{
@ -909,6 +917,7 @@ int main(void)
mktable_tip, devices_tip);
struct filesystem* root_filesystem = NULL;
struct filesystem* boot_filesystem = NULL;
struct filesystem* esp_filesystem = NULL;
struct filesystem* bootloader_filesystem = NULL;
bool not_first = false;
while ( true )
@ -951,6 +960,7 @@ int main(void)
}
root_filesystem = NULL;
boot_filesystem = NULL;
esp_filesystem = NULL;
bool cant_mount = false;
for ( size_t i = 0; i < mountpoints_used; i++ )
{
@ -976,6 +986,8 @@ int main(void)
root_filesystem = mnt->fs;
if ( !strcmp(mnt->entry.fs_file, "/boot") )
boot_filesystem = mnt->fs;
if ( !strcmp(mnt->entry.fs_file, "/boot/efi") )
esp_filesystem = mnt->fs;
}
if ( cant_mount )
continue;
@ -983,12 +995,13 @@ int main(void)
bootloader_filesystem = boot_filesystem ? boot_filesystem : root_filesystem;
assert(bootloader_filesystem);
if ( !strcasecmp(accept_grub, "yes") &&
!strcmp(firmware, "bios") &&
missing_bios_boot_partition(bootloader_filesystem) )
{
const char* where = boot_filesystem ? "/boot" : "root";
const char* dev = device_path_of_blockdevice(bootloader_filesystem->bdev);
assert(dev);
textf("You are a installing BIOS bootloader and the %s "
textf("You are installing a BIOS bootloader and the %s "
"filesystem is located on a GPT partition, but you haven't "
"made a BIOS boot partition on the %s GPT disk. Pick "
"biosboot during mkpart and make a 1 MiB partition.\n",
@ -1007,10 +1020,41 @@ int main(void)
continue;
text("Proceeding, but expect the installation to fail.\n");
}
else if ( !strcasecmp(accept_grub, "yes") &&
!strcmp(firmware, "efi") &&
!esp_filesystem )
{
textf("You are installing an EFI bootloader, but you haven't made "
"an EFI System Partition. Pick efi during mkpart and make a "
"partition and mount it as /boot/efi.\n");
char return_to_disked[10];
while ( true )
{
// TODO: Document autoinstall key.
prompt(return_to_disked, sizeof(return_to_disked),
"missing_esp_partition",
"Return to disked to make an EFI partition?", "yes");
if ( strcasecmp(accept_grub, "no") == 0 ||
strcasecmp(accept_grub, "yes") == 0 )
break;
}
if ( !strcasecmp(return_to_disked, "yes") )
continue;
text("Proceeding, but expect the installation to fail.\n");
}
// TODO: Verify the ESP is actually FAT and has the correct ID/GUID.
break;
}
text("\n");
if ( !strcmp(firmware, "efi") )
{
// TODO: Ask for GRUB_DISTRIBUTOR when FAT LFN is implemented and make
// GRUB_REMOVABLE optional.
install_configurationf("grub", "w", "GRUB_REMOVABLE=true\n");
}
textf("We are now ready to install %s %s. Take a moment to verify "
"everything is in order.\n", BRAND_DISTRIBUTION_NAME, VERSIONSTR);
text("\n");
@ -1022,7 +1066,7 @@ int main(void)
const char* where = mnt->entry.fs_file;
printf(" %-16s use as %s\n", devname, where);
}
if ( strcasecmp(accept_grub, "yes") == 0 )
if ( strcasecmp(accept_grub, "yes") == 0 && !strcmp(firmware, "bios") )
{
struct partition* bbp = search_bios_boot_partition(bootloader_filesystem);
if ( bbp )
@ -1118,8 +1162,7 @@ int main(void)
{
printf(" - Installing bootloader...\n");
execute((const char*[]) { "chroot", "-d", ".", "grub-install",
device_path_of_blockdevice(bootloader_filesystem->bdev), NULL },
"_eqQ");
NULL }, "_eqQ");
printf(" - Configuring bootloader...\n");
execute((const char*[]) { "chroot", "-d", ".", "update-grub", NULL },
"_eqQ");

View File

@ -415,33 +415,34 @@ static void run_ansi_command(char c)
case 'L': // Append lines before current line.
{
column = 0;
unsigned lines = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( rows < row + lines )
lines = rows - row;
unsigned move_lines = rows - row - lines;
move(0, row + lines, 0, row, move_lines * columns);
unsigned count = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( rows - row < count )
count = rows - row;
unsigned move_lines = rows - (row + count);
move(0, row + count, 0, row, move_lines * columns);
struct entry with;
with.attr = 0;
with.fgcolor = attr & ATTR_INVERSE ? current_bgcolor : current_fgcolor;
with.bgcolor = attr & ATTR_INVERSE ? current_fgcolor : current_bgcolor;
with.wc = 0;
if ( 0 < lines )
fill(0, row, columns - 1, row + lines - 1, with);
if ( 0 < count )
fill(0, row, columns - 1, row + count - 1, with);
} break;
case 'M': // Delete lines starting from beginning of current line.
{
column = 0;
unsigned lines = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( rows < row + lines )
lines = rows - row;
unsigned move_lines = rows - row - lines;
move(0, row, 0, row + lines, move_lines * columns);
unsigned count = 0 < ansiusedparams ? ansiparams[0] : 1;
if ( rows - row < count )
count = rows - row;
unsigned move_lines = rows - (row + count);
move(0, row, 0, row + count, move_lines * columns);
struct entry with;
with.attr = 0;
with.fgcolor = attr & ATTR_INVERSE ? current_bgcolor : current_fgcolor;
with.bgcolor = attr & ATTR_INVERSE ? current_fgcolor : current_bgcolor;
with.wc = 0;
fill(0, rows - lines, columns - 1, rows - 1, with);
if ( 0 < count )
fill(0, rows - count, columns - 1, rows - 1, with);
} break;
// TODO: CSI Ps P Delete Ps Character(s) (default = 1) (DCH).
// (delete those characters and move the rest of the line leftward).