Compare commits
33 Commits
7c8e0a7886
...
bf3d066093
Author | SHA1 | Date |
---|---|---|
Jonas 'Sortie' Termansen | bf3d066093 | |
Jonas 'Sortie' Termansen | c0f1a19168 | |
Jonas 'Sortie' Termansen | bc48ded292 | |
Jonas 'Sortie' Termansen | 870fb38bf2 | |
Jonas 'Sortie' Termansen | 2ce9f50905 | |
Jonas 'Sortie' Termansen | f29bbcbde8 | |
Jonas 'Sortie' Termansen | 93fac0fde3 | |
Jonas 'Sortie' Termansen | 82d0bf2b79 | |
Jonas 'Sortie' Termansen | 9910260de7 | |
Jonas 'Sortie' Termansen | e6c6570bf0 | |
Jonas 'Sortie' Termansen | 3ae267d16c | |
Jonas 'Sortie' Termansen | dd08eba6fb | |
Jonas 'Sortie' Termansen | cf353e38af | |
Jonas 'Sortie' Termansen | 2a663892c5 | |
Jonas 'Sortie' Termansen | 615747faf6 | |
Jonas 'Sortie' Termansen | e83b67da24 | |
Jonas 'Sortie' Termansen | 8ee87fc11a | |
Jonas 'Sortie' Termansen | 9c32119949 | |
Jonas 'Sortie' Termansen | 6c52971e8f | |
Jonas 'Sortie' Termansen | 0fb0df0f62 | |
Jonas 'Sortie' Termansen | ba25d9ea1f | |
Jonas 'Sortie' Termansen | d95127432a | |
Jonas 'Sortie' Termansen | 1b5f3242ad | |
Jonas 'Sortie' Termansen | 51cc59f4ca | |
Jonas 'Sortie' Termansen | ec9aa03f99 | |
Jonas 'Sortie' Termansen | ea075b6f44 | |
Jonas 'Sortie' Termansen | 5c9d9fc9ec | |
Jonas 'Sortie' Termansen | 56fbc45a9f | |
Jonas 'Sortie' Termansen | 29d7c4aa72 | |
Jonas 'Sortie' Termansen | a2b31ec1cb | |
Jonas 'Sortie' Termansen | 8681cac923 | |
Juhani Krekelä | 13a75f84e9 | |
Juhani Krekelä | 1c5a396e80 |
1
Makefile
1
Makefile
|
@ -25,6 +25,7 @@ games \
|
|||
hostname \
|
||||
ifconfig \
|
||||
init \
|
||||
irc \
|
||||
iso9660 \
|
||||
kblayout \
|
||||
kblayout-compiler \
|
||||
|
|
|
@ -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
|
|
@ -325,7 +325,7 @@ esac
|
|||
cat << EOF
|
||||
hook_kernel_pre
|
||||
echo -n "Loading /$kernel ($(human_size $kernel)) ... "
|
||||
multiboot /$kernel --firmware=\$grub_platform \$no_random_seed \$enable_network_drivers \$chain "\$@"
|
||||
multiboot /$kernel \$no_random_seed \$enable_network_drivers \$chain "\$@"
|
||||
echo done
|
||||
hook_kernel_post
|
||||
# TODO: Injecting configuration doesn't work for mounted cdroms.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
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"
|
||||
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"
|
||||
sets="basic minimal"
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
irc
|
||||
*.o
|
||||
config.h
|
||||
tests
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 = "";
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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", ¶m) ||
|
||||
is_command_param(input, "/window", ¶m) )
|
||||
{
|
||||
struct scrollback* sb = find_scrollback(ui->network, param);
|
||||
if ( sb )
|
||||
ui->current = sb;
|
||||
}
|
||||
else if ( is_command_param(input, "/query", ¶m) )
|
||||
{
|
||||
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", ¶m) )
|
||||
{
|
||||
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", ¶m) )
|
||||
{
|
||||
irc_command_part(conn, param);
|
||||
}
|
||||
else if ( is_command(input, "/quit", ¶m) )
|
||||
{
|
||||
irc_command_quit(conn, param ? param : "Quiting");
|
||||
}
|
||||
else if ( is_command_param(input, "/nick", ¶m) )
|
||||
{
|
||||
irc_command_nick(conn, param);
|
||||
}
|
||||
else if ( is_command_param(input, "/raw", ¶m) )
|
||||
{
|
||||
irc_transmit_string(conn, param);
|
||||
}
|
||||
else if ( is_command_param(input, "/me", ¶m) )
|
||||
{
|
||||
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", ¶m) )
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -663,16 +663,16 @@ void TextTerminal::RunAnsiCommand(TextBuffer* textbuf, char c)
|
|||
case 'L': // Append lines before current line.
|
||||
{
|
||||
column = 0;
|
||||
unsigned count = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( height - line < count )
|
||||
count = height - line;
|
||||
unsigned lines = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( height < line + lines )
|
||||
lines = height - line;
|
||||
TextPos from(0, line);
|
||||
TextPos move_to(0, line + count);
|
||||
unsigned move_lines = height - (line + count);
|
||||
TextPos move_to(0, line + lines);
|
||||
unsigned move_lines = height - line - lines;
|
||||
textbuf->Move(move_to, from, move_lines * width);
|
||||
if ( 0 < count )
|
||||
if ( 0 < lines )
|
||||
{
|
||||
TextPos fill_to(width - 1, line + count - 1);
|
||||
TextPos fill_to(width - 1, line + lines - 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,22 +682,19 @@ void TextTerminal::RunAnsiCommand(TextBuffer* textbuf, char c)
|
|||
case 'M': // Delete lines starting from beginning of current line.
|
||||
{
|
||||
column = 0;
|
||||
unsigned count = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( height - line < count )
|
||||
count = height - line;
|
||||
TextPos move_from(0, line + count);
|
||||
unsigned lines = 0 < ansiusedparams ? ansiparams[0] : 1;
|
||||
if ( height < line + lines )
|
||||
lines = height - line;
|
||||
TextPos move_from(0, line + lines);
|
||||
TextPos move_to(0, line);
|
||||
unsigned move_lines = height - (line + count);
|
||||
unsigned move_lines = height - line - lines;
|
||||
textbuf->Move(move_to, move_from, move_lines * width);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
} break;
|
||||
// TODO: CSI Ps P Delete Ps Character(s) (default = 1) (DCH).
|
||||
// (delete those characters and move the rest of the line leftward).
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
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'
|
|
@ -1,8 +0,0 @@
|
|||
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'
|
|
@ -1,8 +0,0 @@
|
|||
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'
|
|
@ -253,36 +253,6 @@ 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
|
||||
|
@ -594,41 +564,6 @@ 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
|
||||
|
@ -705,17 +640,6 @@ 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
|
||||
|
@ -746,27 +670,6 @@ 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
|
||||
|
@ -821,44 +724,6 @@ 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
|
||||
|
@ -1059,7 +924,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 --firmware=\$grub_platform $GRUB_CMDLINE_SORTIX
|
||||
+ multiboot $BOOT_REL/sortix.bin $GRUB_CMDLINE_SORTIX
|
||||
+ module $OLD_BOOT_REL/random.seed --random-seed
|
||||
+ module $BOOT_REL/sortix.initrd
|
||||
+}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
NAME=grub
|
||||
EDITION=2
|
||||
RENAMES="grub@1: grub@2"
|
||||
BUILD_LIBRARIES='libiconv? libintl? libfreetype? liblzma?'
|
||||
VERSION=1.0-rc1
|
||||
DISTNAME=sortix-grub-$VERSION
|
||||
|
@ -10,6 +8,6 @@ SHA256SUM=82ac8faf257fb3476969a0b79a0b5fd53d4cdefb2e2aa5941381477e38c5f9c5
|
|||
UPSTREAM_SITE=https://pub.sortix.org/sortix/toolchain
|
||||
UPSTREAM_ARCHIVE=$ARCHIVE
|
||||
BUILD_SYSTEM=configure
|
||||
CONFIGURE_ARGS='--disable-werror --program-prefix= --with-platform=none'
|
||||
CONFIGURE_ARGS='--disable-werror --program-prefix='
|
||||
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]+)?)'
|
||||
|
|
|
@ -7,6 +7,3 @@ 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"
|
||||
|
|
|
@ -21,32 +21,30 @@ 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
|
||||
@@ -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"; },
|
||||
@@ -1,4 +1,5 @@
|
||||
servers = (
|
||||
+ { address = "irc.sortix.org"; chatnet = "sortix"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; },
|
||||
{ address = "irc.undernet.org"; chatnet = "Undernet"; port = "6667"; }
|
||||
{ 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 @@
|
||||
);
|
||||
|
||||
@@ -85,6 +86,9 @@
|
||||
SILC = {
|
||||
type = "SILC";
|
||||
};
|
||||
chatnets = {
|
||||
+ sortix = {
|
||||
+ type = "IRC";
|
||||
+ };
|
||||
Undernet = {
|
||||
DALnet = {
|
||||
type = "IRC";
|
||||
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"; },
|
||||
);
|
||||
max_kicks = "4";
|
||||
@@ -94,6 +98,7 @@
|
||||
};
|
||||
|
||||
aliases = {
|
||||
channels = (
|
||||
+ { name = "#sortix"; chatnet = "sortix"; autojoin = "No"; },
|
||||
{ name = "#lobby"; chatnet = "EsperNet"; autojoin = "No"; },
|
||||
{ name = "#libera"; chatnet = "liberachat";autojoin = "No"; },
|
||||
{ name = "#irssi"; chatnet = "liberachat";autojoin = "No"; },
|
||||
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
|
||||
|
@ -55,7 +53,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
|
|||
{
|
||||
char *dir;
|
||||
+/* PATCH: Sortix doesn't implement flock */
|
||||
+#ifdef F_SETLK
|
||||
+#ifndef __sortix__
|
||||
struct flock lock;
|
||||
+#endif
|
||||
|
||||
|
@ -66,7 +64,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
|
|||
return FALSE;
|
||||
}
|
||||
+/* PATCH: Sortix doesn't implement flock */
|
||||
+#ifdef F_SETLK
|
||||
+#ifndef __sortix__
|
||||
memset(&lock, 0, sizeof(lock));
|
||||
lock.l_type = F_WRLCK;
|
||||
if (fcntl(log->handle, F_SETLK, &lock) == -1 && errno == EACCES) {
|
||||
|
@ -83,7 +81,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 */
|
||||
+#ifdef F_SETLK
|
||||
+#ifndef __sortix__
|
||||
struct flock lock;
|
||||
+#endif
|
||||
|
||||
|
@ -94,7 +92,7 @@ diff -Paur --no-dereference -- irssi.upstream/src/core/log.c irssi/src/core/log.
|
|||
"\n", time(NULL));
|
||||
|
||||
+/* PATCH: Sortix doesn't implement flock */
|
||||
+#ifdef F_SETLK
|
||||
+#ifndef __sortix__
|
||||
memset(&lock, 0, sizeof(lock));
|
||||
lock.l_type = F_UNLCK;
|
||||
fcntl(log->handle, F_SETLK, &lock);
|
||||
|
|
|
@ -69,6 +69,11 @@ 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;
|
||||
|
@ -189,10 +194,6 @@ 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++ )
|
||||
{
|
||||
|
@ -353,9 +354,6 @@ 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,
|
||||
|
@ -513,12 +511,7 @@ int main(void)
|
|||
err(2, "chdir: %s", etc);
|
||||
|
||||
struct utsname uts;
|
||||
if ( uname(&uts) < 0 )
|
||||
err(1, "uname");
|
||||
|
||||
char firmware[16];
|
||||
if ( kernelinfo("firmware", firmware, sizeof(firmware)) != 0 )
|
||||
err(1, "kernelinfo");
|
||||
uname(&uts);
|
||||
|
||||
struct conf conf;
|
||||
conf_init(&conf);
|
||||
|
@ -579,7 +572,6 @@ 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") )
|
||||
{
|
||||
|
@ -917,7 +909,6 @@ 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 )
|
||||
|
@ -960,7 +951,6 @@ 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++ )
|
||||
{
|
||||
|
@ -986,8 +976,6 @@ 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;
|
||||
|
@ -995,13 +983,12 @@ 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 installing a BIOS bootloader and the %s "
|
||||
textf("You are a installing 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",
|
||||
|
@ -1020,41 +1007,10 @@ 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");
|
||||
|
@ -1066,7 +1022,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 && !strcmp(firmware, "bios") )
|
||||
if ( strcasecmp(accept_grub, "yes") == 0 )
|
||||
{
|
||||
struct partition* bbp = search_bios_boot_partition(bootloader_filesystem);
|
||||
if ( bbp )
|
||||
|
@ -1162,7 +1118,8 @@ int main(void)
|
|||
{
|
||||
printf(" - Installing bootloader...\n");
|
||||
execute((const char*[]) { "chroot", "-d", ".", "grub-install",
|
||||
NULL }, "_eqQ");
|
||||
device_path_of_blockdevice(bootloader_filesystem->bdev), NULL },
|
||||
"_eqQ");
|
||||
printf(" - Configuring bootloader...\n");
|
||||
execute((const char*[]) { "chroot", "-d", ".", "update-grub", NULL },
|
||||
"_eqQ");
|
||||
|
|
|
@ -415,34 +415,33 @@ static void run_ansi_command(char c)
|
|||
case 'L': // Append lines before current line.
|
||||
{
|
||||
column = 0;
|
||||
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);
|
||||
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);
|
||||
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 < count )
|
||||
fill(0, row, columns - 1, row + count - 1, with);
|
||||
if ( 0 < lines )
|
||||
fill(0, row, columns - 1, row + lines - 1, with);
|
||||
} break;
|
||||
case 'M': // Delete lines starting from beginning of current line.
|
||||
{
|
||||
column = 0;
|
||||
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);
|
||||
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);
|
||||
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 < count )
|
||||
fill(0, rows - count, columns - 1, rows - 1, with);
|
||||
fill(0, rows - lines, 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).
|
||||
|
|
Loading…
Reference in New Issue