Compare commits

...

12 Commits

Author SHA1 Message Date
Bananymous 69bdff6b7e ports: Add links port
We now have graphical browser on banan-os :O
2025-06-11 16:02:00 +03:00
Bananymous 8574fcf6e1 ports: Add libwebp port 2025-06-11 16:01:47 +03:00
Bananymous f4f424bf04 ports: Add libtiff port 2025-06-11 16:01:47 +03:00
Bananymous ac745bfa3d ports: Add libjpeg port 2025-06-11 16:01:47 +03:00
Bananymous aa691f236e ports: Add libpng port 2025-06-11 16:01:47 +03:00
Bananymous a0a9d49d81 ports: Update toolchain triple and add post_configure
post_configure is called after default configure. This can be used for
port specific customization when using default configure function
2025-06-11 16:01:47 +03:00
Bananymous 125f8b591d Kernel: Don't crash if socket tries to reconnect
:D
2025-06-11 01:54:41 +03:00
Bananymous c97b60e7e5 LibGUI: Remove parameters I had forgot in function declarations 2025-06-11 01:53:31 +03:00
Bananymous 8a73414e3e LibGUI: Add support for clip area to texture 2025-06-11 01:53:31 +03:00
Bananymous ac22e006a4 Shell: Don't get stuck on broken state when failing to build command 2025-06-10 11:03:02 +03:00
Bananymous 30d5d85d1d userspace: Add `test` utility 2025-06-08 23:56:39 +03:00
Bananymous 6f74f3c386 BAN: Allow constexpr construction of StringView 2025-06-07 18:29:32 +03:00
22 changed files with 1159 additions and 27 deletions

View File

@ -21,7 +21,8 @@ namespace BAN
constexpr StringView(const char* string, size_type len = -1)
{
if (len == size_type(-1))
len = strlen(string);
for (len = 0; string[len];)
len++;
m_data = string;
m_size = len;
}

View File

@ -127,7 +127,8 @@ namespace Kernel
LockGuard _(m_mutex);
ASSERT(!m_connection_info.has_value());
if (m_connection_info.has_value())
return BAN::Error::from_errno(EISCONN);
switch (m_state)
{

View File

@ -43,10 +43,16 @@ clean() {
find . -mindepth 1 -maxdepth 1 -not -name 'patches' -not -name 'build.sh' -exec rm -rf {} +
}
post_configure() {
:
}
configure() {
configure_options=("--host=$BANAN_ARCH-banan_os" '--prefix=/usr')
configure_options=("--host=$BANAN_ARCH-pc-banan_os" '--prefix=/usr')
configure_options+=("${CONFIGURE_OPTIONS[@]}")
./configure "${configure_options[@]}" || exit 1
post_configure
}
build() {

13
ports/libjpeg/build.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash ../install.sh
NAME='libjpeg'
VERSION='9f'
DOWNLOAD_URL="https://www.ijg.org/files/jpegsrc.v9f.tar.gz#04705c110cb2469caa79fb71fba3d7bf834914706e9641a4589485c1f832565b"
TAR_CONTENT="jpeg-$VERSION"
install() {
make install DESTDIR="$BANAN_SYSROOT" || exit 1
# remove libtool files
rm -f $BANAN_SYSROOT/usr/lib/libjpeg.la
}

View File

@ -0,0 +1,13 @@
diff -ruN jpeg-9f/config.sub jpeg-9f-banan_os/config.sub
--- jpeg-9f/config.sub 2024-01-08 19:38:48.000000000 +0200
+++ jpeg-9f-banan_os/config.sub 2025-06-11 10:57:34.463953673 +0300
@@ -1748,7 +1748,8 @@
| skyos* | haiku* | rdos* | toppers* | drops* | es* \
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
- | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr*)
+ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
+ | banan_os* )
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)

View File

@ -0,0 +1,20 @@
diff -ruN jpeg-9f/configure jpeg-9f-banan_os/configure
--- jpeg-9f/configure 2024-01-08 19:38:44.000000000 +0200
+++ jpeg-9f-banan_os/configure 2025-06-11 11:03:15.719030091 +0300
@@ -12944,6 +12944,16 @@
esac
;;
+banan_os*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker="$host_os DynamicLoader.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
beos*)
library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"

14
ports/libpng/build.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash ../install.sh
NAME='libpng'
VERSION='1.6.48'
DOWNLOAD_URL="https://download.sourceforge.net/libpng/libpng-$VERSION.tar.gz#68f3d83a79d81dfcb0a439d62b411aa257bb4973d7c67cd1ff8bdf8d011538cd"
DEPENDENCIES=('zlib')
install() {
make install DESTDIR="$BANAN_SYSROOT" || exit 1
# remove libtool files
rm -f $BANAN_SYSROOT/usr/lib/libpng.la
rm -f $BANAN_SYSROOT/usr/lib/libpng16.la
}

View File

@ -0,0 +1,12 @@
diff -ruN libpng-1.6.48/config.sub libpng-1.6.48-banan_os/config.sub
--- libpng-1.6.48/config.sub 2025-04-30 16:51:46.000000000 +0300
+++ libpng-1.6.48-banan_os/config.sub 2025-06-10 14:07:16.060041002 +0300
@@ -1768,7 +1768,7 @@
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
- | fiwix* | mlibc* | cos* | mbr* | ironclad* )
+ | fiwix* | mlibc* | cos* | mbr* | ironclad* | banan_os* )
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)

View File

@ -0,0 +1,31 @@
diff -ruN libpng-1.6.48/configure libpng-1.6.48-banan_os/configure
--- libpng-1.6.48/configure 2025-04-30 16:51:46.000000000 +0300
+++ libpng-1.6.48-banan_os/configure 2025-06-10 15:01:05.944037763 +0300
@@ -6523,6 +6523,10 @@
lt_cv_deplibs_check_method=pass_all
;;
+banan_os*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
beos*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -12389,6 +12393,16 @@
esac
;;
+banan_os*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker="$host_os DynamicLoader.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
beos*)
library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"

15
ports/libtiff/build.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash ../install.sh
NAME='libtiff'
VERSION='4.7.0'
DOWNLOAD_URL="https://download.osgeo.org/libtiff/tiff-$VERSION.tar.gz#67160e3457365ab96c5b3286a0903aa6e78bdc44c4bc737d2e486bcecb6ba976"
TAR_CONTENT="tiff-$VERSION"
DEPENDENCIES=('zlib' 'zstd' 'libjpeg')
install() {
make install "DESTDIR=$BANAN_SYSROOT" || exit 1
# remove libtool files
rm -f $BANAN_SYSROOT/usr/lib/libtiff.la
rm -f $BANAN_SYSROOT/usr/lib/libtiffxx.la
}

View File

@ -0,0 +1,11 @@
diff -ruN tiff-4.7.0/config/config.sub tiff-4.7.0-banan_os/config/config.sub
--- tiff-4.7.0/config/config.sub 2024-09-11 10:38:00.000000000 +0300
+++ tiff-4.7.0-banan_os/config/config.sub 2025-06-11 12:55:18.797614388 +0300
@@ -1976,6 +1976,7 @@
| atheos* \
| auroraux* \
| aux* \
+ | banan_os* \
| beos* \
| bitrig* \
| bme* \

View File

@ -0,0 +1,72 @@
diff -ruN tiff-4.7.0/configure tiff-4.7.0-banan_os/configure
--- tiff-4.7.0/configure 2024-09-11 10:42:32.000000000 +0300
+++ tiff-4.7.0-banan_os/configure 2025-06-11 14:56:13.977026933 +0300
@@ -6312,6 +6312,10 @@
lt_cv_deplibs_check_method=pass_all
;;
+banan_os*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
beos*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -10492,6 +10496,11 @@
esac
;;
+ banan_os*)
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ link_all_deplibs=yes
+ ;;
+
beos*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
allow_undefined_flag=unsupported
@@ -12059,6 +12068,16 @@
esac
;;
+banan_os*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker="$host_os DynamicLoader.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
beos*)
library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"
@@ -14558,6 +14577,11 @@
fi
;;
+ banan_os*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+
beos*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
allow_undefined_flag_CXX=unsupported
@@ -16456,6 +16480,16 @@
esac
;;
+banan_os*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker="$host_os DynamicLoader.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
beos*)
library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"

19
ports/libwebp/build.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash ../install.sh
NAME='libwebp'
VERSION='1.5.0'
DOWNLOAD_URL="https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$VERSION.tar.gz#7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c"
DEPENDENCIES=('libpng' 'libjpeg' 'libtiff')
CONFIGURE_OPTIONS=(
"--with-pngincludedir=$BANAN_SYSROOT/usr/include"
"--with-pnglibdir=$BANAN_SYSROOT/usr/lib"
)
install() {
make install DESTDIR="$BANAN_SYSROOT" || exit 1
# remove libtool files
rm -f $BANAN_SYSROOT/usr/lib/libwebp.la
rm -f $BANAN_SYSROOT/usr/lib/libwebpdemux.la
rm -f $BANAN_SYSROOT/usr/lib/libwebpmux.la
}

View File

@ -0,0 +1,12 @@
diff -ruN libwebp-1.5.0/config.sub libwebp-1.5.0-banan_os/config.sub
--- libwebp-1.5.0/config.sub 2024-12-20 03:52:53.000000000 +0200
+++ libwebp-1.5.0-banan_os/config.sub 2025-06-11 12:46:06.199497350 +0300
@@ -1754,7 +1754,7 @@
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
- | fiwix* )
+ | fiwix* | banan_os* )
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)

View File

@ -0,0 +1,31 @@
diff -ruN libwebp-1.5.0/configure libwebp-1.5.0-banan_os/configure
--- libwebp-1.5.0/configure 2024-12-20 03:52:53.000000000 +0200
+++ libwebp-1.5.0-banan_os/configure 2025-06-11 15:06:04.232055731 +0300
@@ -6104,6 +6104,10 @@
lt_cv_deplibs_check_method=pass_all
;;
+banan_os*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
beos*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -11469,6 +11473,16 @@
esac
;;
+banan_os*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker="$host_os DynamicLoader.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
beos*)
library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"

54
ports/links/build.sh Executable file
View File

@ -0,0 +1,54 @@
#!/bin/bash ../install.sh
NAME='links'
VERSION='2.30'
DOWNLOAD_URL="http://links.twibright.com/download/links-$VERSION.tar.gz#7f0d54f4f7d1f094c25c9cbd657f98bc998311122563b1d757c9aeb1d3423b9e"
DEPENDENCIES=('ca-certificates' 'openssl' 'zlib' 'zstd' 'libpng' 'libjpeg' 'libtiff' 'libwebp')
post_configure() {
config_defines=(
'HAVE_PNG_CREATE_INFO_STRUCT'
'HAVE_PNG_GET_BIT_DEPTH'
'HAVE_PNG_GET_COLOR_TYPE'
'HAVE_PNG_GET_GAMA'
'HAVE_PNG_GET_IMAGE_HEIGHT'
'HAVE_PNG_GET_IMAGE_WIDTH'
'HAVE_PNG_GET_LIBPNG_VER'
'HAVE_PNG_GET_SRGB'
'HAVE_PNG_GET_VALID'
'HAVE_PNG_SET_RGB_TO_GRAY'
'HAVE_PNG_SET_STRIP_ALPHA'
'HAVE_PNG_H'
'HAVE_LIBPNG'
'HAVE_JPEGLIB_H'
'HAVE_LIBJPEG'
'HAVE_JPEG'
'HAVE_TIFFIO_H'
'HAVE_LIBTIFF'
'HAVE_TIFF'
'HAVE_WEBPDECODERGBA'
'HAVE_WEBPFREE'
'HAVE_WEBP_DECODE_H'
'HAVE_LIBWEBP'
'HAVE_WEBP'
'G'
)
for define in "${config_defines[@]}"; do
sed -i "s|^/\* #undef $define \*/$|#define $define 1|" config.h
done
echo '#define GRDRV_BANAN_OS 1' >> config.h
}
build() {
make -j$(nproc) -f Makefile.banan_os || exit 1
}
install() {
cp -v links "$BANAN_SYSROOT/usr/bin/" || exit 1
}

View File

@ -0,0 +1,444 @@
diff -ruN links-2.30/banan_os.cpp links-2.30-banan_os/banan_os.cpp
--- links-2.30/banan_os.cpp 1970-01-01 02:00:00.000000000 +0200
+++ links-2.30-banan_os/banan_os.cpp 2025-06-11 11:37:19.635667846 +0300
@@ -0,0 +1,392 @@
+#include "cfg.h"
+
+#ifdef GRDRV_BANAN_OS
+
+extern "C" {
+#include "links.h"
+#undef G
+}
+
+#include <BAN/UTF8.h>
+#include <LibGUI/Texture.h>
+#include <LibGUI/Window.h>
+
+extern "C" struct graphics_driver banan_os_driver;
+
+struct ban_driver_data
+{
+ BAN::UniqPtr<LibGUI::Window> window;
+ uint8_t buttons;
+ int last_x;
+ int last_y;
+};
+
+struct ban_bitmap_flags
+{
+ LibGUI::Texture texture;
+};
+
+static void ban_window_event(void* window)
+{
+ static_cast<LibGUI::Window*>(window)->poll_events();
+}
+
+static int ban_translate_key(LibInput::Key key, uint16_t modifier)
+{
+ switch (key)
+ {
+ case LibInput::Key::Enter: return KBD_ENTER;
+ case LibInput::Key::Backspace: return KBD_BS;
+ case LibInput::Key::Tab: return KBD_TAB;
+ case LibInput::Key::Escape: return KBD_ESC;
+ case LibInput::Key::ArrowLeft: return KBD_LEFT;
+ case LibInput::Key::ArrowRight: return KBD_RIGHT;
+ case LibInput::Key::ArrowUp: return KBD_UP;
+ case LibInput::Key::ArrowDown: return KBD_DOWN;
+ case LibInput::Key::Insert: return KBD_INS;
+ case LibInput::Key::Delete: return KBD_DEL;
+ case LibInput::Key::Home: return KBD_HOME;
+ case LibInput::Key::End: return KBD_END;
+ case LibInput::Key::PageUp: return KBD_PAGE_UP;
+ case LibInput::Key::PageDown: return KBD_PAGE_DOWN;
+ case LibInput::Key::F1: return KBD_F1;
+ case LibInput::Key::F2: return KBD_F2;
+ case LibInput::Key::F3: return KBD_F3;
+ case LibInput::Key::F4: return KBD_F4;
+ case LibInput::Key::F5: return KBD_F5;
+ case LibInput::Key::F6: return KBD_F6;
+ case LibInput::Key::F7: return KBD_F7;
+ case LibInput::Key::F8: return KBD_F8;
+ case LibInput::Key::F9: return KBD_F9;
+ case LibInput::Key::F10: return KBD_F10;
+ case LibInput::Key::F11: return KBD_F11;
+ case LibInput::Key::F12: return KBD_F12;
+ }
+
+ const char* utf8 = LibInput::key_to_utf8(key, modifier);
+ if (utf8 == nullptr)
+ return 0;
+
+ const uint32_t codepoint = BAN::UTF8::to_codepoint(utf8);
+ if (codepoint == BAN::UTF8::invalid)
+ return 0;
+
+ return codepoint;
+}
+
+static int ban_translate_mouse_button(LibInput::MouseButton button)
+{
+ switch (button)
+ {
+ case LibInput::MouseButton::Left: return B_LEFT;
+ case LibInput::MouseButton::Right: return B_RIGHT;
+ case LibInput::MouseButton::Middle: return B_MIDDLE;
+ case LibInput::MouseButton::Extra1: return B_FOURTH;
+ case LibInput::MouseButton::Extra2: return B_FIFTH;
+ }
+ return 0;
+}
+
+unsigned char* ban_init_driver(unsigned char* param, unsigned char* display)
+{
+ banan_os_driver.depth = 0xc4;
+ return nullptr;
+}
+
+static void ban_shutdown_driver(void)
+{
+}
+
+struct graphics_device* ban_init_device(void)
+{
+ auto window_attributes = LibGUI::Window::default_attributes;
+ window_attributes.resizable = true;
+
+ auto window_or_error = LibGUI::Window::create(600, 400, "Links"_sv, window_attributes);
+ if (window_or_error.is_error()) {
+ dwarnln("failed to create a window: {}", window_or_error.error());
+ return nullptr;
+ }
+
+ auto* device = new graphics_device;
+ if (device == nullptr)
+ return nullptr;
+ memset(device, 0, sizeof(graphics_device));
+
+ auto* driver_data = new ban_driver_data(window_or_error.release_value(), 0, 0, 0);
+ if (driver_data == nullptr) {
+ delete device;
+ return nullptr;
+ }
+
+ driver_data->window->texture().fill(0xFFFFFF);
+ driver_data->window->texture().set_bg_color(0xFFFFFF);
+ driver_data->window->invalidate();
+
+ device->driver_data = driver_data;
+ device->size.x1 = device->size.y1 = 0;
+ device->size.x2 = driver_data->window->width();
+ device->size.y2 = driver_data->window->height();
+ memcpy(&device->clip, &device->size, sizeof(struct rect));
+
+ driver_data->window->set_resize_window_event_callback(
+ [device, driver_data]()
+ {
+ if (device->resize_handler == nullptr)
+ return;
+ device->size.x2 = driver_data->window->width();
+ device->size.y2 = driver_data->window->height();
+ device->resize_handler(device);
+ }
+ );
+
+ driver_data->window->set_key_event_callback(
+ [device, driver_data](LibGUI::EventPacket::KeyEvent::event_t event)
+ {
+ if (!event.pressed())
+ return;
+ if (device->keyboard_handler == nullptr)
+ return;
+
+ int flags = 0;
+ if (event.shift()) flags |= KBD_SHIFT;
+ if (event.ctrl()) flags |= KBD_CTRL;
+ if (event.alt()) flags |= KBD_ALT;
+
+ const int key = ban_translate_key(event.key, event.modifier);
+ if (key == 0)
+ return;
+
+ device->keyboard_handler(device, key, flags);
+ }
+ );
+
+ driver_data->window->set_mouse_button_event_callback(
+ [device, driver_data](LibGUI::EventPacket::MouseButtonEvent::event_t event)
+ {
+ if (device->mouse_handler == nullptr)
+ return;
+ const int button = ban_translate_mouse_button(event.button);
+ if (event.pressed)
+ driver_data->buttons |= (1 << button);
+ else
+ driver_data->buttons &= ~(1 << button);
+ driver_data->last_x = event.x;
+ driver_data->last_y = event.y;
+ device->mouse_handler(device, event.x, event.y, (event.pressed ? B_DOWN : B_UP) | button);
+ }
+ );
+
+ driver_data->window->set_mouse_move_event_callback(
+ [device, driver_data](LibGUI::EventPacket::MouseMoveEvent::event_t event)
+ {
+ if (device->mouse_handler == nullptr)
+ return;
+
+ int buttons = B_MOVE;
+ if (driver_data->buttons) {
+ for (int i = 0; i < 8; i++) {
+ if (!(driver_data->buttons & (1 << i)))
+ continue;
+ buttons = B_DRAG | i;
+ break;
+ }
+ }
+
+ driver_data->last_x = event.x;
+ driver_data->last_y = event.y;
+ device->mouse_handler(device, event.x, event.y, buttons);
+ }
+ );
+
+ driver_data->window->set_mouse_scroll_event_callback(
+ [device, driver_data](LibGUI::EventPacket::MouseScrollEvent::event_t event)
+ {
+ if (device->mouse_handler == nullptr)
+ return;
+ device->mouse_handler(device, driver_data->last_x, driver_data->last_y, event.scroll < 0 ? B_WHEELDOWN : B_WHEELUP);
+ }
+ );
+
+ set_handlers(driver_data->window->server_fd(), ban_window_event, NULL, driver_data->window.ptr());
+
+ return device;
+}
+
+static void ban_shutdown_device(struct graphics_device* dev)
+{
+ auto* driver_data = static_cast<ban_driver_data*>(dev->driver_data);
+ set_handlers(driver_data->window->server_fd(), NULL, NULL, NULL);
+ delete driver_data;
+ delete dev;
+}
+
+static int ban_get_empty_bitmap(struct bitmap* dest)
+{
+ auto texture_or_error = LibGUI::Texture::create(dest->x, dest->y, 0xFFFFFF);
+ if (texture_or_error.is_error()) {
+ dwarnln("failed to create a texture: {}", texture_or_error.error());
+ return -1;
+ }
+
+ auto* flags = new ban_bitmap_flags(texture_or_error.release_value());
+ if (flags == nullptr)
+ return -1;
+
+ dest->flags = flags;
+ dest->data = flags->texture.pixels().data();
+ dest->skip = flags->texture.width() * 4;
+
+ return 0;
+}
+
+static void ban_register_bitmap(struct bitmap* bmp)
+{
+}
+
+static void ban_unregister_bitmap(struct bitmap* bmp)
+{
+ if (bmp->flags == nullptr)
+ return;
+ delete static_cast<ban_bitmap_flags*>(bmp->flags);
+ bmp->flags = nullptr;
+ bmp->data = 0;
+ bmp->skip = 0;
+}
+
+static void* ban_prepare_strip(struct bitmap* bmp, int top, int lines)
+{
+ if (bmp->flags == nullptr)
+ return nullptr;
+ return static_cast<uint8_t*>(bmp->data) + bmp->skip * top;
+}
+
+static void ban_commit_strip(struct bitmap* bmp, int top, int lines)
+{
+}
+
+static void ban_draw_bitmap(struct graphics_device* dev, struct bitmap* bmp, int x, int y)
+{
+ CLIP_DRAW_BITMAP
+ auto& window = *static_cast<ban_driver_data*>(dev->driver_data)->window;
+ auto& texture = static_cast<ban_bitmap_flags*>(bmp->flags)->texture;
+ window.texture().copy_texture(texture, x, y);
+ window.invalidate(x, y, texture.width(), texture.height());
+}
+
+static long ban_get_color(int rgb)
+{
+ return rgb;
+}
+
+static void ban_fill_area(struct graphics_device* dev, int x1, int y1, int x2, int y2, long color)
+{
+ CLIP_FILL_AREA
+ auto& window = *static_cast<ban_driver_data*>(dev->driver_data)->window;
+ window.texture().fill_rect(x1, y1, x2 - x1, y2 - y1, color);
+ window.invalidate(x1, y1, x2 - x1, y2 - y1);
+}
+
+static void ban_draw_hline(struct graphics_device* dev, int x1, int y, int x2, long color)
+{
+ CLIP_DRAW_HLINE
+ auto& window = *static_cast<ban_driver_data*>(dev->driver_data)->window;
+ for (int x = x1; x < x2; x++)
+ window.texture().set_pixel(x, y, color);
+ window.invalidate(x1, y, x2 - x1, 1);
+}
+
+static void ban_draw_vline(struct graphics_device* dev, int x, int y1, int y2, long color)
+{
+ CLIP_DRAW_VLINE
+ auto& window = *static_cast<ban_driver_data*>(dev->driver_data)->window;
+ for (int y = y1; y < y2; y++)
+ window.texture().set_pixel(x, y, color);
+ window.invalidate(x, y1, 1, y2 - y1);
+}
+
+static int ban_scroll(struct graphics_device* dev, struct rect_set* *set, int scx, int scy)
+{
+ const int dst_x = dev->clip.x1 + (scx >= 0 ? scx : 0);
+ const int dst_y = dev->clip.y1 + (scy >= 0 ? scy : 0);
+ const int dst_w = dev->clip.x2 + (scx < 0 ? scx : 0) - dst_x;
+ const int dst_h = dev->clip.y2 + (scy < 0 ? scy : 0) - dst_y;
+
+ const int src_x = dev->clip.x1 - (scx < 0 ? scx : 0);
+ const int src_y = dev->clip.y1 - (scy < 0 ? scy : 0);
+ const int src_w = dev->clip.x2 - (scx >= 0 ? scx : 0) - src_x;
+ const int src_h = dev->clip.y2 - (scy >= 0 ? scy : 0) - src_y;
+
+ auto& window = *static_cast<ban_driver_data*>(dev->driver_data)->window;
+ window.texture().copy_rect(
+ dst_x, dst_y,
+ src_x, src_y,
+ BAN::Math::min(dst_w, src_w),
+ BAN::Math::min(dst_h, src_h)
+ );
+ window.invalidate();
+
+ return 1;
+}
+
+static void ban_set_clip_area(struct graphics_device* dev)
+{
+ auto& window = *static_cast<ban_driver_data*>(dev->driver_data)->window;
+ window.texture().set_clip_area(
+ dev->clip.x1,
+ dev->clip.y1,
+ dev->clip.x2 - dev->clip.x1,
+ dev->clip.y2 - dev->clip.y1
+ );
+}
+
+static void ban_flush(struct graphics_device* dev)
+{
+}
+
+static void ban_set_title(struct graphics_device* dev, unsigned char* title)
+{
+ auto& driver_data = *static_cast<ban_driver_data*>(dev->driver_data);
+ driver_data.window->set_title(reinterpret_cast<const char*>(title));
+}
+
+struct graphics_driver banan_os_driver = {
+ (unsigned char* )"banan-os",
+ ban_init_driver,
+ ban_init_device,
+ ban_shutdown_device,
+ ban_shutdown_driver,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ban_get_empty_bitmap,
+ ban_register_bitmap,
+ ban_prepare_strip,
+ ban_commit_strip,
+ ban_unregister_bitmap,
+ ban_draw_bitmap,
+ ban_get_color,
+ ban_fill_area,
+ ban_draw_hline,
+ ban_draw_vline,
+ ban_scroll,
+ ban_set_clip_area,
+ ban_flush,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ban_set_title,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0, 0,
+ GD_UNICODE_KEYS,
+ NULL,
+};
+
+#endif
diff -ruN links-2.30/drivers.c links-2.30-banan_os/drivers.c
--- links-2.30/drivers.c 2019-04-27 21:00:25.000000000 +0300
+++ links-2.30-banan_os/drivers.c 2025-06-11 11:39:53.787842588 +0300
@@ -31,6 +31,9 @@
#ifdef GRDRV_ATHEOS
extern struct graphics_driver atheos_driver;
#endif
+#ifdef GRDRV_BANAN_OS
+extern struct graphics_driver banan_os_driver;
+#endif
#ifdef GRDRV_HAIKU
extern struct graphics_driver haiku_driver;
#endif
@@ -54,6 +57,9 @@
#ifdef GRDRV_ATHEOS
&atheos_driver,
#endif
+#ifdef GRDRV_BANAN_OS
+ &banan_os_driver,
+#endif
#ifdef GRDRV_HAIKU
&haiku_driver,
#endif
diff -ruN links-2.30/Makefile.banan_os links-2.30-banan_os/Makefile.banan_os
--- links-2.30/Makefile.banan_os 1970-01-01 02:00:00.000000000 +0200
+++ links-2.30-banan_os/Makefile.banan_os 2025-06-11 11:36:53.080046754 +0300
@@ -0,0 +1,21 @@
+OBJS=af_unix.o avif.o auth.o banan_os.o beos.o bfu.o block.o bookmark.o cache.o charsets.o compress.o connect.o cookies.o data.o default.o dip.o directfb.o dither.o dns.o doh.o dos.o drivers.o error.o file.o finger.o fn_impl.o fontconf.o font_inc.o framebuf.o freetype.o ftp.o gif.o grx.o hpux.o html.o html_gr.o html_r.o html_tbl.o http.o https.o img.o imgcache.o jpeg.o jsint.o kbd.o language.o listedit.o lru.o mailto.o main.o memory.o menu.o objreq.o os_dep.o pmshell.o png.o sched.o select.o session.o smb.o string.o suffix.o svg.o svgalib.o terminal.o tiff.o types.o url.o view.o view_gr.o vms.o webp.o x.o xbm.o
+
+CFLAGS=-g -O2 -DHAVE_CONFIG_H
+CXXFLAGS=$(CFLAGS) --std=c++20
+LIBS=-lgui -linput -lwebp -ltiff -ljpeg -lpng -lssl -lcrypto -lzstd -lz
+
+.PHONY: all
+all: links
+
+links: $(OBJS)
+ $(CXX) $(CFLAGS) -o links $(OBJS) $(LIBS)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
+
+.PHONY: clean
+clean:
+ rm -f $(OBJS)

View File

@ -32,9 +32,41 @@ namespace LibGUI
m_height = new_height;
m_pixels = BAN::move(pixels);
if (m_has_set_clip)
set_clip_area(m_clip_x, m_clip_y, m_clip_w, m_clip_h);
else
{
m_clip_w = new_width;
m_clip_h = new_height;
}
return {};
}
void Texture::set_clip_area(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
m_clip_x = 0;
m_clip_y = 0;
m_clip_w = this->width();
m_clip_h = this->height();
if (!clamp_to_texture(x, y, width, height))
{
m_clip_h = 0;
m_clip_w = 0;
}
else
{
m_clip_x = x;
m_clip_y = y;
m_clip_w = width;
m_clip_h = height;
}
m_has_set_clip = true;
}
void Texture::fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color)
{
if (!clamp_to_texture(x, y, width, height))
@ -44,15 +76,17 @@ namespace LibGUI
set_pixel(x + x_off, y + y_off, color);
}
void Texture::copy_texture(const Texture& texture, int32_t x, int32_t y)
void Texture::copy_texture(const Texture& texture, int32_t x, int32_t y, uint32_t sub_x, uint32_t sub_y, uint32_t width, uint32_t height)
{
uint32_t width = texture.width();
uint32_t height = texture.height();
if (!clamp_to_texture(x, y, width, height))
int32_t src_x = sub_x, src_y = sub_y;
if (!clamp_to_texture(x, y, src_x, src_y, width, height, texture))
return;
sub_x = src_x;
sub_y = src_y;
for (uint32_t y_off = 0; y_off < height; y_off++)
for (uint32_t x_off = 0; x_off < width; x_off++)
set_pixel(x + x_off, y + y_off, texture.get_pixel(x_off, y_off));
set_pixel(x + x_off, y + y_off, texture.get_pixel(sub_x + x_off, sub_y + y_off));
}
void Texture::draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
@ -132,9 +166,7 @@ namespace LibGUI
void Texture::copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height)
{
if (!clamp_to_texture(dst_x, dst_y, width, height))
return;
if (!clamp_to_texture(src_x, src_y, width, height))
if (!clamp_to_texture(dst_x, dst_y, src_x, src_y, width, height, *this))
return;
const bool copy_dir = dst_y < src_y;
@ -151,10 +183,10 @@ namespace LibGUI
bool Texture::clamp_to_texture(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const
{
const int32_t min_x = BAN::Math::max<int32_t>(signed_x, 0);
const int32_t min_y = BAN::Math::max<int32_t>(signed_y, 0);
const int32_t max_x = BAN::Math::min<int32_t>(this->width(), signed_x + (int32_t)width);
const int32_t max_y = BAN::Math::min<int32_t>(this->height(), signed_y + (int32_t)height);
const int32_t min_x = BAN::Math::max<int32_t>(signed_x, m_clip_x);
const int32_t min_y = BAN::Math::max<int32_t>(signed_y, m_clip_y);
const int32_t max_x = BAN::Math::min<int32_t>(signed_x + (int32_t)width, m_clip_x + m_clip_w);
const int32_t max_y = BAN::Math::min<int32_t>(signed_y + (int32_t)height, m_clip_y + m_clip_h);
if (min_x >= max_x)
return false;
@ -163,9 +195,63 @@ namespace LibGUI
signed_x = min_x;
signed_y = min_y;
width = max_x - min_x;
width = max_x - min_x;
height = max_y - min_y;
return true;
}
bool Texture::clamp_to_texture(int32_t& dst_x, int32_t& dst_y, int32_t& src_x, int32_t& src_y, uint32_t& width, uint32_t& height, const Texture& texture) const
{
if (width > texture.width())
width = texture.width();
if (height > texture.height())
height = texture.height();
if (dst_x >= static_cast<int32_t>(m_clip_x + m_clip_w))
return false;
if (dst_y >= static_cast<int32_t>(m_clip_y + m_clip_h))
return false;
if (src_x >= static_cast<int32_t>(texture.width()))
return false;
if (src_y >= static_cast<int32_t>(texture.height()))
return false;
if (dst_x + static_cast<int32_t>(width) > static_cast<int32_t>(m_clip_x + m_clip_w))
width = m_clip_x + m_clip_w - dst_x;
if (src_x + static_cast<int32_t>(width) > static_cast<int32_t>(texture.width()))
width = texture.width() - src_x;
if (dst_y + static_cast<int32_t>(height) > static_cast<int32_t>(m_clip_y + m_clip_h))
height = m_clip_y + m_clip_h - dst_y;
if (src_y + static_cast<int32_t>(height) > static_cast<int32_t>(texture.height()))
height = texture.height() - src_y;
int32_t off_x = 0;
if (dst_x < static_cast<int32_t>(m_clip_x))
off_x = m_clip_x - dst_x;
if (src_x + off_x < 0)
off_x = -src_x;
if (off_x >= static_cast<int32_t>(width))
return false;
int32_t off_y = 0;
if (dst_y < static_cast<int32_t>(m_clip_y))
off_y = m_clip_y - dst_y;
if (src_y + off_y < 0)
off_y = -src_y;
if (off_y >= static_cast<int32_t>(height))
return false;
dst_x += off_x;
src_x += off_x;
dst_y += off_y;
src_y += off_y;
width -= off_x;
height -= off_y;
return true;
}
}

View File

@ -13,6 +13,7 @@ namespace LibGUI
{
public:
static BAN::ErrorOr<Texture> create(uint32_t width, uint32_t height, uint32_t color);
Texture() = default;
BAN::ErrorOr<void> resize(uint32_t width, uint32_t height);
@ -20,6 +21,10 @@ namespace LibGUI
{
ASSERT(x < m_width);
ASSERT(y < m_height);
if (x < m_clip_x || x >= m_clip_x + m_clip_w)
return;
if (y < m_clip_y || y >= m_clip_y + m_clip_h)
return;
m_pixels[y * m_width + x] = color;
}
@ -32,25 +37,24 @@ namespace LibGUI
BAN::Span<uint32_t> pixels() { return m_pixels.span(); }
void set_clip_area(int32_t x, int32_t y, uint32_t width, uint32_t height);
void fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color);
void fill(uint32_t color) { return fill_rect(0, 0, width(), height(), color); }
void copy_texture(const Texture& texture, int32_t x, int32_t y);
void copy_texture(const Texture& texture, int32_t x, int32_t y, uint32_t sub_x = 0, uint32_t sub_y = 0, uint32_t width = -1, uint32_t height = -1);
void draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
void draw_text(BAN::StringView text, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
// shift whole vertically by amount pixels, sign determines the direction
// fill_color is used to fill "new" data
void shift_vertical(int32_t amount, uint32_t fill_color);
void shift_vertical(int32_t amount);
// copy horizontal slice [src_y, src_y + amount[ to [dst_y, dst_y + amount[
// fill_color is used when copying data outside of window bounds
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount, uint32_t fill_color);
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount);
// copy rect (src_x, src_y, width, height) to (dst_x, dst_y, width, height)
// fill_color is used when copying data outside of window bounds
void copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height, uint32_t fill_color);
void copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height);
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
@ -59,15 +63,19 @@ namespace LibGUI
void set_bg_color(uint32_t bg_color) { m_bg_color = bg_color; }
private:
Texture() = default;
Texture(BAN::Vector<uint32_t>&& pixels, uint32_t width, uint32_t height, uint32_t color)
: m_pixels(BAN::move(pixels))
, m_width(width)
, m_height(height)
, m_bg_color(color)
, m_clip_x(0)
, m_clip_y(0)
, m_clip_w(width)
, m_clip_h(height)
{}
bool clamp_to_texture(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const;
bool clamp_to_texture(int32_t& dst_x, int32_t& dst_y, int32_t& src_x, int32_t& src_y, uint32_t& width, uint32_t& height, const Texture&) const;
private:
BAN::Vector<uint32_t> m_pixels;
@ -75,6 +83,12 @@ namespace LibGUI
uint32_t m_height { 0 };
uint32_t m_bg_color { 0xFFFFFFFF };
uint32_t m_clip_x { 0 };
uint32_t m_clip_y { 0 };
uint32_t m_clip_w { 0 };
uint32_t m_clip_h { 0 };
bool m_has_set_clip { false };
friend class Window;
};

View File

@ -4,6 +4,7 @@
#include "TokenParser.h"
#include <BAN/HashSet.h>
#include <BAN/ScopeGuard.h>
#include <stdio.h>
@ -659,12 +660,13 @@ BAN::ErrorOr<void> TokenParser::run(BAN::Vector<Token>&& tokens)
{
TRY(feed_tokens(BAN::move(tokens)));
BAN::ScopeGuard _([this] {
m_token_stream.clear();
});
auto command_tree = TRY(parse_command_tree());
const auto token_type = peek_token().type();
while (!m_token_stream.empty())
m_token_stream.pop();
if (token_type != Token::Type::EOF_)
return unexpected_token_error(token_type);

View File

@ -0,0 +1,14 @@
set(SOURCES
main.cpp
)
add_executable(test ${SOURCES})
banan_link_library(test ban)
install(TARGETS test OPTIONAL)
install(CODE "
if (EXISTS ${CMAKE_INSTALL_BINDIR}/test)
file(COPY_FILE ${CMAKE_INSTALL_BINDIR}/test ${CMAKE_INSTALL_BINDIR}/[ ONLY_IF_DIFFERENT)
endif()
")

View File

@ -0,0 +1,247 @@
#include <BAN/Optional.h>
#include <BAN/Span.h>
#include <BAN/StringView.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
const char* argv0 = nullptr;
[[noreturn]] void exit_on_error(const char* format, ...)
{
fprintf(stderr, "%s: ", argv0);
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(2);
__builtin_unreachable();
}
long long parse_integer(const char* string)
{
errno = 0;
char* endptr;
long long value = strtoll(string, &endptr, 0);
if (*endptr == '\0' && errno == 0)
return value;
exit_on_error("integer expression expected, got %s\n", string);
}
bool check_file_mode(const char* pathname, mode_t mask, mode_t mode)
{
const auto func = (mode == S_IFLNK) ? lstat : stat;
struct stat st;
if (func(pathname, &st) == -1)
return false;
return (st.st_mode & mask) == mode;
}
bool check_file_not_empty(const char* pathname)
{
struct stat st;
if (stat(pathname, &st) == -1)
return false;
return st.st_size > 0;
}
BAN::Optional<bool> evaluate_file_op(BAN::Span<const char*>& args)
{
struct FileOp
{
char name;
bool (*func)(const char*);
};
constexpr FileOp file_ops[] {
{ 'b', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFBLK); } },
{ 'c', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFCHR); } },
{ 'd', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFDIR); } },
{ 'e', [](auto* s) { return check_file_mode(s, 0, 0 ); } },
{ 'f', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFREG); } },
{ 'g', [](auto* s) { return check_file_mode(s, S_ISGID, S_ISGID); } },
{ 'h', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFLNK); } },
{ 'L', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFLNK); } },
{ 'p', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFIFO); } },
{ 'S', [](auto* s) { return check_file_mode(s, S_IFMT, S_IFSOCK); } },
{ 'u', [](auto* s) { return check_file_mode(s, S_ISUID, S_ISUID); } },
{ 's', [](auto* s) { return check_file_not_empty(s); } },
{ 'r', [](auto* s) { return access(s, R_OK) == 0; } },
{ 'w', [](auto* s) { return access(s, W_OK) == 0; } },
{ 'x', [](auto* s) { return access(s, X_OK) == 0; } },
};
if (args.size() < 2)
return {};
if (args[0][0] != '-' || args[0][1] == '\0' || args[0][2] != '\0')
return {};
for (const auto& file_op : file_ops)
{
if (args[0][1] != file_op.name)
continue;
const char* pathname = args[1];
args = args.slice(2);
return file_op.func(pathname);
}
return {};
}
BAN::Optional<bool> evaluate_string_op(BAN::Span<const char*>& args)
{
if (args.size() < 3)
return {};
if (args[1] != "="_sv && args[1] != "!="_sv)
return {};
const bool result = (args[1] == "="_sv) == (strcmp(args[0], args[2]) == 0);
args = args.slice(3);
return result;
}
BAN::Optional<bool> evaluate_numeric_op(BAN::Span<const char*>& args)
{
if (args.size() < 3)
return {};
struct NumericOp
{
BAN::StringView name;
bool (*func)(long long, long long);
};
constexpr NumericOp numeric_ops[] {
{ "-eq", [](auto val1, auto val2) { return val1 == val2; } },
{ "-ne", [](auto val1, auto val2) { return val1 != val2; } },
{ "-gt", [](auto val1, auto val2) { return val1 > val2; } },
{ "-ge", [](auto val1, auto val2) { return val1 >= val2; } },
{ "-lt", [](auto val1, auto val2) { return val1 < val2; } },
{ "-le", [](auto val1, auto val2) { return val1 <= val2; } },
};
for (const auto& numeric_op : numeric_ops)
{
if (args[1] != numeric_op.name)
continue;
auto val1 = parse_integer(args[0]);
auto val2 = parse_integer(args[2]);
args = args.slice(3);
return numeric_op.func(val1, val2);
}
return {};
}
bool evaluate(BAN::Span<const char*>& args);
bool evaluate_expression(BAN::Span<const char*>& args)
{
if (args.empty())
return false;
if (args.size() == 1 || args[1] == "-o"_sv || args[1] == "-a"_sv)
{
const bool result = (args[0] != ""_sv);
args = args.slice(1);
return result;
}
// the string comparison binary primaries '=' and "!=" shall have a higher
// precedence than any unary primary
if (auto result = evaluate_string_op(args); result.has_value())
return false;
if (args[0] == "!"_sv)
{
args = args.slice(1);
return !evaluate_expression(args);
}
if (args[0] == "-z"_sv || args[0] == "-n"_sv)
{
const bool want_empty = (args[0] == "-z"_sv);
const bool is_empty = (args[1] == ""_sv);
args = args.slice(2);
return want_empty == is_empty;
}
if (args[0] == "-t"_sv)
{
auto value = parse_integer(args[1]);
args = args.slice(2);
if (value < 0 || value > INT_MAX)
return false;
return isatty(value);
}
if (auto result = evaluate_file_op(args); result.has_value())
return result.value();
if (auto result = evaluate_numeric_op(args); result.has_value())
return result.value();
if (args[0] == "("_sv)
{
args = args.slice(1);
const bool value = evaluate(args);
if (args.empty() || args[0] != ")"_sv)
exit_on_error("missing ')'\n");
args = args.slice(1);
return value;
}
const bool result = args[0] != ""_sv;
args = args.slice(1);
return result;
}
bool evaluate(BAN::Span<const char*>& args)
{
bool value = evaluate_expression(args);
while (!args.empty())
{
// NOTE: POSIX says -a has higher precedence than -o, but other
// implementations just do it as left associative.
// Even linux man page says: 'Binary -a and -o are ambiguous.'
if (args[0] != "-o"_sv && args[0] != "-a"_sv)
break;
const bool op_and = (args[0] == "-a"_sv);
args = args.slice(1);
const bool rhs = evaluate_expression(args);
value = op_and ? (value && rhs) : (value || rhs);
}
return value;
}
int main(int argc, const char** argv)
{
argv0 = argv[0];
char argv0_copy[PATH_MAX];
strcpy(argv0_copy, argv0);
if (strcmp(basename(argv0_copy), "[") == 0)
{
if (strcmp(argv[argc - 1], "]") != 0)
exit_on_error("missing ']'\n");
argc--;
}
auto args = BAN::Span(argv + 1, argc - 1);
const bool result = evaluate(args);
if (!args.empty())
exit_on_error("parse error near '%s'\n", args[0]);
return result ? 0 : 1;
}