diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 3b32fa94..ebf67918 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -35,268 +35,289 @@ if (DOXYGEN_FOUND) ${CMAKE_SOURCE_DIR}/LICENSE.md ${CMAKE_SOURCE_DIR}/doc/signal.md ${CMAKE_SOURCE_DIR}/doc/syscall.md - - ${CMAKE_SOURCE_DIR}/mentos/inc/boot.h - ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/gdt.h - ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/idt.h - ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/isr.h - ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/tss.h - ${CMAKE_SOURCE_DIR}/mentos/inc/devices/fpu.h - ${CMAKE_SOURCE_DIR}/mentos/inc/devices/pci.h - ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/ata/ata.h - ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/ata/ata_types.h + + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/rtc.h + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/ps2.h ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/fdc.h - ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/keyboard/keyboard.h - ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/keyboard/keymap.h ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/mouse.h - ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/ps2.h - ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/rtc.h + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/mem.h + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/ata/ata_types.h + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/ata/ata.h + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/keyboard/keymap.h + ${CMAKE_SOURCE_DIR}/mentos/inc/drivers/keyboard/keyboard.h ${CMAKE_SOURCE_DIR}/mentos/inc/elf/elf.h - ${CMAKE_SOURCE_DIR}/mentos/inc/fs/ext2.h - ${CMAKE_SOURCE_DIR}/mentos/inc/fs/ioctl.h - ${CMAKE_SOURCE_DIR}/mentos/inc/fs/procfs.h - ${CMAKE_SOURCE_DIR}/mentos/inc/fs/vfs.h - ${CMAKE_SOURCE_DIR}/mentos/inc/fs/vfs_types.h + ${CMAKE_SOURCE_DIR}/mentos/inc/multiboot.h + ${CMAKE_SOURCE_DIR}/mentos/inc/process/scheduler_feedback.h + ${CMAKE_SOURCE_DIR}/mentos/inc/process/prio.h + ${CMAKE_SOURCE_DIR}/mentos/inc/process/process.h + ${CMAKE_SOURCE_DIR}/mentos/inc/process/wait.h + ${CMAKE_SOURCE_DIR}/mentos/inc/process/scheduler.h + ${CMAKE_SOURCE_DIR}/mentos/inc/version.h + ${CMAKE_SOURCE_DIR}/mentos/inc/proc_access.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/gfp.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/zone_allocator.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/paging.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/slab.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/kheap.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/vmem_map.h + ${CMAKE_SOURCE_DIR}/mentos/inc/mem/buddysystem.h + ${CMAKE_SOURCE_DIR}/mentos/inc/hardware/timer.h ${CMAKE_SOURCE_DIR}/mentos/inc/hardware/cpuid.h ${CMAKE_SOURCE_DIR}/mentos/inc/hardware/pic8259.h - ${CMAKE_SOURCE_DIR}/mentos/inc/hardware/timer.h - ${CMAKE_SOURCE_DIR}/mentos/inc/io/proc_modules.h - ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga.h - ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_font.h - ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_mode.h - ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_palette.h - ${CMAKE_SOURCE_DIR}/mentos/inc/io/video.h ${CMAKE_SOURCE_DIR}/mentos/inc/kernel.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/compiler.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/hashmap.h + ${CMAKE_SOURCE_DIR}/mentos/inc/link_access.h + ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/gdt.h + ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/tss.h + ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/isr.h + ${CMAKE_SOURCE_DIR}/mentos/inc/descriptor_tables/idt.h + ${CMAKE_SOURCE_DIR}/mentos/inc/ipc/ipc.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/irqflags.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/list.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/list_head.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/mutex.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/ndtree.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/rbtree.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/spinlock.h - ${CMAKE_SOURCE_DIR}/mentos/inc/klib/stack_helper.h ${CMAKE_SOURCE_DIR}/mentos/inc/klib/stdatomic.h - ${CMAKE_SOURCE_DIR}/mentos/inc/link_access.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/buddysystem.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/gfp.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/kheap.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/paging.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/slab.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/vmem_map.h - ${CMAKE_SOURCE_DIR}/mentos/inc/mem/zone_allocator.h - ${CMAKE_SOURCE_DIR}/mentos/inc/multiboot.h - ${CMAKE_SOURCE_DIR}/mentos/inc/process/prio.h - ${CMAKE_SOURCE_DIR}/mentos/inc/process/process.h - ${CMAKE_SOURCE_DIR}/mentos/inc/process/scheduler.h - ${CMAKE_SOURCE_DIR}/mentos/inc/process/wait.h - ${CMAKE_SOURCE_DIR}/mentos/inc/proc_access.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/errno.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/ipc.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/module.h - ${CMAKE_SOURCE_DIR}/mentos/inc/sys/reboot.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/ndtree.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/stack_helper.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/mutex.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/compiler.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/rbtree.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/hashmap.h + ${CMAKE_SOURCE_DIR}/mentos/inc/klib/list.h + ${CMAKE_SOURCE_DIR}/mentos/inc/boot.h ${CMAKE_SOURCE_DIR}/mentos/inc/sys/types.h + ${CMAKE_SOURCE_DIR}/mentos/inc/sys/reboot.h + ${CMAKE_SOURCE_DIR}/mentos/inc/sys/module.h ${CMAKE_SOURCE_DIR}/mentos/inc/sys/utsname.h - ${CMAKE_SOURCE_DIR}/mentos/inc/system/panic.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_font.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_palette.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/vga/vga_mode.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/proc_modules.h + ${CMAKE_SOURCE_DIR}/mentos/inc/io/video.h + ${CMAKE_SOURCE_DIR}/mentos/inc/devices/pci.h + ${CMAKE_SOURCE_DIR}/mentos/inc/devices/fpu.h ${CMAKE_SOURCE_DIR}/mentos/inc/system/printk.h - ${CMAKE_SOURCE_DIR}/mentos/inc/system/signal.h + ${CMAKE_SOURCE_DIR}/mentos/inc/system/panic.h ${CMAKE_SOURCE_DIR}/mentos/inc/system/syscall.h - ${CMAKE_SOURCE_DIR}/mentos/inc/version.h + ${CMAKE_SOURCE_DIR}/mentos/inc/system/signal.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/vfs.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/ext2.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/namei.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/attr.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/procfs.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/vfs_types.h + ${CMAKE_SOURCE_DIR}/mentos/inc/fs/ioctl.h + ${CMAKE_SOURCE_DIR}/libc/inc/limits.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdbool.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdint.h + ${CMAKE_SOURCE_DIR}/libc/inc/time.h + ${CMAKE_SOURCE_DIR}/libc/inc/bits/termios-struct.h + ${CMAKE_SOURCE_DIR}/libc/inc/bits/ioctls.h + ${CMAKE_SOURCE_DIR}/libc/inc/bits/stat.h + ${CMAKE_SOURCE_DIR}/libc/inc/shadow.h + ${CMAKE_SOURCE_DIR}/libc/inc/fcvt.h + ${CMAKE_SOURCE_DIR}/libc/inc/string.h + ${CMAKE_SOURCE_DIR}/libc/inc/pwd.h + ${CMAKE_SOURCE_DIR}/libc/inc/readline.h + ${CMAKE_SOURCE_DIR}/libc/inc/crypt/sha256.h + ${CMAKE_SOURCE_DIR}/libc/inc/termios.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdlib.h + ${CMAKE_SOURCE_DIR}/libc/inc/fcntl.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdio.h + ${CMAKE_SOURCE_DIR}/libc/inc/ring_buffer.h + ${CMAKE_SOURCE_DIR}/libc/inc/err.h + ${CMAKE_SOURCE_DIR}/libc/inc/stdarg.h + ${CMAKE_SOURCE_DIR}/libc/inc/libgen.h + ${CMAKE_SOURCE_DIR}/libc/inc/sched.h + ${CMAKE_SOURCE_DIR}/libc/inc/assert.h + ${CMAKE_SOURCE_DIR}/libc/inc/stddef.h + ${CMAKE_SOURCE_DIR}/libc/inc/signal.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/errno.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/shm.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/bitops.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/types.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/sem.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/reboot.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/msg.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/ipc.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/wait.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/list_head.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/kernel_levels.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/dirent.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/ioctl.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/mman.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/utsname.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/unistd.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/stat.h + ${CMAKE_SOURCE_DIR}/libc/inc/sys/list_head_algorithm.h + ${CMAKE_SOURCE_DIR}/libc/inc/grp.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/ansi_colors.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/mm_io.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/debug.h + ${CMAKE_SOURCE_DIR}/libc/inc/io/port_io.h + ${CMAKE_SOURCE_DIR}/libc/inc/ctype.h + ${CMAKE_SOURCE_DIR}/libc/inc/system/syscall_types.h + ${CMAKE_SOURCE_DIR}/libc/inc/array.h + ${CMAKE_SOURCE_DIR}/libc/inc/strerror.h + ${CMAKE_SOURCE_DIR}/libc/inc/math.h - ${CMAKE_SOURCE_DIR}/mentos/src/boot.c - ${CMAKE_SOURCE_DIR}/mentos/src/crypt/sha256.c - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.c - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.c - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.c - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.c - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.c - ${CMAKE_SOURCE_DIR}/mentos/src/devices/fpu.c - ${CMAKE_SOURCE_DIR}/mentos/src/devices/pci.c - ${CMAKE_SOURCE_DIR}/mentos/src/drivers/ata.c + ${CMAKE_SOURCE_DIR}/mentos/src/drivers/mouse.c + ${CMAKE_SOURCE_DIR}/mentos/src/drivers/rtc.c + ${CMAKE_SOURCE_DIR}/mentos/src/drivers/mem.c ${CMAKE_SOURCE_DIR}/mentos/src/drivers/fdc.c - ${CMAKE_SOURCE_DIR}/mentos/src/drivers/keyboard/keyboard.c ${CMAKE_SOURCE_DIR}/mentos/src/drivers/keyboard/keymap.c - ${CMAKE_SOURCE_DIR}/mentos/src/drivers/mouse.c + ${CMAKE_SOURCE_DIR}/mentos/src/drivers/keyboard/keyboard.c ${CMAKE_SOURCE_DIR}/mentos/src/drivers/ps2.c - ${CMAKE_SOURCE_DIR}/mentos/src/drivers/rtc.c + ${CMAKE_SOURCE_DIR}/mentos/src/drivers/ata.c ${CMAKE_SOURCE_DIR}/mentos/src/elf/elf.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/ext2.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/ioctl.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/namei.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/open.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/procfs.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/readdir.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/read_write.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/stat.c - ${CMAKE_SOURCE_DIR}/mentos/src/fs/vfs.c - ${CMAKE_SOURCE_DIR}/mentos/src/hardware/cpuid.c - ${CMAKE_SOURCE_DIR}/mentos/src/hardware/pic8259.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler_feedback.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/process.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler_algorithm.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/wait.c + ${CMAKE_SOURCE_DIR}/mentos/src/crypt/sha256.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/zone_allocator.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/paging.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/buddysystem.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/kheap.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/slab.c + ${CMAKE_SOURCE_DIR}/mentos/src/mem/vmem_map.c ${CMAKE_SOURCE_DIR}/mentos/src/hardware/timer.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/debug.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/mm_io.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_running.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_system.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_video.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/stdio.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/vga/vga.c - ${CMAKE_SOURCE_DIR}/mentos/src/io/video.c - ${CMAKE_SOURCE_DIR}/mentos/src/ipc/msg.c + ${CMAKE_SOURCE_DIR}/mentos/src/hardware/pic8259.c + ${CMAKE_SOURCE_DIR}/mentos/src/hardware/cpuid.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.c + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.c ${CMAKE_SOURCE_DIR}/mentos/src/ipc/sem.c + ${CMAKE_SOURCE_DIR}/mentos/src/ipc/msg.c + ${CMAKE_SOURCE_DIR}/mentos/src/ipc/ipc.c ${CMAKE_SOURCE_DIR}/mentos/src/ipc/shm.c - ${CMAKE_SOURCE_DIR}/mentos/src/kernel/sys.c - ${CMAKE_SOURCE_DIR}/mentos/src/kernel.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/assert.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/ctype.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/stdlib.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/rbtree.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/vsprintf.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/fcvt.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/hashmap.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/vscanf.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/time.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/libgen.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/list.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/math.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/string.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/mutex.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/hashmap.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/ndtree.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/rbtree.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/spinlock.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/assert.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/list.c ${CMAKE_SOURCE_DIR}/mentos/src/klib/strerror.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/string.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/time.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/vscanf.c - ${CMAKE_SOURCE_DIR}/mentos/src/klib/vsprintf.c - ${CMAKE_SOURCE_DIR}/mentos/src/mem/buddysystem.c - ${CMAKE_SOURCE_DIR}/mentos/src/mem/kheap.c - ${CMAKE_SOURCE_DIR}/mentos/src/mem/paging.c - ${CMAKE_SOURCE_DIR}/mentos/src/mem/slab.c - ${CMAKE_SOURCE_DIR}/mentos/src/mem/vmem_map.c - ${CMAKE_SOURCE_DIR}/mentos/src/mem/zone_allocator.c - ${CMAKE_SOURCE_DIR}/mentos/src/multiboot.c - ${CMAKE_SOURCE_DIR}/mentos/src/process/process.c - ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler.c - ${CMAKE_SOURCE_DIR}/mentos/src/process/scheduler_algorithm.c - ${CMAKE_SOURCE_DIR}/mentos/src/process/wait.c - ${CMAKE_SOURCE_DIR}/mentos/src/sys/module.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/ctype.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/spinlock.c + ${CMAKE_SOURCE_DIR}/mentos/src/klib/math.c ${CMAKE_SOURCE_DIR}/mentos/src/sys/utsname.c + ${CMAKE_SOURCE_DIR}/mentos/src/sys/module.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/debug.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/mm_io.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/vga/vga.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/stdio.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/video.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_video.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_running.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_feedback.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_ipc.c + ${CMAKE_SOURCE_DIR}/mentos/src/io/proc_system.c + ${CMAKE_SOURCE_DIR}/mentos/src/kernel.c + ${CMAKE_SOURCE_DIR}/mentos/src/devices/pci.c + ${CMAKE_SOURCE_DIR}/mentos/src/devices/fpu.c + ${CMAKE_SOURCE_DIR}/mentos/src/boot.c + ${CMAKE_SOURCE_DIR}/mentos/src/system/signal.c ${CMAKE_SOURCE_DIR}/mentos/src/system/errno.c ${CMAKE_SOURCE_DIR}/mentos/src/system/panic.c - ${CMAKE_SOURCE_DIR}/mentos/src/system/printk.c - ${CMAKE_SOURCE_DIR}/mentos/src/system/signal.c ${CMAKE_SOURCE_DIR}/mentos/src/system/syscall.c - - ${CMAKE_SOURCE_DIR}/mentos/src/boot.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.S - ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.S - ${CMAKE_SOURCE_DIR}/mentos/src/process/user.S - - ${CMAKE_SOURCE_DIR}/libc/inc/array.h - ${CMAKE_SOURCE_DIR}/libc/inc/assert.h - ${CMAKE_SOURCE_DIR}/libc/inc/bits/ioctls.h - ${CMAKE_SOURCE_DIR}/libc/inc/bits/stat.h - ${CMAKE_SOURCE_DIR}/libc/inc/bits/termios-struct.h - ${CMAKE_SOURCE_DIR}/libc/inc/crypt/sha256.h - ${CMAKE_SOURCE_DIR}/libc/inc/ctype.h - ${CMAKE_SOURCE_DIR}/libc/inc/fcntl.h - ${CMAKE_SOURCE_DIR}/libc/inc/fcvt.h - ${CMAKE_SOURCE_DIR}/libc/inc/grp.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/ansi_colors.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/debug.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/mm_io.h - ${CMAKE_SOURCE_DIR}/libc/inc/io/port_io.h - ${CMAKE_SOURCE_DIR}/libc/inc/ipc/ipc.h - ${CMAKE_SOURCE_DIR}/libc/inc/ipc/msg.h - ${CMAKE_SOURCE_DIR}/libc/inc/ipc/sem.h - ${CMAKE_SOURCE_DIR}/libc/inc/ipc/shm.h - ${CMAKE_SOURCE_DIR}/libc/inc/libgen.h - ${CMAKE_SOURCE_DIR}/libc/inc/limits.h - ${CMAKE_SOURCE_DIR}/libc/inc/math.h - ${CMAKE_SOURCE_DIR}/libc/inc/pwd.h - ${CMAKE_SOURCE_DIR}/libc/inc/ring_buffer.h - ${CMAKE_SOURCE_DIR}/libc/inc/sched.h - ${CMAKE_SOURCE_DIR}/libc/inc/signal.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdarg.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdbool.h - ${CMAKE_SOURCE_DIR}/libc/inc/stddef.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdint.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdio.h - ${CMAKE_SOURCE_DIR}/libc/inc/stdlib.h - ${CMAKE_SOURCE_DIR}/libc/inc/strerror.h - ${CMAKE_SOURCE_DIR}/libc/inc/string.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/bitops.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/dirent.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/errno.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/ioctl.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/kernel_levels.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/reboot.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/stat.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/types.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/unistd.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/utsname.h - ${CMAKE_SOURCE_DIR}/libc/inc/sys/wait.h - ${CMAKE_SOURCE_DIR}/libc/inc/system/syscall_types.h - ${CMAKE_SOURCE_DIR}/libc/inc/termios.h - ${CMAKE_SOURCE_DIR}/libc/inc/time.h - - ${CMAKE_SOURCE_DIR}/libc/src/abort.c - ${CMAKE_SOURCE_DIR}/libc/src/assert.c - ${CMAKE_SOURCE_DIR}/libc/src/ctype.c - ${CMAKE_SOURCE_DIR}/libc/src/fcvt.c - ${CMAKE_SOURCE_DIR}/libc/src/grp.c - ${CMAKE_SOURCE_DIR}/libc/src/io/debug.c - ${CMAKE_SOURCE_DIR}/libc/src/io/mm_io.c - ${CMAKE_SOURCE_DIR}/libc/src/ipc/ipc.c + ${CMAKE_SOURCE_DIR}/mentos/src/system/printk.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/open.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/readdir.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/ext2.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/read_write.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/stat.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/ioctl.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/vfs.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/attr.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/procfs.c + ${CMAKE_SOURCE_DIR}/mentos/src/fs/namei.c + ${CMAKE_SOURCE_DIR}/mentos/src/multiboot.c + ${CMAKE_SOURCE_DIR}/mentos/src/kernel/sys.c + ${CMAKE_SOURCE_DIR}/libc/src/stdlib.c ${CMAKE_SOURCE_DIR}/libc/src/libc_start.c - ${CMAKE_SOURCE_DIR}/libc/src/libgen.c - ${CMAKE_SOURCE_DIR}/libc/src/math.c - ${CMAKE_SOURCE_DIR}/libc/src/pwd.c - ${CMAKE_SOURCE_DIR}/libc/src/sched.c ${CMAKE_SOURCE_DIR}/libc/src/setenv.c - ${CMAKE_SOURCE_DIR}/libc/src/stdio.c - ${CMAKE_SOURCE_DIR}/libc/src/stdlib.c - ${CMAKE_SOURCE_DIR}/libc/src/strerror.c - ${CMAKE_SOURCE_DIR}/libc/src/string.c - ${CMAKE_SOURCE_DIR}/libc/src/sys/errno.c - ${CMAKE_SOURCE_DIR}/libc/src/sys/ioctl.c - ${CMAKE_SOURCE_DIR}/libc/src/sys/unistd.c - ${CMAKE_SOURCE_DIR}/libc/src/sys/utsname.c - ${CMAKE_SOURCE_DIR}/libc/src/termios.c - ${CMAKE_SOURCE_DIR}/libc/src/time.c - ${CMAKE_SOURCE_DIR}/libc/src/crypt/sha256.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/chdir.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/close.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/creat.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/exec.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/signal.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/reboot.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/exit.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/fork.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getcwd.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getdents.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getgid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getpgid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getpid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/setuid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/open.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/kill.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/getppid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getsid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/getuid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/interval.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/kill.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/lseek.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/mkdir.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/nice.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/open.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/symlink.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getsid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/read.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/reboot.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/readlink.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/chmod.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/mkdir.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getdents.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/rmdir.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/setgid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/setpgid.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/setsid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/setuid.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/signal.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/stat.c - ${CMAKE_SOURCE_DIR}/libc/src/unistd/unlink.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getgid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/dup.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getpid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getuid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/exec.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getcwd.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/setpgid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/close.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/waitpid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/chown.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/creat.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/nice.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/lseek.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/setgid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/getpgid.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/chdir.c + ${CMAKE_SOURCE_DIR}/libc/src/unistd/unlink.c ${CMAKE_SOURCE_DIR}/libc/src/unistd/write.c - ${CMAKE_SOURCE_DIR}/libc/src/vscanf.c ${CMAKE_SOURCE_DIR}/libc/src/vsprintf.c + ${CMAKE_SOURCE_DIR}/libc/src/fcvt.c + ${CMAKE_SOURCE_DIR}/libc/src/abort.c + ${CMAKE_SOURCE_DIR}/libc/src/stdio.c + ${CMAKE_SOURCE_DIR}/libc/src/vscanf.c + ${CMAKE_SOURCE_DIR}/libc/src/time.c + ${CMAKE_SOURCE_DIR}/libc/src/err.c + ${CMAKE_SOURCE_DIR}/libc/src/crypt/sha256.c + ${CMAKE_SOURCE_DIR}/libc/src/pwd.c + ${CMAKE_SOURCE_DIR}/libc/src/libgen.c + ${CMAKE_SOURCE_DIR}/libc/src/string.c + ${CMAKE_SOURCE_DIR}/libc/src/assert.c + ${CMAKE_SOURCE_DIR}/libc/src/grp.c + ${CMAKE_SOURCE_DIR}/libc/src/shadow.c + ${CMAKE_SOURCE_DIR}/libc/src/sched.c + ${CMAKE_SOURCE_DIR}/libc/src/strerror.c + ${CMAKE_SOURCE_DIR}/libc/src/sys/mman.c + ${CMAKE_SOURCE_DIR}/libc/src/sys/errno.c + ${CMAKE_SOURCE_DIR}/libc/src/sys/ipc.c + ${CMAKE_SOURCE_DIR}/libc/src/sys/unistd.c + ${CMAKE_SOURCE_DIR}/libc/src/sys/ioctl.c + ${CMAKE_SOURCE_DIR}/libc/src/sys/utsname.c + ${CMAKE_SOURCE_DIR}/libc/src/ctype.c + ${CMAKE_SOURCE_DIR}/libc/src/readline.c + ${CMAKE_SOURCE_DIR}/libc/src/termios.c + ${CMAKE_SOURCE_DIR}/libc/src/io/debug.c + ${CMAKE_SOURCE_DIR}/libc/src/io/mm_io.c + ${CMAKE_SOURCE_DIR}/libc/src/math.c + ${CMAKE_SOURCE_DIR}/mentos/src/process/user.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/exception.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/idt.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/interrupt.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/gdt.S + ${CMAKE_SOURCE_DIR}/mentos/src/descriptor_tables/tss.S + ${CMAKE_SOURCE_DIR}/mentos/src/boot.S ${CMAKE_SOURCE_DIR}/libc/src/crt0.S ) endif (DOXYGEN_FOUND) \ No newline at end of file diff --git a/files/README b/files/README.md similarity index 90% rename from files/README rename to files/README.md index b3572567..102e9c1d 100644 --- a/files/README +++ b/files/README.md @@ -1,4 +1,4 @@ -MentOS 0.7.2 +# MentOS 0.7.2 Welcome to the MentOS, the Mentoring Operating System. diff --git a/files/home/user/README b/files/home/user/README deleted file mode 120000 index 3830a411..00000000 --- a/files/home/user/README +++ /dev/null @@ -1 +0,0 @@ -../../README \ No newline at end of file diff --git a/files/home/user/README.md b/files/home/user/README.md new file mode 120000 index 00000000..fe840054 --- /dev/null +++ b/files/home/user/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/libc/inc/err.h b/libc/inc/err.h index 0a60d3a5..803f0444 100644 --- a/libc/inc/err.h +++ b/libc/inc/err.h @@ -7,14 +7,51 @@ #include -/// @brief Print formatted error message on stderr and exit -/// @param eval The exit value. -/// @param fmt The format string. +/// @brief Print a formatted error message on stderr and exit the program. +/// +/// @details This function prints an error message to stderr, formatted +/// according to the given format string, followed by a system error message if +/// applicable. It then terminates the program with the specified exit value. +/// This is typically used when a system call fails. +/// +/// @param eval The exit value to use when terminating the program. +/// @param fmt The format string for the error message. void err(int eval, const char *fmt, ...); + +/// @brief Print a formatted error message on stderr using a va_list and exit +/// the program. +/// +/// @details This function is similar to `err()`, but accepts a `va_list` to +/// support variable arguments. This allows you to pass a list of arguments that +/// can be formatted into the error message. The program exits with the +/// specified exit value. +/// +/// @param eval The exit value to use when terminating the program. +/// @param fmt The format string for the error message. +/// @param args The variable argument list. void verr(int eval, const char *fmt, va_list args); -/// @brief Print formatted message on stderr without appending an error message and exit -/// @param eval The exit value. -/// @param fmt The format string. +/// @brief Print a formatted message on stderr without appending an error +/// message and exit. +/// +/// @details This function prints a formatted message to stderr without +/// appending a system error message (such as those related to errno). It then +/// terminates the program with the specified exit value. This is useful when +/// the error isn't related to a system call failure but requires exiting the +/// program. +/// +/// @param eval The exit value to use when terminating the program. +/// @param fmt The format string for the message. void errx(int eval, const char *fmt, ...); + +/// @brief Print a formatted message on stderr using a va_list and exit without +/// appending an error message. +/// +/// @details This function is similar to `errx()`, but accepts a `va_list` to +/// handle variable arguments. It prints the formatted message and exits the +/// program without appending a system error message. +/// +/// @param eval The exit value to use when terminating the program. +/// @param fmt The format string for the message. +/// @param args The variable argument list. void verrx(int eval, const char *fmt, va_list args); diff --git a/libc/inc/ring_buffer.h b/libc/inc/ring_buffer.h index 85b5ebcb..897d687a 100644 --- a/libc/inc/ring_buffer.h +++ b/libc/inc/ring_buffer.h @@ -1,127 +1,460 @@ /// @file ring_buffer.h -/// @brief +/// @brief This file provides a macro to declare a ring buffer with either fixed or +/// dynamic size, and also single or two-dimensional ring buffers. +/// @details +/// The ring buffer supports standard queue operations like pushing to the back +/// or front, popping from the front or back, and peeking at elements without +/// removing them. Additionally, it allows for customization through a +/// user-defined copy function, enabling flexible handling of how data is copied +/// between buffer entries. +/// +/// Each entry in the buffer consists of a one-dimensional array (second +/// dimension), and the buffer is designed to manage multiple such entries +/// (first dimension). A copy function can be provided at initialization, +/// allowing for custom behaviors during data insertion, such as deep copies or +/// specialized copying logic. This abstraction is useful for scenarios +/// requiring efficient circular buffer behavior while handling structured or +/// complex data types. +/// +/// The macro creates several key functions, including: +/// - Initialization of the ring buffer with a custom copy function. +/// - Push operations to add elements to the front or back of the buffer. +/// - Pop operations to remove elements from the front or back of the buffer. +/// - Peek operations to inspect elements without removal. +/// - Utility functions like checking whether the buffer is empty or full. +/// +/// Usage example for a 1D ring buffer: +/// ```c +/// #define DECLARE_FIXED_SIZE_RING_BUFFER(type, name, size, init_value) +/// DECLARE_FIXED_SIZE_RING_BUFFER(int, 1d_buffer, 10, 0); +/// +/// rb_1d_buffer_t buffer; +/// rb_1d_buffer_init(&buffer); +/// +/// // Push values to the buffer +/// int value = 5; +/// rb_1d_buffer_push_back(&buffer, value); +/// +/// // Pop a value from the front +/// int popped_value; +/// rb_1d_buffer_pop_front(&buffer, &popped_value); +/// ``` +/// +/// Usage example for a 2D ring buffer: +/// ```c +/// #define DECLARE_FIXED_SIZE_2D_RING_BUFFER(type, name, size1, size2, init_value, copy_func) +/// DECLARE_FIXED_SIZE_2D_RING_BUFFER(int, 2d_buffer, 10, 5, 0, NULL); +/// +/// rb_2d_buffer_t buffer; +/// rb_2d_buffer_init(&buffer); +/// +/// // Create an entry to push to the buffer +/// rb_2d_buffer_arr_t entry; +/// entry.size = 5; // Set the size for the second dimension +/// for (unsigned i = 0; i < entry.size; i++) { +/// entry.buffer[i] = i; // Fill the entry buffer +/// } +/// +/// // Push the entry to the buffer +/// rb_2d_buffer_push_back(&buffer, &entry); +/// +/// // Pop an entry from the front +/// rb_2d_buffer_pop_front(&buffer, &entry); +/// ``` +/// +/// This structure is suitable for applications that require a circular buffer for +/// multi-dimensional data, such as managing a history of states, logs, or streaming +/// data where array-based data must be efficiently stored and retrieved. +/// /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -/// @brief Declares a fixed-size ring-buffer. -#define DECLARE_FIXED_SIZE_RING_BUFFER(type, name, length, init) \ - typedef struct fs_rb_##name##_t { \ - unsigned size, read, write; \ - type buffer[length]; \ - } fs_rb_##name##_t; \ - static inline void fs_rb_##name##_init(fs_rb_##name##_t *rb) \ - { \ - rb->size = length; \ - rb->read = rb->write = 0; \ - char *dst = (char *)rb->buffer; \ - long num = sizeof(type) * length; \ - while (num--) *dst++ = (char)(init & 0xFF); \ - } \ - static inline unsigned fs_rb_##name##_step(fs_rb_##name##_t *rb, unsigned index) \ - { \ - return (index == (rb->size - 1)) ? 0 : index + 1; \ - } \ - static inline void fs_rb_##name##_push_front(fs_rb_##name##_t *rb, type item) \ - { \ - if (fs_rb_##name##_step(rb, rb->write) == rb->read) \ - rb->read = fs_rb_##name##_step(rb, rb->read); \ - rb->buffer[rb->write] = item; \ - rb->write = fs_rb_##name##_step(rb, rb->write); \ - } \ - static inline type fs_rb_##name##_empty(fs_rb_##name##_t *rb) \ - { \ - return rb->write == rb->read; \ - } \ - static inline type fs_rb_##name##_pop_back(fs_rb_##name##_t *rb) \ - { \ - type item = init; \ - if (!fs_rb_##name##_empty(rb)) { \ - item = rb->buffer[rb->read]; \ - rb->read = fs_rb_##name##_step(rb, rb->read); \ - } \ - return item; \ - } \ - static inline type fs_rb_##name##_pop_front(fs_rb_##name##_t *rb) \ - { \ - if (fs_rb_##name##_empty(rb)) \ - return init; \ - rb->write = (rb->write > 0) ? rb->write - 1 : rb->size - 1; \ - return rb->buffer[rb->write]; \ - } \ - static inline type fs_rb_##name##_get(fs_rb_##name##_t *rb, unsigned index) \ - { \ - if (index < rb->size) \ - return rb->buffer[index]; \ - return init; \ - } \ - static inline type fs_rb_##name##_back(fs_rb_##name##_t *rb) \ - { \ - if (fs_rb_##name##_empty(rb)) \ - return init; \ - return rb->buffer[rb->read]; \ - } \ - static inline type fs_rb_##name##_front(fs_rb_##name##_t *rb) \ - { \ - if (fs_rb_##name##_empty(rb)) \ - return init; \ - return rb->buffer[(rb->write > 0) ? rb->write - 1 : rb->size - 1]; \ +/// @brief Declares a fixed-size ring buffer. +#define DECLARE_FIXED_SIZE_RING_BUFFER(type, name, length, init) \ + typedef struct { \ + type buffer[length]; \ + unsigned size; \ + unsigned head; \ + unsigned tail; \ + unsigned count; \ + } rb_##name##_t; \ + \ + static inline void rb_##name##_init(rb_##name##_t *rb) \ + { \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + rb->size = length; \ + for (unsigned i = 0; i < length; i++) { \ + rb->buffer[i] = init; \ + } \ + } \ + \ + static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) \ + { \ + return rb->count == 0; \ + } \ + \ + static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) \ + { \ + return rb->count == rb->size; \ + } \ + \ + static inline void rb_##name##_push_back(rb_##name##_t *rb, type item) \ + { \ + rb->buffer[rb->head] = item; \ + if (rb_##name##_is_full(rb)) { \ + rb->tail = (rb->tail + 1) % rb->size; \ + } else { \ + rb->count++; \ + } \ + rb->head = (rb->head + 1) % rb->size; \ + } \ + \ + static inline void rb_##name##_push_front(rb_##name##_t *rb, type item) \ + { \ + if (rb_##name##_is_full(rb)) { \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + } else { \ + rb->count++; \ + } \ + rb->tail = (rb->tail == 0) ? rb->size - 1 : rb->tail - 1; \ + rb->buffer[rb->tail] = item; \ + } \ + \ + static inline type rb_##name##_pop_front(rb_##name##_t *rb) \ + { \ + type item = init; \ + if (!rb_##name##_is_empty(rb)) { \ + item = rb->buffer[rb->tail]; \ + rb->buffer[rb->tail] = init; \ + rb->tail = (rb->tail + 1) % rb->size; \ + rb->count--; \ + } \ + return item; \ + } \ + \ + static inline type rb_##name##_pop_back(rb_##name##_t *rb) \ + { \ + type item = init; \ + if (!rb_##name##_is_empty(rb)) { \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + item = rb->buffer[rb->head]; \ + rb->buffer[rb->head] = init; \ + rb->count--; \ + } \ + return item; \ + } \ + \ + static inline type rb_##name##_peek_front(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[rb->tail]; \ + } \ + \ + static inline type rb_##name##_peek_back(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[(rb->head == 0) ? rb->size - 1 : rb->head - 1]; \ + } \ + \ + static inline type rb_##name##_get(rb_##name##_t *rb, unsigned position) \ + { \ + type item = init; \ + if (!rb_##name##_is_empty(rb) && (position < rb->size)) { \ + item = rb->buffer[(rb->tail + position) % rb->size]; \ + } \ + return item; \ + } \ + \ + static inline void rb_##name##_iterate(rb_##name##_t *rb, \ + void (*callback)(type)) \ + { \ + for (unsigned i = 0; i < rb->count; i++) { \ + callback(rb_##name##_get(rb, i)); \ + } \ } -#ifdef __KERNEL__ -/// Function for allocating memory for the ring buffer. -#define RING_BUFFER_ALLOC kmalloc -/// Function for freeing the memory for the ring buffer. -#define RING_BUFFER_FREE kfree -#else -/// Function for allocating memory for the ring buffer. -#define RING_BUFFER_ALLOC malloc -/// Function for freeing the memory for the ring buffer. -#define RING_BUFFER_FREE free -#endif - /// @brief Declares a dynamic-size ring-buffer. -#define DECLARE_RING_BUFFER(type, name, init) \ - typedef struct rb_##name##_t { \ - const unsigned size; \ - unsigned read, write; \ - type *buffer; \ - } rb_##name##_t; \ - static inline rb_##name##_t alloc_rb_##name(unsigned len) \ - { \ - rb_##name##_t rb = { len, 0U, 0U, len > 0 ? RING_BUFFER_ALLOC(sizeof(type) * len) : NULL }; \ - memset(rb.buffer, init, sizeof(type) * len); \ - return rb; \ - } \ - static inline void free_rb_##name(rb_##name##_t *rb) \ - { \ - RING_BUFFER_FREE(rb->buffer); \ - } \ - static inline unsigned step_rb_##name(rb_##name##_t *rb, unsigned index) \ - { \ - return (index == (rb->size - 1)) ? 0 : index + 1; \ - } \ - static inline void push_rb_##name(rb_##name##_t *rb, type item) \ - { \ - if (step_rb_##name(rb, rb->write) == rb->read) \ - rb->read = step_rb_##name(rb, rb->read); \ - rb->buffer[rb->write] = item; \ - rb->write = step_rb_##name(rb, rb->write); \ - } \ - static inline void pop_rb_##name(rb_##name##_t *rb, type *item) \ - { \ - *item = init; \ - if (rb->write != rb->read) { \ - *item = rb->buffer[rb->read]; \ - rb->read = step_rb_##name(rb, rb->read); \ - } \ - } \ - static inline void get_rb_##name(rb_##name##_t *rb, unsigned index, type *item) \ - { \ - if (index < rb->size) \ - *item = rb->buffer[index]; \ +#define DECLARE_DYNAMIC_SIZE_RING_BUFFER(type, name, init) \ + typedef struct { \ + type *buffer; \ + unsigned size; \ + unsigned head; \ + unsigned tail; \ + unsigned count; \ + } rb_##name##_t; \ + \ + static inline int rb_##name##_init(rb_##name##_t *rb, unsigned length, \ + void *(*alloc_func)(size_t)) \ + { \ + rb->buffer = (type *)alloc_func(length * sizeof(type)); \ + if (!rb->buffer) { \ + return -1; /* Memory allocation failed */ \ + } \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + rb->size = length; \ + for (unsigned i = 0; i < length; i++) { \ + rb->buffer[i] = init; \ + } \ + return 0; /* Success */ \ + } \ + \ + static inline void rb_##name##_free(rb_##name##_t *rb, \ + void (*free_func)(void *)) \ + { \ + if (rb->buffer) { \ + free_func(rb->buffer); \ + } \ + rb->buffer = 0; \ + rb->size = 0; \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + } \ + \ + static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) \ + { \ + return rb->count == 0; \ + } \ + \ + static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) \ + { \ + return rb->count == rb->size; \ + } \ + \ + static inline void rb_##name##_push_back(rb_##name##_t *rb, type item) \ + { \ + rb->buffer[rb->head] = item; \ + if (rb_##name##_is_full(rb)) { \ + rb->tail = (rb->tail + 1) % rb->size; \ + } else { \ + rb->count++; \ + } \ + rb->head = (rb->head + 1) % rb->size; \ + } \ + \ + static inline void rb_##name##_push_front(rb_##name##_t *rb, type item) \ + { \ + if (rb_##name##_is_full(rb)) { \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + } else { \ + rb->count++; \ + } \ + rb->tail = (rb->tail == 0) ? rb->size - 1 : rb->tail - 1; \ + rb->buffer[rb->tail] = item; \ + } \ + \ + static inline type rb_##name##_pop_front(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + type item = rb->buffer[rb->tail]; \ + rb->buffer[rb->tail] = init; \ + rb->tail = (rb->tail + 1) % rb->size; \ + rb->count--; \ + return item; \ + } \ + \ + static inline type rb_##name##_pop_back(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + rb->head = (rb->head == 0) ? rb->size - 1 : rb->head - 1; \ + type item = rb->buffer[rb->head]; \ + rb->buffer[rb->head] = init; \ + rb->count--; \ + return item; \ + } \ + \ + static inline type rb_##name##_peek_front(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[rb->tail]; \ + } \ + \ + static inline type rb_##name##_peek_back(rb_##name##_t *rb) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return init; \ + } \ + return rb->buffer[(rb->head == 0) ? rb->size - 1 : rb->head - 1]; \ + } \ + \ + static inline type rb_##name##_get(rb_##name##_t *rb, unsigned position) \ + { \ + if (rb_##name##_is_empty(rb) || (position >= rb->size)) { \ + return init; \ + } \ + return rb->buffer[(rb->tail + position) % rb->size]; \ + } \ + \ + static inline void rb_##name##_iterate(rb_##name##_t *rb, \ + void (*callback)(type)) \ + { \ + for (unsigned i = 0; i < rb->count; i++) { \ + callback(rb_##name##_get(rb, i)); \ + } \ } -#undef RING_BUFFER_ALLOC -#undef RING_BUFFER_FREE +/// @brief Declares a fixed-size 2d ring buffer. +#define DECLARE_FIXED_SIZE_2D_RING_BUFFER(type, name, size1, size2, init) \ + typedef struct { \ + type buffer[size2]; \ + unsigned size; \ + } rb_##name##_entry_t; \ + \ + void rb_##name##_entry_default_copy_fun(type *dest, const type *src, \ + unsigned size) \ + { \ + for (unsigned i = 0; i < size; i++) { \ + dest[i] = src[i]; \ + } \ + } \ + \ + typedef struct { \ + rb_##name##_entry_t buffer[size1]; \ + unsigned size; \ + unsigned head; \ + unsigned tail; \ + unsigned count; \ + void (*copy)(type * dest, const type *src, unsigned); \ + } rb_##name##_t; \ + \ + static inline void rb_##name##_init_entry(rb_##name##_entry_t *entry) \ + { \ + entry->size = size2; \ + for (unsigned i = 0; i < size2; i++) { \ + entry->buffer[i] = init; \ + } \ + } \ + \ + static inline void rb_##name##_init( \ + rb_##name##_t *rb, \ + void (*copy_fun)(type * dest, const type *src, unsigned)) \ + { \ + rb->head = 0; \ + rb->tail = 0; \ + rb->count = 0; \ + rb->size = size1; \ + if (copy_fun) { \ + rb->copy = copy_fun; \ + } else { \ + rb->copy = rb_##name##_entry_default_copy_fun; \ + } \ + for (unsigned i = 0; i < size1; i++) { \ + rb_##name##_init_entry(rb->buffer + i); \ + } \ + } \ + \ + static inline unsigned rb_##name##_is_empty(rb_##name##_t *rb) \ + { \ + return rb->count == 0; \ + } \ + \ + static inline unsigned rb_##name##_is_full(rb_##name##_t *rb) \ + { \ + return rb->count == size1; \ + } \ + \ + static inline void rb_##name##_push_back(rb_##name##_t *rb, \ + rb_##name##_entry_t *item) \ + { \ + rb->copy(rb->buffer[rb->head].buffer, item->buffer, size2); \ + if (rb_##name##_is_full(rb)) { \ + rb->tail = (rb->tail + 1) % size1; \ + } else { \ + rb->count++; \ + } \ + rb->head = (rb->head + 1) % size1; \ + } \ + \ + static inline void rb_##name##_push_front(rb_##name##_t *rb, \ + rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_full(rb)) { \ + rb->head = (rb->head == 0) ? size1 - 1 : rb->head - 1; \ + } else { \ + rb->count++; \ + } \ + rb->tail = (rb->tail == 0) ? size1 - 1 : rb->tail - 1; \ + rb->copy(rb->buffer[rb->tail].buffer, item->buffer, size2); \ + } \ + \ + static inline int rb_##name##_pop_back(rb_##name##_t *rb, \ + rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + rb->head = (rb->head == 0) ? size1 - 1 : rb->head - 1; \ + rb->copy(item->buffer, rb->buffer[rb->head].buffer, size2); \ + rb->count--; \ + return 0; \ + } \ + \ + static inline int rb_##name##_pop_front(rb_##name##_t *rb, \ + rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + rb->copy(item->buffer, rb->buffer[rb->tail].buffer, size2); \ + rb->tail = (rb->tail + 1) % size1; \ + rb->count--; \ + return 0; \ + } \ + \ + static inline int rb_##name##_peek_back(rb_##name##_t *rb, \ + rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + unsigned index = ((rb->head == 0) ? size1 - 1 : rb->head - 1); \ + rb->copy(item->buffer, rb->buffer[index].buffer, size2); \ + return 0; \ + } \ + \ + static inline int rb_##name##_peek_front(rb_##name##_t *rb, \ + rb_##name##_entry_t *item) \ + { \ + if (rb_##name##_is_empty(rb)) { \ + return 1; \ + } \ + rb->copy(item->buffer, rb->buffer[rb->tail].buffer, size2); \ + return 0; \ + } \ + \ + static inline int rb_##name##_get(rb_##name##_t *rb, unsigned position, \ + rb_##name##_entry_t *item) \ + { \ + if (!rb_##name##_is_empty(rb) && (position < rb->count)) { \ + unsigned index = (rb->tail + position) % size1; \ + rb->copy(item->buffer, rb->buffer[index].buffer, size2); \ + return 1; \ + } \ + return 0; \ + } \ + \ + static inline void rb_##name##_iterate( \ + rb_##name##_t *rb, void (*callback)(rb_##name##_entry_t *)) \ + { \ + rb_##name##_entry_t item; \ + for (unsigned i = 0; i < rb->count; i++) { \ + rb_##name##_get(rb, i, &item); \ + callback(&item); \ + } \ + } diff --git a/libc/inc/shadow.h b/libc/inc/shadow.h index 5f6dd645..f39f5e4f 100644 --- a/libc/inc/shadow.h +++ b/libc/inc/shadow.h @@ -1,22 +1,52 @@ -// @file shadow.h -/// @brief Secret password file routines +/// @file shadow.h +/// @brief Defines structures and functions for working with the shadow password +/// file. +/// @copyright (c) 2014-2024 This file is distributed under the MIT License. +/// See LICENSE.md for details. + #pragma once #include "stddef.h" -#define SHADOW "/etc/shadow" +#define SHADOW "/etc/shadow" ///< Path to the shadow password file. +/// @brief Structure representing a shadow password record. +/// @details This structure is used to store details from the shadow password +/// file (`/etc/shadow`), including information such as the user's encrypted +/// password and password change policies. struct spwd { - char *sp_namp; ///< user login name. - char *sp_pwdp; ///< encrypted password. - long int sp_lstchg; ///< last password change. - long int sp_min; ///< days until change allowed. - long int sp_max; ///< days before change required. - long int sp_warn; ///< days warning for expiration. - long int sp_inact; ///< days before account inactive. - long int sp_expire; ///< date when account expires. - unsigned long int sp_flag; ///< reserved for future use. + char *sp_namp; ///< User login name. + char *sp_pwdp; ///< Encrypted password. + long int sp_lstchg; ///< Date of the last password change, in days since the epoch. + long int sp_min; ///< Minimum number of days until the next password change is allowed. + long int sp_max; ///< Maximum number of days before a password change is required. + long int sp_warn; ///< Number of days before the password expires to warn the user. + long int sp_inact; ///< Number of days after expiration until the account is considered inactive. + long int sp_expire; ///< Date when the account expires, in days since the epoch. + unsigned long int sp_flag; ///< Reserved for future use. }; -struct spwd *getspnam(const char *); -int getspnam_r(const char *, struct spwd *, char *, size_t, struct spwd **); +/// @brief Retrieves a shadow password record by username. +/// +/// @details This function retrieves the shadow password entry for a specific +/// user from the shadow password file (`/etc/shadow`). It uses a static buffer +/// to store the result, which is overwritten on each call. +/// +/// @param name The login name of the user to search for. +/// @return Pointer to the `spwd` structure with the user's shadow password entry, or NULL if not found. +struct spwd *getspnam(const char * name); + +/// @brief Retrieves a shadow password record by username (reentrant version). +/// +/// @details This function retrieves the shadow password entry for a specific +/// user in a reentrant manner. It stores the result in user-provided buffers to +/// avoid race conditions. This is the safer, thread-safe version of +/// `getspnam()`. +/// +/// @param name The login name of the user to search for. +/// @param spwd_buf Pointer to a user-provided `spwd` structure where the result will be stored. +/// @param buf Buffer to hold additional string data like the encrypted password. +/// @param buflen Size of the buffer provided. +/// @param result Pointer to the result. On success, this will point to `spwd_buf`, or NULL on failure. +/// @return 0 on success, or a non-zero error code on failure. +int getspnam_r(const char *name, struct spwd *spwd_buf, char *buf, size_t buflen, struct spwd **result); diff --git a/libc/inc/stdio.h b/libc/inc/stdio.h index c254f0c0..42e25ef1 100644 --- a/libc/inc/stdio.h +++ b/libc/inc/stdio.h @@ -82,6 +82,15 @@ int printf(const char *fmt, ...); /// On failure, a negative number is returned. int sprintf(char *str, const char *fmt, ...); +/// @brief Writes formatted output to `str`. +/// @param str The buffer where the formatted string will be placed. +/// @param size The size of the buffer. +/// @param fmt The format string, following the same specifications as printf. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written (excluding the null terminator) is returned. +/// On failure, a negative number is returned. +int snprintf(char *str, size_t size, const char *fmt, ...); + #ifndef __KERNEL__ /// @brief Write formatted output to a file. /// @param fd The file descriptor associated with the file. @@ -100,6 +109,14 @@ int fprintf(int fd, const char *fmt, ...); int vfprintf(int fd, const char *fmt, va_list args); #endif +/// @brief Formats a string and ensures buffer boundaries are respected. +/// @param str The output buffer where the formatted string will be stored. +/// @param size The maximum size of the output buffer. +/// @param fmt The format string. +/// @param args The argument list for the format specifiers. +/// @return int The number of characters written, excluding the null-terminator. +int vsnprintf(char *str, size_t size, const char *fmt, va_list args); + /// @brief Write formatted data from variable argument list to string. /// @param str Pointer to a buffer where the resulting C-string is stored. /// @param fmt Format string, following the same specifications as printf. diff --git a/libc/inc/sys/mman.h b/libc/inc/sys/mman.h index 0dc897f0..5d33fdd3 100644 --- a/libc/inc/sys/mman.h +++ b/libc/inc/sys/mman.h @@ -14,8 +14,6 @@ #define MAP_SHARED 0x01 ///< The memory is shared. #define MAP_PRIVATE 0x02 ///< The memory is private. -#ifndef __KERNEL__ - /// @brief creates a new mapping in the virtual address space of the calling process. /// @param addr the starting address for the new mapping. /// @param length specifies the length of the mapping (which must be greater than 0). @@ -31,23 +29,3 @@ void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) /// @param length the length of the mapped area. /// @return 0 on success, -1 on falure and errno is set. int munmap(void *addr, size_t length); - -#else - -/// @brief creates a new mapping in the virtual address space of the calling process. -/// @param addr the starting address for the new mapping. -/// @param length specifies the length of the mapping (which must be greater than 0). -/// @param prot describes the desired memory protection of the mapping (and must not conflict with the open mode of the file). -/// @param flags determines whether updates to the mapping are visible to other processes mapping the same region. -/// @param fd in case of file mapping, the file descriptor to use. -/// @param offset offset in the file, which must be a multiple of the page size PAGE_SIZE. -/// @return returns a pointer to the mapped area, -1 and errno is set. -void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); - -/// @brief deletes the mappings for the specified address range. -/// @param addr the starting address. -/// @param length the length of the mapped area. -/// @return 0 on success, -1 on falure and errno is set. -int sys_munmap(void *addr, size_t length); - -#endif diff --git a/libc/inc/sys/msg.h b/libc/inc/sys/msg.h index e7782109..68dad426 100644 --- a/libc/inc/sys/msg.h +++ b/libc/inc/sys/msg.h @@ -63,51 +63,6 @@ struct msqid_ds { pid_t msg_lrpid; }; -#ifdef __KERNEL__ - -/// @brief Initializes the message queue system. -/// @return 0 on success, 1 on failure. -int msq_init(void); - -/// @brief Get a System V message queue identifier. -/// @param key can be used either to obtain the identifier of a previously -/// created message queue, or to create a new set. -/// @param msgflg controls the behaviour of the function. -/// @return the message queue identifier, -1 on failure, and errno is set to -/// indicate the error. -int sys_msgget(key_t key, int msgflg); - -/// @brief Used to send messages. -/// @param msqid the message queue identifier. -/// @param msgp points to a used-defined msgbuf. -/// @param msgsz specifies the size in bytes of mtext. -/// @param msgflg specifies the action to be taken in case of specific events. -/// @return 0 on success, -1 on failure and errno is set to indicate the error. -int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); - -/// @brief Used to receive messages. -/// @param msqid the message queue identifier. -/// @param msgp points to a used-defined msgbuf. -/// @param msgsz specifies the size in bytes of mtext. -/// @param msgtyp specifies the type of message requested, as follows: -/// - msgtyp == 0: the first message on the queue is received. -/// - msgtyp > 0: the first message of type, msgtyp, is received. -/// - msgtyp < 0: the first message of the lowest type that is less than or -/// equal to the absolute value of msgtyp is received. -/// @param msgflg specifies the action to be taken in case of specific events. -/// @return the number of bytes actually copied on success, -1 on failure and -/// errno is set to indicate the error. -ssize_t sys_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); - -/// @brief Message queue control operations. -/// @param msqid the message queue identifier. -/// @param cmd The command to perform. -/// @param buf used with IPC_STAT and IPC_SET. -/// @return 0 on success, -1 on failure and errno is set to indicate the error. -int sys_msgctl(int msqid, int cmd, struct msqid_ds *buf); - -#else - /// @brief Get a System V message queue identifier. /// @param key can be used either to obtain the identifier of a previously /// created message queue, or to create a new set. @@ -144,5 +99,3 @@ ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); /// @param buf used with IPC_STAT and IPC_SET. /// @return 0 on success, -1 on failure and errno is set to indicate the error. int msgctl(int msqid, int cmd, struct msqid_ds *buf); - -#endif diff --git a/libc/inc/sys/sem.h b/libc/inc/sys/sem.h index ac37f64f..a6a6929d 100644 --- a/libc/inc/sys/sem.h +++ b/libc/inc/sys/sem.h @@ -26,8 +26,9 @@ #define SEM_STAT 18 ///< Return a semid_ds structure. #define SEM_INFO 19 ///< Return a seminfo structure. -/// }@ +/// @} +/// @brief Defines the maximum number of semaphores in a semaphore set. #define SEM_SET_MAX 256 /// @brief Optional argument for semctl() function @@ -76,38 +77,6 @@ struct sembuf { short sem_flg; }; -#ifdef __KERNEL__ - -/// @brief Initializes the semaphore system. -/// @return 0 on success, 1 on failure. -int sem_init(void); - -/// @brief Get a System V semaphore set identifier. -/// @param key can be used either to obtain the identifier of a previously -/// created semaphore set, or to create a new set. -/// @param nsems number of semaphores. -/// @param semflg controls the behaviour of the function. -/// @return the semaphore set identifier, -1 on failure, and errno is set to -/// indicate the error. -long sys_semget(key_t key, int nsems, int semflg); - -/// @brief Performs operations on selected semaphores in the set. -/// @param semid the semaphore set identifier. -/// @param sops specifies operations to be performed on single semaphores. -/// @param nsops number of operations. -/// @return 0 on success, -1 on failure and errno is set to indicate the error. -long sys_semop(int semid, struct sembuf *sops, unsigned nsops); - -/// @brief Performs control operations on a semaphore set. -/// @param semid the semaphore set identifier. -/// @param semnum the n-th semaphore of the set on which we perform the operations. -/// @param cmd the command to perform. -/// @param arg -/// @return 0 on success, -1 on failure and errno is set to indicate the error. -long sys_semctl(int semid, int semnum, int cmd, union semun *arg); - -#else - /// @brief Get a System V semaphore set identifier. /// @param key can be used either to obtain the identifier of a previously /// created semaphore set, or to create a new set. @@ -131,5 +100,3 @@ long semop(int semid, struct sembuf *sops, unsigned nsops); /// @param arg /// @return 0 on success, -1 on failure and errno is set to indicate the error. long semctl(int semid, int semnum, int cmd, union semun *arg); - -#endif diff --git a/libc/inc/sys/shm.h b/libc/inc/sys/shm.h index 00639eb9..d9ddf3e2 100644 --- a/libc/inc/sys/shm.h +++ b/libc/inc/sys/shm.h @@ -86,47 +86,6 @@ struct shmid_ds { #define SHM_REMAP 040000 ///< Take-over region on attach. #define SHM_EXEC 0100000 ///< Execution access. -#ifdef __KERNEL__ - -/// @brief Initializes the shared memory. -/// @return 0 on success, 1 on failure. -int shm_init(void); - -/// @brief Get a System V shared memory identifier. -/// @param key can be used either to obtain the identifier of a previously -/// created shared memory, or to create a new one. -/// @param size of the shared memory, rounded up to a multiple of PAGE_SIZE. -/// @param shmflg controls the behaviour of the function. -/// @return the shared memory identifier, -1 on failure, and errno is set to -/// indicate the error. -long sys_shmget(key_t key, size_t size, int shmflg); - -/// @brief Attaches the shared memory segment identified by shmid to the address -/// space of the calling process. -/// @param shmid the shared memory identifier. -/// @param shmaddr the attaching address. -/// @param shmflg controls the behaviour of the function. -/// @return returns the address of the attached shared memory segment; on error -/// (void *) -1 is returned, and errno is set to indicate the error. -void *sys_shmat(int shmid, const void *shmaddr, int shmflg); - -/// @brief Detaches the shared memory segment located at the address specified -/// by shmaddr from the address space of the calling process -/// @param shmaddr the address of the shared memory segment. -/// @return 0 on success, -1 on failure and errno is set to indicate the error. -long sys_shmdt(const void *shmaddr); - -/// @brief Performs the control operation specified by cmd on the shared memory -/// segment whose identifier is given in shmid. -/// @param shmid the shared memory identifier. -/// @param cmd the command to perform. -/// @param buf is a pointer to a shmid_ds structure. -/// @return a non-negative value basedon on the requested operation, -1 on -/// failure and errno is set to indicate the error. -long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf); - -#else - /// @brief Get a System V shared memory identifier. /// @param key can be used either to obtain the identifier of a previously /// created shared memory, or to create a new one. @@ -159,5 +118,3 @@ long shmdt(const void *shmaddr); /// @return a non-negative value basedon on the requested operation, -1 on /// failure and errno is set to indicate the error. long shmctl(int shmid, int cmd, struct shmid_ds *buf); - -#endif diff --git a/libc/src/crypt/sha256.c b/libc/src/crypt/sha256.c index 28e02f38..9addd98c 100644 --- a/libc/src/crypt/sha256.c +++ b/libc/src/crypt/sha256.c @@ -157,12 +157,6 @@ static inline void __sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) ctx->state[7] += h; } -/// @brief Converts a byte array into its hexadecimal string representation. -/// @param src Pointer to the source byte array. -/// @param src_length Length of the source byte array. -/// @param out Pointer to the output buffer for the hexadecimal string. -/// @param out_length Length of the output buffer (must be at least 2 * src_length + 1). -/// @details The output string will be null-terminated if the buffer is large enough. void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_length) { // Check if the output buffer is large enough to hold the hex string diff --git a/libc/src/shadow.c b/libc/src/shadow.c index 9ff32ce3..91c1c0e0 100644 --- a/libc/src/shadow.c +++ b/libc/src/shadow.c @@ -1,7 +1,8 @@ /// @file shadow.c -/// @brief +/// @brief Functions for handling the shadow password file (`/etc/shadow`). /// @copyright (c) 2005-2020 Rich Felker, et al. /// This file is based on the code from libmusl. + #include #include #include @@ -12,103 +13,126 @@ #include #include +/// Defines the buffer size for reading lines from the shadow file. +#define LINE_LIM 256 + +/// @brief Converts a string to a long integer. +/// +/// @details Parses a string into a long integer and advances the string pointer. +/// If the string starts with a colon or newline, returns -1. +/// +/// @param s Pointer to the string to convert. +/// @return The parsed long integer. static long xatol(char **s) { - long x; - if (**s == ':' || **s == '\n') return -1; - for (x=0; **s-'0'<10U; ++*s) x=10*x+(**s-'0'); - return x; + long x; + if (**s == ':' || **s == '\n') return -1; + for (x = 0; **s - '0' < 10U; ++*s) x = 10 * x + (**s - '0'); + return x; } +/// @brief Parses a shadow password entry from a string. +/// +/// @details This function parses a line from the shadow password file into a `spwd` structure. +/// The fields in the shadow password file are separated by colons. +/// +/// @param s The string containing the shadow password entry. +/// @param sp Pointer to the `spwd` structure where the parsed data will be stored. +/// @return 0 on success, -1 on failure. int __parsespent(char *s, struct spwd *sp) { - sp->sp_namp = s; - if (!(s = strchr(s, ':'))) return -1; - *s = 0; - - sp->sp_pwdp = ++s; - if (!(s = strchr(s, ':'))) return -1; - *s = 0; - - s++; sp->sp_lstchg = xatol(&s); - if (*s != ':') return -1; - - s++; sp->sp_min = xatol(&s); - if (*s != ':') return -1; - - s++; sp->sp_max = xatol(&s); - if (*s != ':') return -1; - - s++; sp->sp_warn = xatol(&s); - if (*s != ':') return -1; - - s++; sp->sp_inact = xatol(&s); - if (*s != ':') return -1; - - s++; sp->sp_expire = xatol(&s); - if (*s != ':') return -1; - - s++; sp->sp_flag = xatol(&s); - if (*s != '\n') return -1; - return 0; + sp->sp_namp = s; + if (!(s = strchr(s, ':'))) return -1; + *s = 0; + + sp->sp_pwdp = ++s; + if (!(s = strchr(s, ':'))) return -1; + *s = 0; + + s++; + sp->sp_lstchg = xatol(&s); + if (*s != ':') return -1; + + s++; + sp->sp_min = xatol(&s); + if (*s != ':') return -1; + + s++; + sp->sp_max = xatol(&s); + if (*s != ':') return -1; + + s++; + sp->sp_warn = xatol(&s); + if (*s != ':') return -1; + + s++; + sp->sp_inact = xatol(&s); + if (*s != ':') return -1; + + s++; + sp->sp_expire = xatol(&s); + if (*s != ':') return -1; + + s++; + sp->sp_flag = xatol(&s); + if (*s != '\n') return -1; + return 0; } -int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res) +struct spwd *getspnam(const char *name) { - char path[20+NAME_MAX]; - int rv = 0; - int fd; - size_t k, l = strlen(name); - int skip = 0; - int cs; - int orig_errno = errno; - - *res = 0; - - /* Disallow potentially-malicious user names */ - if (*name=='.' || strchr(name, '/') || !l) - return errno = EINVAL; - - /* Buffer size must at least be able to hold name, plus some.. */ - if (size < l+100) - return errno = ERANGE; - - fd = open(SHADOW, O_RDONLY, 0); - if (fd < 0) { - return errno; - } - - while (fgets(buf, size, fd) && (k=strlen(buf))>0) { - if (skip || strncmp(name, buf, l) || buf[l]!=':') { - skip = buf[k-1] != '\n'; - continue; - } - if (buf[k-1] != '\n') { - rv = ERANGE; - break; - } - - if (__parsespent(buf, sp) < 0) continue; - *res = sp; - break; - } - errno = rv ? rv : orig_errno; - return rv; + static struct spwd spwd_buf; + static char *line; + struct spwd *result; + int e; + int orig_errno = errno; + + if (!line) line = malloc(LINE_LIM); + if (!line) return 0; + e = getspnam_r(name, &spwd_buf, line, LINE_LIM, &result); + errno = e ? e : orig_errno; + return result; } -#define LINE_LIM 256 - -struct spwd *getspnam(const char *name) +int getspnam_r(const char *name, struct spwd *spwd_buf, char *buf, size_t buflen, struct spwd **result) { - static struct spwd sp; - static char *line; - struct spwd *res; - int e; - int orig_errno = errno; - - if (!line) line = malloc(LINE_LIM); - if (!line) return 0; - e = getspnam_r(name, &sp, line, LINE_LIM, &res); - errno = e ? e : orig_errno; - return res; + char path[20 + NAME_MAX]; + int rv = 0; + int fd; + size_t k, l = strlen(name); + int skip = 0; + int cs; + int orig_errno = errno; + + *result = 0; + + /* Disallow potentially-malicious user names */ + if (*name == '.' || strchr(name, '/') || !l) + return errno = EINVAL; + + /* Buffer size must at least be able to hold name, plus some.. */ + if (buflen < l + 100) + return errno = ERANGE; + + fd = open(SHADOW, O_RDONLY, 0); + if (fd < 0) { + return errno; + } + + while (fgets(buf, buflen, fd) && (k = strlen(buf)) > 0) { + if (skip || strncmp(name, buf, l) || buf[l] != ':') { + skip = buf[k - 1] != '\n'; + continue; + } + if (buf[k - 1] != '\n') { + rv = ERANGE; + break; + } + + if (__parsespent(buf, spwd_buf) < 0) continue; + *result = spwd_buf; + break; + } + errno = rv ? rv : orig_errno; + return rv; } diff --git a/libc/src/stdio.c b/libc/src/stdio.c index fd8be745..f9970ab8 100644 --- a/libc/src/stdio.c +++ b/libc/src/stdio.c @@ -23,7 +23,7 @@ void puts(const char *str) int getchar(void) { - char c; + char c = 0; while (read(STDIN_FILENO, &c, 1) == 0) { continue; } @@ -209,7 +209,7 @@ int fgetc(int fd) char *fgets(char *buf, int n, int fd) { int c; - char *p = buf; + char *p = buf; int count = n - 1; // Leave space for null terminator // Read characters until reaching the limit or newline diff --git a/libc/src/sys/ipc.c b/libc/src/sys/ipc.c index 2ac03097..58c04d46 100644 --- a/libc/src/sys/ipc.c +++ b/libc/src/sys/ipc.c @@ -5,19 +5,12 @@ #include "sys/ipc.h" -#include "io/debug.h" -#include "io/debug.h" -#include "stddef.h" -#include "stdio.h" -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "sys/errno.h" -#include "sys/msg.h" -#include "sys/sem.h" #include "sys/shm.h" +#include "sys/sem.h" +#include "sys/msg.h" +#include "sys/errno.h" #include "sys/stat.h" -#include "sys/unistd.h" +#include "io/debug.h" #include "system/syscall_types.h" _syscall3(void *, shmat, int, shmid, const void *, shmaddr, int, shmflg) diff --git a/libc/src/vsprintf.c b/libc/src/vsprintf.c index 1443ff78..8b2d7bb1 100644 --- a/libc/src/vsprintf.c +++ b/libc/src/vsprintf.c @@ -62,13 +62,14 @@ static inline int skip_atoi(const char **s) /// @brief Transforms the number into a string. /// @param str the output string. +/// @param end the end of the buffer to prevent overflow. /// @param num the number to transform to string. /// @param base the base to use for number transformation (e.g., 10 for decimal, 16 for hex). /// @param size the minimum size of the output string (pads with '0' or spaces if necessary). /// @param precision the precision for number conversion (affects floating point numbers and zero padding). /// @param flags control flags (e.g., for padding, sign, and case sensitivity). /// @return the resulting string after number transformation. -static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags) +static char *number(char *str, char *end, long num, int base, int size, int32_t precision, unsigned flags) { char tmp[66] = { 0 }; // Temporary buffer to hold the number in reverse order char *dig = _digits; // Default digits for base conversion (lowercase) @@ -141,21 +142,21 @@ static char *number(char *str, long num, int base, int size, int32_t precision, // Write padding spaces if right-aligned and no zero padding. if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { - while (size-- > 0) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } } // Write the sign character if necessary. - if (sign) { + if (sign && (end == NULL || str < end)) { *str++ = sign; } // Write the prefix for octal and hexadecimal if the FLAGS_HASH is set. if (bitmask_check(flags, FLAGS_HASH)) { - if (base == 8) { + if (base == 8 && (end == NULL || str < end)) { *str++ = '0'; // Octal prefix. - } else if (base == 16) { + } else if (base == 16 && (str + 1) < end) { *str++ = '0'; // Hexadecimal prefix "0x". *str++ = _digits[33]; // 'x' or 'X' based on FLAGS_UPPERCASE. } @@ -163,23 +164,23 @@ static char *number(char *str, long num, int base, int size, int32_t precision, // Write zero-padding if necessary. if (!bitmask_check(flags, FLAGS_LEFT)) { - while (size-- > 0) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = paddingc; // Pad with '0' or ' '. } } // Write any additional zeros required by the precision. - while (i < precision--) { + while (i < precision-- && (end == NULL || str < end)) { *str++ = '0'; } // Write the number in reverse order (tmp array holds the reversed digits). - while (i-- > 0) { + while (i-- > 0 && (end == NULL || str < end)) { *str++ = tmp[i]; } // If the number is left-aligned, pad remaining space with spaces. - while (size-- > 0) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } @@ -188,12 +189,13 @@ static char *number(char *str, long num, int base, int size, int32_t precision, /// @brief Converts a MAC address into a human-readable string format. /// @param str The output string where the MAC address will be written. +/// @param end The end of the buffer to prevent overflow. /// @param addr The 6-byte MAC address to be formatted. /// @param size The minimum field width for the output (pads with spaces if necessary). /// @param precision Unused in this function (for compatibility with similar functions). /// @param flags Control flags that affect the format (e.g., uppercase and left alignment). /// @return Pointer to the end of the output string. -static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +static char *eaddr(char *str, char *end, unsigned char *addr, int size, int precision, unsigned flags) { (void)precision; // Precision is unused in this function. @@ -210,27 +212,33 @@ static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsi for (i = 0; i < 6; i++) { // Add colon separator between address bytes. if (i != 0) { - tmp[len++] = ':'; + if (len < sizeof(tmp)) { + tmp[len++] = ':'; + } } - tmp[len++] = dig[addr[i] >> 4]; // Convert upper nibble to hex. - tmp[len++] = dig[addr[i] & 0x0F]; // Convert lower nibble to hex. + if (len < sizeof(tmp)) { + tmp[len++] = dig[addr[i] >> 4]; // Convert upper nibble to hex. + } + if (len < sizeof(tmp)) { + tmp[len++] = dig[addr[i] & 0x0F]; // Convert lower nibble to hex. + } } // Handle right alignment if the FLAGS_LEFT flag is not set. if (!bitmask_check(flags, FLAGS_LEFT)) { - while (len < size--) { + while (len < size-- && (end == NULL || str < end)) { *str++ = ' '; // Pad with spaces on the left if needed. } } // Copy the formatted MAC address from tmp buffer to the output string. - for (i = 0; i < len; ++i) { + for (i = 0; i < len && (end == NULL || str < end); ++i) { *str++ = tmp[i]; } // Handle left padding if the FLAGS_LEFT flag is set. - while (len < size--) { + while (len < size-- && (end == NULL || str < end)) { *str++ = ' '; // Pad with spaces on the right if needed. } @@ -239,12 +247,13 @@ static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsi /// @brief Converts an IPv4 address into a human-readable string format. /// @param str The output string where the IPv4 address will be written. +/// @param end The end of the buffer to prevent overflow. /// @param addr The 4-byte IPv4 address to be formatted. /// @param size The minimum field width for the output (pads with spaces if necessary). /// @param precision Unused in this function (for compatibility with similar functions). /// @param flags Control flags that affect the format (e.g., left alignment). /// @return Pointer to the end of the output string. -static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +static char *iaddr(char *str, char *end, unsigned char *addr, int size, int precision, unsigned flags) { (void)precision; // Precision is unused in this function. @@ -255,7 +264,7 @@ static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsi // Convert each byte of the IP address to decimal format. for (i = 0; i < 4; i++) { // Add a dot between each octet. - if (i != 0) { + if (i != 0 && len < sizeof(tmp)) { tmp[len++] = '.'; } @@ -265,42 +274,43 @@ static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsi // Convert the current octet to decimal digits. if (n == 0) { // Handle the case where the octet is zero. - tmp[len++] = _digits[0]; + if (len < sizeof(tmp)) { + tmp[len++] = _digits[0]; + } } else { // If the number is greater than or equal to 100, we need to extract // hundreds, tens, and units. - if (n >= 100) { + if (n >= 100 && len < sizeof(tmp)) { tmp[len++] = _digits[n / 100]; // Hundreds place. n = n % 100; - tmp[len++] = _digits[n / 10]; // Tens place. - n = n % 10; - } else if (n >= 10) { - // If the number is between 10 and 99, we only need tens and units. + } + if (n >= 10 && len < sizeof(tmp)) { tmp[len++] = _digits[n / 10]; // Tens place. n = n % 10; } - // Finally, add the unit digit. - tmp[len++] = _digits[n]; + if (len < sizeof(tmp)) { + tmp[len++] = _digits[n]; + } } } // Handle right alignment if the FLAGS_LEFT flag is not set. if (!bitmask_check(flags, FLAGS_LEFT)) { // Pad with spaces on the left if needed. - while (len < size--) { + while (len < size-- && (end == NULL || str < end)) { *str++ = ' '; } } // Copy the formatted IP address from tmp buffer to the output string. - for (i = 0; i < len; ++i) { + for (i = 0; i < len && (end == NULL || str < end); ++i) { *str++ = tmp[i]; } // Handle left padding if the FLAGS_LEFT flag is set. Pad with spaces on the // right if needed. - while (len < size--) { + while (len < size-- && (end == NULL || str < end)) { *str++ = ' '; } @@ -310,10 +320,11 @@ static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsi /// @brief Converts a floating-point number to a string with a specified format. /// @param value The floating-point value to be converted. /// @param buffer The output buffer to store the resulting string. +/// @param bufsize The size of the output buffer. /// @param fmt The format specifier ('e', 'f', or 'g'). /// @param precision The number of digits to be displayed after the decimal /// point. -static void cfltcvt(double value, char *buffer, char fmt, int precision) +static void cfltcvt(double value, char *buffer, size_t bufsize, char fmt, int precision) { int decpt, sign, exp, pos; char cvtbuf[CVTBUFSIZE]; // Temporary buffer to store the digits. @@ -321,6 +332,8 @@ static void cfltcvt(double value, char *buffer, char fmt, int precision) int capexp = 0; // Flag to check for uppercase exponent. int magnitude; + char *end = buffer + bufsize - 1; // Pointer to the end of the buffer. + // Handle uppercase 'G' or 'E' format specifier. // Convert them to lowercase 'g' or 'e' for uniform processing. if (fmt == 'G' || fmt == 'E') { @@ -349,24 +362,29 @@ static void cfltcvt(double value, char *buffer, char fmt, int precision) ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE); // Add the sign to the output buffer if necessary. - if (sign) { + if (sign && buffer < end) { *buffer++ = '-'; } // Add the first digit. - *buffer++ = *digits; + if (buffer < end) { + *buffer++ = *digits; + } // Add the decimal point and remaining digits. - if (precision > 0) { + if (precision > 0 && buffer < end) { *buffer++ = '.'; } // Copy the remaining digits. - memcpy(buffer, digits + 1, precision); - buffer += precision; + for (int i = 1; i <= precision && buffer < end; i++) { + *buffer++ = digits[i]; + } // Add the exponent character ('e' or 'E'). - *buffer++ = capexp ? 'E' : 'e'; + if (buffer < end) { + *buffer++ = capexp ? 'E' : 'e'; + } // Calculate the exponent. if (decpt == 0) { @@ -376,19 +394,18 @@ static void cfltcvt(double value, char *buffer, char fmt, int precision) } // Add the sign of the exponent. - if (exp < 0) { + if (exp < 0 && buffer < end) { *buffer++ = '-'; exp = -exp; - } else { + } else if (buffer < end) { *buffer++ = '+'; } // Add the exponent digits (e.g., '01', '02'). - buffer[2] = (char)((exp % 10) + '0'); - exp /= 10; - buffer[1] = (char)((exp % 10) + '0'); - exp /= 10; - buffer[0] = (char)((exp % 10) + '0'); + for (int i = 2; i >= 0 && buffer < end; i--) { + buffer[i] = (char)((exp % 10) + '0'); + exp /= 10; + } buffer += 3; } // Handle fixed-point notation ('f' format). @@ -397,7 +414,7 @@ static void cfltcvt(double value, char *buffer, char fmt, int precision) fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); // Add the sign to the output buffer if necessary. - if (sign) { + if (sign && buffer < end) { *buffer++ = '-'; } @@ -406,52 +423,66 @@ static void cfltcvt(double value, char *buffer, char fmt, int precision) // Handle the case where the decimal point is before the first // digit. if (decpt <= 0) { - *buffer++ = '0'; - *buffer++ = '.'; + if (buffer < end) { + *buffer++ = '0'; + } + if (buffer < end) { + *buffer++ = '.'; + } // Add leading zeros before the first significant digit. - for (pos = 0; pos < -decpt; pos++) { + for (pos = 0; pos < -decpt && buffer < end; pos++) { *buffer++ = '0'; } // Copy the digits. - while (*digits) { + while (*digits && buffer < end) { *buffer++ = *digits++; } } // Handle normal case where decimal point is after some digits. else { pos = 0; - while (*digits) { - if (pos++ == decpt) { + while (*digits && buffer < end) { + if (pos++ == decpt && buffer < end) { *buffer++ = '.'; } - *buffer++ = *digits++; + if (buffer < end) { + *buffer++ = *digits++; + } } } } // Handle case where the value is zero. else { - *buffer++ = '0'; - if (precision > 0) { + if (buffer < end) { + *buffer++ = '0'; + } + if (precision > 0 && buffer < end) { *buffer++ = '.'; - for (pos = 0; pos < precision; pos++) { + for (pos = 0; pos < precision && buffer < end; pos++) { *buffer++ = '0'; } } } } - *buffer = '\0'; // Null-terminate the string. + if (buffer < end) { + *buffer = '\0'; // Null-terminate the string. + } else { + *end = '\0'; // Ensure null-termination if buffer exceeded. + } } /// @brief Ensures that a decimal point is present in the given number string. /// @param buffer The string representation of a number. -static void forcdecpt(char *buffer) +/// @param bufsize The size of the output buffer. +static void forcdecpt(char *buffer, size_t bufsize) { // Traverse the buffer to check if there is already a decimal point or an // exponent ('e' or 'E'). - while (*buffer) { + char *end = buffer + bufsize - 1; // Pointer to the end of the buffer. + while (*buffer && buffer < end) { if (*buffer == '.') { return; // Decimal point found, no need to modify. } @@ -463,22 +494,25 @@ static void forcdecpt(char *buffer) // If an exponent ('e' or 'E') is found, shift the exponent part to make // space for the decimal point. - if (*buffer) { + if (*buffer && buffer < end) { // Get the length of the exponent part. long n = (long)strlen(buffer); - // Shift the buffer contents one position to the right to make space for - // the decimal point. - while (n > 0) { - buffer[n + 1] = buffer[n]; - n--; - } + // Check if there is enough space to shift and add the decimal point. + if (buffer + n + 1 < end) { + // Shift the buffer contents one position to the right to make space for + // the decimal point. + while (n >= 0) { + buffer[n + 1] = buffer[n]; + n--; + } - // Insert the decimal point. - *buffer = '.'; + // Insert the decimal point. + *buffer = '.'; + } } // If no exponent is found, append the decimal point at the end of the string. - else { + else if (buffer < end) { *buffer++ = '.'; // Add decimal point at the current end. *buffer = '\0'; // Null-terminate the string. } @@ -486,21 +520,23 @@ static void forcdecpt(char *buffer) /// @brief Removes trailing zeros after the decimal point in a number string. /// @param buffer The string representation of a number. -static void cropzeros(char *buffer) +/// @param bufsize The size of the output buffer. +static void cropzeros(char *buffer, size_t bufsize) { char *stop; + char *end = buffer + bufsize - 1; // Pointer to the end of the buffer. // Traverse until a decimal point is found or the end of the string is // reached. - while (*buffer && *buffer != '.') { + while (*buffer && *buffer != '.' && buffer < end) { buffer++; } // If there is a decimal point, find the position of the exponent or the end // of the number. - if (*buffer++) { // Move past the decimal point. + if (*buffer++ && buffer < end) { // Move past the decimal point. // Continue until 'e', 'E', or end of string is found. - while (*buffer && *buffer != 'e' && *buffer != 'E') { + while (*buffer && *buffer != 'e' && *buffer != 'E' && buffer < end) { buffer++; } @@ -509,7 +545,7 @@ static void cropzeros(char *buffer) stop = buffer--; // Backtrack over trailing zeros. - while (*buffer == '0') { + while (*buffer == '0' && buffer > (buffer - bufsize)) { buffer--; } @@ -521,7 +557,12 @@ static void cropzeros(char *buffer) // Shift the string forward to overwrite any unnecessary characters // (trailing zeros or decimal point). - while ((*++buffer = *stop++)) {} + while (buffer < end && (*++buffer = *stop++)) {} + + // Ensure null-termination if buffer exceeded. + if (buffer >= end) { + *end = '\0'; + } } } @@ -532,6 +573,7 @@ static void cropzeros(char *buffer) /// handles alignment, padding, and sign appropriately. /// /// @param str Pointer to the output string where the formatted number will be stored. +/// @param end Pointer to the end of the buffer to prevent overflow. /// @param num The floating-point number to format. /// @param size The total size of the output string, including padding. /// @param precision The number of digits to display after the decimal point. @@ -539,7 +581,7 @@ static void cropzeros(char *buffer) /// @param flags Control flags that modify the output format (e.g., left alignment, zero padding). /// /// @return Pointer to the next position in the output string after the formatted number. -static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags) +static char *flt(char *str, char *end, double num, int size, int precision, char fmt, unsigned flags) { char workbuf[80]; char c, sign; @@ -585,17 +627,17 @@ static char *flt(char *str, double num, int size, int precision, char fmt, unsig /// Convert the floating-point number `num` into a string `workbuf` using the /// given format `fmt`. - cfltcvt(num, workbuf, fmt, precision); + cfltcvt(num, workbuf, sizeof(workbuf), fmt, precision); /// If the `FLAGS_HASH` is set and precision is 0, force a decimal point in /// the output. if (bitmask_check(flags, FLAGS_HASH) && (precision == 0)) { - forcdecpt(workbuf); + forcdecpt(workbuf, sizeof(workbuf)); } /// For format 'g', remove trailing zeros unless `FLAGS_HASH` is set. if ((fmt == 'g') && !bitmask_check(flags, FLAGS_HASH)) { - cropzeros(workbuf); + cropzeros(workbuf, sizeof(workbuf)); } /// Calculate the length of the resulting string `workbuf`. @@ -606,30 +648,30 @@ static char *flt(char *str, double num, int size, int precision, char fmt, unsig /// Add padding spaces before the number if neither `FLAGS_ZEROPAD` nor `FLAGS_LEFT` are set. if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { - while (size-- > 0) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } } /// Add the sign character (if any) before the number. - if (sign) { + if (sign && (end == NULL || str < end)) { *str++ = sign; } /// Add padding characters (either '0' or spaces) before the number if `FLAGS_ZEROPAD` is set. if (!bitmask_check(flags, FLAGS_LEFT)) { - while (size-- > 0) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = c; } } /// Copy the formatted number string to the output `str`. - for (i = 0; i < n; i++) { + for (i = 0; i < n && (end == NULL || str < end); i++) { *str++ = workbuf[i]; } /// Add padding spaces after the number if `FLAGS_LEFT` is set (left-aligned output). - while (size-- > 0) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } @@ -769,7 +811,7 @@ int vsprintf(char *str, const char *fmt, va_list args) // Zero pad for pointers. bitmask_set_assign(flags, FLAGS_ZEROPAD); } - tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + tmp = number(tmp, NULL, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); continue; case 'n': @@ -791,9 +833,9 @@ int vsprintf(char *str, const char *fmt, va_list args) case 'a': // Handle address formatting (either Ethernet or IP). if (qualifier == 'l') { - tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + tmp = eaddr(tmp, NULL, va_arg(args, unsigned char *), field_width, precision, flags); } else { - tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + tmp = iaddr(tmp, NULL, va_arg(args, unsigned char *), field_width, precision, flags); } continue; @@ -827,7 +869,7 @@ int vsprintf(char *str, const char *fmt, va_list args) case 'f': case 'g': // Handle floating-point formatting. - tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); + tmp = flt(tmp, NULL, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); continue; default: @@ -848,13 +890,13 @@ int vsprintf(char *str, const char *fmt, va_list args) (qualifier == 'h') ? va_arg(args, short) : va_arg(args, int); // Add the number. - tmp = number(tmp, num, base, field_width, precision, flags); + tmp = number(tmp, NULL, num, base, field_width, precision, flags); } else { unsigned long num = (qualifier == 'l') ? va_arg(args, unsigned long) : (qualifier == 'h') ? va_arg(args, unsigned short) : va_arg(args, unsigned int); // Add the number. - tmp = number(tmp, num, base, field_width, precision, flags); + tmp = number(tmp, NULL, num, base, field_width, precision, flags); } } @@ -862,19 +904,252 @@ int vsprintf(char *str, const char *fmt, va_list args) return (int)(tmp - str); // Return the number of characters written. } -int printf(const char *fmt, ...) +int vsnprintf(char *str, size_t bufsize, const char *fmt, va_list args) { - char buffer[4096]; - va_list ap; + int base; // Base for number formatting. + char *tmp; // Pointer to current position in the output buffer. + char *s; // Pointer for string formatting. + unsigned flags; // Flags for number formatting. + char qualifier; // Character qualifier for integer fields ('h', 'l', or 'L'). + + // Check for null input buffer or format string. + if (str == NULL || fmt == NULL) { + return -1; // Error: null pointer provided. + } + + char *end = str + bufsize - 1; // Reserve space for null-terminator. + + for (tmp = str; *fmt && tmp < end; fmt++) { + if (*fmt != '%') { + if (tmp < end) { + *tmp++ = *fmt; // Directly copy non-format characters. + } + continue; + } + + // Reset flags for each new format specifier. + flags = 0; + + repeat: + + // Process format flags (skips first '%'). + fmt++; + switch (*fmt) { + case '-': + bitmask_set_assign(flags, FLAGS_LEFT); + goto repeat; + case '+': + bitmask_set_assign(flags, FLAGS_PLUS); + goto repeat; + case ' ': + bitmask_set_assign(flags, FLAGS_SPACE); + goto repeat; + case '#': + bitmask_set_assign(flags, FLAGS_HASH); + goto repeat; + case '0': + bitmask_set_assign(flags, FLAGS_ZEROPAD); + goto repeat; + } + + // Get the width of the output field. + int32_t field_width = -1; + + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int32_t); + if (field_width < 0) { + field_width = -field_width; + bitmask_set_assign(flags, FLAGS_LEFT); + } + } + + // Get the precision, thus the minimum number of digits for integers; + // max number of chars for from string. + int32_t precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + // Get the conversion qualifier. + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base for integer conversion. + base = 10; + + switch (*fmt) { + case 'c': + // Handle left padding. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (--field_width > 0 && tmp < end) { + *tmp++ = ' '; + } + } + // Add the character. + if (tmp < end) { + *tmp++ = va_arg(args, int); + } + // Handle right padding. + while (--field_width > 0 && tmp < end) { + *tmp++ = ' '; + } + continue; + + case 's': + // Handle string formatting. + s = va_arg(args, char *); + if (!s) { + s = ""; + } + // Get the length of the string. + int32_t len = (int32_t)strnlen(s, (uint32_t)precision); + // Handle left padding. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < field_width-- && tmp < end) { + *tmp++ = ' '; + } + } + // Add the string. + for (int32_t it = 0; it < len && tmp < end; ++it) { + *tmp++ = *s++; + } + // Handle right padding. + while (len < field_width-- && tmp < end) { + *tmp++ = ' '; + } + continue; + + case 'p': + // Handle pointer formatting. + if (field_width == -1) { + field_width = 2 * sizeof(void *); + // Zero pad for pointers. + bitmask_set_assign(flags, FLAGS_ZEROPAD); + } + tmp = number(tmp, end, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + continue; + + case 'n': + // Handle writing the number of characters written. + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (tmp - str); + } else { + int *ip = va_arg(args, int *); + *ip = (tmp - str); + } + continue; + + case 'A': + // Handle hexadecimal formatting with uppercase. + bitmask_set_assign(flags, FLAGS_UPPERCASE); + break; + + case 'a': + // Handle address formatting (either Ethernet or IP). + if (qualifier == 'l') { + tmp = eaddr(tmp, end, va_arg(args, unsigned char *), field_width, precision, flags); + } else { + tmp = iaddr(tmp, end, va_arg(args, unsigned char *), field_width, precision, flags); + } + continue; + + case 'o': + // Integer number formats. + base = 8; + break; + + case 'X': + // Handle hexadecimal formatting with uppercase. + bitmask_set_assign(flags, FLAGS_UPPERCASE); + // Fall through. + case 'x': + // Handle hexadecimal formatting. + base = 16; + break; + + case 'd': + case 'i': + // Handle signed integer formatting. + bitmask_set_assign(flags, FLAGS_SIGN); + // Fall through. + case 'u': + // Handle unsigned integer formatting. + break; + + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + // Handle floating-point formatting. + tmp = flt(tmp, end, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); + continue; + + default: + if (*fmt != '%') { + if (tmp < end) { + *tmp++ = '%'; // Output '%' if not a format specifier. + } + } + if (*fmt && tmp < end) { + *tmp++ = *fmt; // Output the current character. + } else { + --fmt; // Handle the case of trailing '%'. + } + continue; + } + + // Process the integer value. + if (bitmask_check(flags, FLAGS_SIGN)) { + long num = (qualifier == 'l') ? va_arg(args, long) : + (qualifier == 'h') ? va_arg(args, short) : + va_arg(args, int); + // Add the number. + tmp = number(tmp, end, num, base, field_width, precision, flags); + } else { + unsigned long num = (qualifier == 'l') ? va_arg(args, unsigned long) : + (qualifier == 'h') ? va_arg(args, unsigned short) : + va_arg(args, unsigned int); + // Add the number. + tmp = number(tmp, end, num, base, field_width, precision, flags); + } + } + + if (tmp < end) { + *tmp = '\0'; // Null-terminate the output string. + } else { + *end = '\0'; // Ensure null-termination if buffer exceeded. + } + return (int)(tmp - str); // Return the number of characters written. +} + +int printf(const char *format, ...) +{ + char buffer[4096] = { 0 }; + va_list ap; + int len; // Start variabile argument's list. - va_start(ap, fmt); - int len = vsprintf(buffer, fmt, ap); + va_start(ap, format); + len = vsnprintf(buffer, 4096, format, ap); va_end(ap); - - // Write the contento to standard output. puts(buffer); - return len; } @@ -889,6 +1164,18 @@ int sprintf(char *str, const char *fmt, ...) return len; } +int snprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list args; + int len; + + va_start(args, fmt); + len = vsnprintf(str, size, fmt, args); + va_end(args); + + return len; +} + int vfprintf(int fd, const char *fmt, va_list args) { char buffer[4096]; diff --git a/mentos/inc/drivers/keyboard/keyboard.h b/mentos/inc/drivers/keyboard/keyboard.h index 38628e10..c2f489f8 100644 --- a/mentos/inc/drivers/keyboard/keyboard.h +++ b/mentos/inc/drivers/keyboard/keyboard.h @@ -13,7 +13,7 @@ #include "ring_buffer.h" #include "kernel.h" -DECLARE_FIXED_SIZE_RING_BUFFER(int, scancode, 256, -1) +DECLARE_FIXED_SIZE_RING_BUFFER(int, keybuffer, 256, -1) /// @brief The interrupt service routine of the keyboard. /// @param f The interrupt stack frame. @@ -34,11 +34,11 @@ int keyboard_pop_back(void); /// @brief Gets a char from the back of the buffer. /// @return The read character. -int keyboard_back(void); +int keyboard_peek_back(void); /// @brief Gets a char from the front of the buffer. /// @return The read character. -int keyboard_front(void); +int keyboard_peek_front(void); /// @brief Initializes the keyboard drivers. /// @return 0 on success, 1 on error. diff --git a/mentos/inc/drivers/mem.h b/mentos/inc/drivers/mem.h index 30413c08..b9703da9 100644 --- a/mentos/inc/drivers/mem.h +++ b/mentos/inc/drivers/mem.h @@ -10,8 +10,14 @@ #pragma once -/// @brief Initializes the memory devices. -/// @return 0 on success, 1 on error. +/// @brief Initializes memory devices and registers the null device. +/// +/// @details This function creates the `/dev/null` device, registers the null +/// filesystem type, and mounts the null device to the virtual file system. If +/// any step fails, it logs the error and returns an appropriate error code. +/// +/// @return 0 on success, -ENODEV if device creation fails, 1 if filesystem +/// registration or mounting fails. int mem_devs_initialize(void); /// @} diff --git a/mentos/inc/fs/attr.h b/mentos/inc/fs/attr.h index 384d4505..73c67da7 100644 --- a/mentos/inc/fs/attr.h +++ b/mentos/inc/fs/attr.h @@ -1,16 +1,55 @@ /// @file attr.h -/// @brief change file attributes +/// @brief Change file attributes. +/// @details This header provides declarations for system calls that handle file +/// ownership and permissions, such as changing the owner, group, and mode of +/// files or file descriptors. /// @copyright (c) 2024 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "stddef.h" -int sys_chown(const char* path, uid_t owner, gid_t group); +/// @brief Change the owner and group of a file by path. It modifies the file's +/// ownership attributes to the provided `owner` and `group`. +/// +/// @param path Path to the file. +/// @param owner The user ID of the new owner. +/// @param group The group ID of the new group. +/// @return 0 on success, or -1 on error. +int sys_chown(const char *path, uid_t owner, gid_t group); -int sys_lchown(const char* path, uid_t owner, gid_t group); +/// @brief Change the owner and group of a symbolic link. It modifies the +/// symbolic link's ownership attributes to the provided `owner` and `group`. +/// This does not follow symbolic links, modifying only the link itself. +/// +/// @param path Path to the symbolic link. +/// @param owner The user ID of the new owner. +/// @param group The group ID of the new group. +/// @return 0 on success, or -1 on error. +int sys_lchown(const char *path, uid_t owner, gid_t group); +/// @brief Change the owner and group of a file by file descriptor. It modifies +/// the file's ownership attributes to the provided `owner` and `group`. +/// +/// @param fd File descriptor referring to the file. +/// @param owner The user ID of the new owner. +/// @param group The group ID of the new group. +/// @return 0 on success, or -1 on error. int sys_fchown(int fd, uid_t owner, gid_t group); -int sys_chmod(const char* path, mode_t mode); +/// @brief Change the mode (permissions) of a file by path. It modifies the +/// file's permissions to the provided `mode`, determining the file's access +/// permissions. +/// +/// @param path Path to the file. +/// @param mode The new file mode (permissions). +/// @return 0 on success, or -1 on error. +int sys_chmod(const char *path, mode_t mode); +/// @brief Change the mode (permissions) of a file by file descriptor. It +/// modifies the file's permissions to the provided `mode`, determining the +/// file's access permissions. +/// +/// @param fd File descriptor referring to the file. +/// @param mode The new file mode (permissions). +/// @return 0 on success, or -1 on error. int sys_fchmod(int fd, mode_t mode); diff --git a/mentos/inc/fs/namei.h b/mentos/inc/fs/namei.h index 116ade9b..189852df 100644 --- a/mentos/inc/fs/namei.h +++ b/mentos/inc/fs/namei.h @@ -1,5 +1,8 @@ /// @file namei.h -/// @brief Headers for path resolution +/// @brief Headers for path resolution. +/// @details This header provides definitions and function declarations for +/// resolving paths, including symbolic links and handling different flags that +/// control the behavior of the path resolution process. /// @copyright (c) 2024 This file is distributed under the MIT License. /// See LICENSE.md for details. @@ -7,11 +10,20 @@ #include "stddef.h" +/// Flag to remove any trailing slash from the path. #define REMOVE_TRAILING_SLASH 1 << 0 -#define FOLLOW_LINKS 1 << 1 -#define CREAT_LAST_COMPONENT 1 << 2 +/// Flag to follow symbolic links during path resolution. +#define FOLLOW_LINKS 1 << 1 +/// Flag to create the last component of the path if it doesn't exist. +#define CREAT_LAST_COMPONENT 1 << 2 /// @brief Resolve the given path by following all symbolic links. +/// +/// @details This function resolves a file path, following any symbolic links +/// encountered along the way, and returns the absolute path. The behavior can +/// be controlled with flags such as whether to follow symbolic links and how to +/// handle trailing slashes. +/// /// @param path The path to resolve, can include symbolic links. /// @param buffer The buffer where the resolved absolute path will be stored. /// @param buflen The size of the buffer, which should be large enough to hold the resolved path. diff --git a/mentos/inc/io/video.h b/mentos/inc/io/video.h index cc6e3a19..7a62439b 100644 --- a/mentos/inc/io/video.h +++ b/mentos/inc/io/video.h @@ -23,7 +23,7 @@ void video_putc(int c); void video_puts(const char *str); /// @brief When something is written in another position, update the cursor. -void video_set_cursor_auto(void); +void video_update_cursor_position(void); /// @brief Move the cursor at the position x, y on the screen. /// @param x The x coordinate. diff --git a/mentos/inc/ipc/ipc.h b/mentos/inc/ipc/ipc.h index 0906baaa..36fe2594 100644 --- a/mentos/inc/ipc/ipc.h +++ b/mentos/inc/ipc/ipc.h @@ -11,6 +11,26 @@ #error "How did you include this file... include `libc/inc/sys/ipc.h` instead!" #endif +/// @brief Validate IPC permissions based on flags and the given permission structure. +/// @param flags Flags that control the validation behavior. +/// @param perm Pointer to the IPC permission structure to validate. +/// @return 0 if the permissions are valid, or a non-zero value on failure. int ipc_valid_permissions(int flags, struct ipc_perm *perm); +/// @brief Register an IPC resource with a given key and mode. +/// @param key The key associated with the IPC resource. +/// @param mode The mode (permissions) for the IPC resource. +/// @return A `struct ipc_perm` representing the registered IPC resource. struct ipc_perm register_ipc(key_t key, mode_t mode); + +/// @brief Initializes the semaphore system. +/// @return 0 on success, 1 on failure. +int sem_init(void); + +/// @brief Initializes the shared memory system. +/// @return 0 on success, 1 on failure. +int shm_init(void); + +/// @brief Initializes the message queue system. +/// @return 0 on success, 1 on failure. +int msq_init(void); diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index 8960886d..8e86dc05 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -156,7 +156,7 @@ typedef struct task_struct { /// Process-wise terminal options. termios_t termios; /// Buffer for managing inputs from keyboard. - fs_rb_scancode_t keyboard_rb; + rb_keybuffer_t keyboard_rb; //==== Future work ========================================================= // - task's attributes: diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index 628c7fc5..ca91c4cc 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -10,6 +10,9 @@ #include "kernel.h" #include "sys/dirent.h" #include "sys/types.h" +#include "sys/msg.h" +#include "sys/sem.h" +#include "sys/shm.h" /// @brief Initialize the system calls. void syscall_init(void); @@ -266,3 +269,113 @@ ssize_t sys_getdents(int fd, dirent_t *dirp, unsigned int count); /// @param time Where the time should be stored. /// @return The current time. time_t sys_time(time_t *time); + +/// @brief Get a System V semaphore set identifier. +/// @param key can be used either to obtain the identifier of a previously +/// created semaphore set, or to create a new set. +/// @param nsems number of semaphores. +/// @param semflg controls the behaviour of the function. +/// @return the semaphore set identifier, -1 on failure, and errno is set to +/// indicate the error. +long sys_semget(key_t key, int nsems, int semflg); + +/// @brief Performs operations on selected semaphores in the set. +/// @param semid the semaphore set identifier. +/// @param sops specifies operations to be performed on single semaphores. +/// @param nsops number of operations. +/// @return 0 on success, -1 on failure and errno is set to indicate the error. +long sys_semop(int semid, struct sembuf *sops, unsigned nsops); + +/// @brief Performs control operations on a semaphore set. +/// @param semid the semaphore set identifier. +/// @param semnum the n-th semaphore of the set on which we perform the operations. +/// @param cmd the command to perform. +/// @param arg +/// @return 0 on success, -1 on failure and errno is set to indicate the error. +long sys_semctl(int semid, int semnum, int cmd, union semun *arg); + +/// @brief Get a System V shared memory identifier. +/// @param key can be used either to obtain the identifier of a previously +/// created shared memory, or to create a new one. +/// @param size of the shared memory, rounded up to a multiple of PAGE_SIZE. +/// @param shmflg controls the behaviour of the function. +/// @return the shared memory identifier, -1 on failure, and errno is set to +/// indicate the error. +long sys_shmget(key_t key, size_t size, int shmflg); + +/// @brief Attaches the shared memory segment identified by shmid to the address +/// space of the calling process. +/// @param shmid the shared memory identifier. +/// @param shmaddr the attaching address. +/// @param shmflg controls the behaviour of the function. +/// @return returns the address of the attached shared memory segment; on error +/// (void *) -1 is returned, and errno is set to indicate the error. +void *sys_shmat(int shmid, const void *shmaddr, int shmflg); + +/// @brief Detaches the shared memory segment located at the address specified +/// by shmaddr from the address space of the calling process +/// @param shmaddr the address of the shared memory segment. +/// @return 0 on success, -1 on failure and errno is set to indicate the error. +long sys_shmdt(const void *shmaddr); + +/// @brief Performs the control operation specified by cmd on the shared memory +/// segment whose identifier is given in shmid. +/// @param shmid the shared memory identifier. +/// @param cmd the command to perform. +/// @param buf is a pointer to a shmid_ds structure. +/// @return a non-negative value basedon on the requested operation, -1 on +/// failure and errno is set to indicate the error. +long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf); + +/// @brief Get a System V message queue identifier. +/// @param key can be used either to obtain the identifier of a previously +/// created message queue, or to create a new set. +/// @param msgflg controls the behaviour of the function. +/// @return the message queue identifier, -1 on failure, and errno is set to +/// indicate the error. +int sys_msgget(key_t key, int msgflg); + +/// @brief Used to send messages. +/// @param msqid the message queue identifier. +/// @param msgp points to a used-defined msgbuf. +/// @param msgsz specifies the size in bytes of mtext. +/// @param msgflg specifies the action to be taken in case of specific events. +/// @return 0 on success, -1 on failure and errno is set to indicate the error. +int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); + +/// @brief Used to receive messages. +/// @param msqid the message queue identifier. +/// @param msgp points to a used-defined msgbuf. +/// @param msgsz specifies the size in bytes of mtext. +/// @param msgtyp specifies the type of message requested, as follows: +/// - msgtyp == 0: the first message on the queue is received. +/// - msgtyp > 0: the first message of type, msgtyp, is received. +/// - msgtyp < 0: the first message of the lowest type that is less than or +/// equal to the absolute value of msgtyp is received. +/// @param msgflg specifies the action to be taken in case of specific events. +/// @return the number of bytes actually copied on success, -1 on failure and +/// errno is set to indicate the error. +ssize_t sys_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); + +/// @brief Message queue control operations. +/// @param msqid the message queue identifier. +/// @param cmd The command to perform. +/// @param buf used with IPC_STAT and IPC_SET. +/// @return 0 on success, -1 on failure and errno is set to indicate the error. +int sys_msgctl(int msqid, int cmd, struct msqid_ds *buf); + +/// @brief creates a new mapping in the virtual address space of the calling process. +/// @param addr the starting address for the new mapping. +/// @param length specifies the length of the mapping (which must be greater than 0). +/// @param prot describes the desired memory protection of the mapping (and must not conflict with the open mode of the file). +/// @param flags determines whether updates to the mapping are visible to other processes mapping the same region. +/// @param fd in case of file mapping, the file descriptor to use. +/// @param offset offset in the file, which must be a multiple of the page size PAGE_SIZE. +/// @return returns a pointer to the mapped area, -1 and errno is set. +void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); + +/// @brief deletes the mappings for the specified address range. +/// @param addr the starting address. +/// @param length the length of the mapped area. +/// @return 0 on success, -1 on falure and errno is set. +int sys_munmap(void *addr, size_t length); diff --git a/mentos/src/crypt/sha256.c b/mentos/src/crypt/sha256.c index a6530ae3..15bada19 100644 --- a/mentos/src/crypt/sha256.c +++ b/mentos/src/crypt/sha256.c @@ -157,12 +157,6 @@ static inline void __sha256_transform(SHA256_ctx_t *ctx, const uint8_t data[]) ctx->state[7] += h; } -/// @brief Converts a byte array into its hexadecimal string representation. -/// @param src Pointer to the source byte array. -/// @param src_length Length of the source byte array. -/// @param out Pointer to the output buffer for the hexadecimal string. -/// @param out_length Length of the output buffer (must be at least 2 * src_length + 1). -/// @details The output string will be null-terminated if the buffer is large enough. void sha256_bytes_to_hex(uint8_t *src, size_t src_length, char *out, size_t out_length) { // Check if the output buffer is large enough to hold the hex string diff --git a/mentos/src/drivers/keyboard/keyboard.c b/mentos/src/drivers/keyboard/keyboard.c index 011ebd64..4b214386 100644 --- a/mentos/src/drivers/keyboard/keyboard.c +++ b/mentos/src/drivers/keyboard/keyboard.c @@ -29,7 +29,7 @@ static uint8_t ledstate = 0; /// The flags concerning the keyboard. static uint32_t kflags = 0; /// Where we store the keypress. -fs_rb_scancode_t scancodes; +rb_keybuffer_t scancodes; /// Spinlock to protect access to the scancode buffer. spinlock_t scancodes_lock; @@ -43,6 +43,56 @@ spinlock_t scancodes_lock; #define KBD_LEFT_ALT (1 << 7) ///< Flag which identifies the left alt. #define KBD_RIGHT_ALT (1 << 8) ///< Flag which identifies the right alt. +/// Define variable to switch between normal mode sequences and xterm sequences. +#define USE_XTERM_SEQUENCES 0 + +#define SEQ_UP_ARROW "\033[A" ///< Escape sequence for the Up Arrow key. +#define SEQ_DOWN_ARROW "\033[B" ///< Escape sequence for the Down Arrow key. +#define SEQ_RIGHT_ARROW "\033[C" ///< Escape sequence for the Right Arrow key. +#define SEQ_LEFT_ARROW "\033[D" ///< Escape sequence for the Left Arrow key. +#define SEQ_CTRL_UP_ARROW "\033[1;5A" ///< Escape sequence for Ctrl + Up Arrow. +#define SEQ_CTRL_DOWN_ARROW "\033[1;5B" ///< Escape sequence for Ctrl + Down Arrow. +#define SEQ_CTRL_RIGHT_ARROW "\033[1;5C" ///< Escape sequence for Ctrl + Right Arrow. +#define SEQ_CTRL_LEFT_ARROW "\033[1;5D" ///< Escape sequence for Ctrl + Left Arrow. +#define SEQ_INSERT "\033[2~" ///< Escape sequence for the Insert key. +#define SEQ_DELETE "\033[3~" ///< Escape sequence for the Delete key. +#define SEQ_PAGE_UP "\033[5~" ///< Escape sequence for the Page Up key. +#define SEQ_PAGE_DOWN "\033[6~" ///< Escape sequence for the Page Down key. +#define SEQ_F1 "\033OP" ///< Escape sequence for the F1 key. +#define SEQ_F2 "\033OQ" ///< Escape sequence for the F2 key. +#define SEQ_F3 "\033OR" ///< Escape sequence for the F3 key. +#define SEQ_F4 "\033OS" ///< Escape sequence for the F4 key. +#define SEQ_F5 "\033[15~" ///< Escape sequence for the F5 key. +#define SEQ_F6 "\033[17~" ///< Escape sequence for the F6 key. +#define SEQ_F7 "\033[18~" ///< Escape sequence for the F7 key. +#define SEQ_F8 "\033[19~" ///< Escape sequence for the F8 key. +#define SEQ_F9 "\033[20~" ///< Escape sequence for the F9 key. +#define SEQ_F10 "\033[21~" ///< Escape sequence for the F10 key. +#define SEQ_F11 "\033[23~" ///< Escape sequence for the F11 key. +#define SEQ_F12 "\033[24~" ///< Escape sequence for the F12 key. + +#if USE_XTERM_SEQUENCES +#define SEQ_HOME "\033[1~" ///< Escape sequence for the Home key in xterm. +#define SEQ_END "\033[4~" ///< Escape sequence for the End key in xterm. +#else +#define SEQ_HOME "\033[H" ///< Escape sequence for the Home key. +#define SEQ_END "\033[F" ///< Escape sequence for the End key. +#endif + +/// @brief Pushes a character into the scancode ring buffer. +/// @param c The character to push into the ring buffer. +static inline void keyboard_push_back(unsigned int c) +{ + // Lock the scancode buffer to ensure thread safety during push operation. + spinlock_lock(&scancodes_lock); + + // Push the character into the front of the scancode buffer. + rb_keybuffer_push_back(&scancodes, (int)c); + + // Unlock the buffer after the push operation is complete. + spinlock_unlock(&scancodes_lock); +} + /// @brief Pushes a character into the scancode ring buffer. /// @param c The character to push into the ring buffer. static inline void keyboard_push_front(unsigned int c) @@ -51,7 +101,7 @@ static inline void keyboard_push_front(unsigned int c) spinlock_lock(&scancodes_lock); // Push the character into the front of the scancode buffer. - fs_rb_scancode_push_front(&scancodes, (int)c); + rb_keybuffer_push_front(&scancodes, (int)c); // Unlock the buffer after the push operation is complete. spinlock_unlock(&scancodes_lock); @@ -59,14 +109,30 @@ static inline void keyboard_push_front(unsigned int c) /// @brief Pushes a sequence of characters (scancodes) into the keyboard buffer. /// @param sequence A null-terminated string representing the sequence to push. -static inline void keyboard_push_sequence(char *sequence) +static inline void keyboard_push_back_sequence(char *sequence) +{ + // Lock the scancodes ring buffer to ensure thread safety. + spinlock_lock(&scancodes_lock); + + // Iterate through each character in the sequence and push it to the buffer. + for (size_t i = 0; i < strlen(sequence); ++i) { + rb_keybuffer_push_back(&scancodes, (int)sequence[i]); + } + + // Unlock the buffer after the operation is complete. + spinlock_unlock(&scancodes_lock); +} + +/// @brief Pushes a sequence of characters (scancodes) into the keyboard buffer. +/// @param sequence A null-terminated string representing the sequence to push. +static inline void keyboard_push_front_sequence(char *sequence) { // Lock the scancodes ring buffer to ensure thread safety. spinlock_lock(&scancodes_lock); // Iterate through each character in the sequence and push it to the buffer. for (size_t i = 0; i < strlen(sequence); ++i) { - fs_rb_scancode_push_front(&scancodes, (int)sequence[i]); + rb_keybuffer_push_front(&scancodes, (int)sequence[i]); } // Unlock the buffer after the operation is complete. @@ -77,39 +143,24 @@ static inline void keyboard_push_sequence(char *sequence) /// @return the value we removed from the ring buffer. int keyboard_pop_back(void) { - int c; spinlock_lock(&scancodes_lock); - if (!fs_rb_scancode_empty(&scancodes)) { - c = fs_rb_scancode_pop_back(&scancodes); - } else { - c = -1; - } + int c = rb_keybuffer_pop_back(&scancodes); spinlock_unlock(&scancodes_lock); return c; } -int keyboard_back(void) +int keyboard_peek_back(void) { - int c; spinlock_lock(&scancodes_lock); - if (!fs_rb_scancode_empty(&scancodes)) { - c = fs_rb_scancode_back(&scancodes); - } else { - c = -1; - } + int c = rb_keybuffer_peek_back(&scancodes); spinlock_unlock(&scancodes_lock); return c; } -int keyboard_front(void) +int keyboard_peek_front(void) { - int c = -1; spinlock_lock(&scancodes_lock); - if (!fs_rb_scancode_empty(&scancodes)) { - c = fs_rb_scancode_front(&scancodes); - } else { - c = -1; - } + int c = rb_keybuffer_peek_front(&scancodes); spinlock_unlock(&scancodes_lock); return c; } @@ -132,146 +183,204 @@ void keyboard_isr(pt_regs *f) // Get the keypad number, of num-lock is disabled. int keypad_code = !bitmask_check(kflags, KBD_NUM_LOCK) ? scancode : 0; + int ctrl_pressed = (kflags & (KBD_LEFT_CONTROL | KBD_RIGHT_CONTROL)) != 0; + int shift_pressed = (kflags & (KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT)) != 0; + int right_alt_pressed = (kflags & KBD_RIGHT_ALT) != 0; + int caps_lock_pressed = (kflags & KBD_CAPS_LOCK) != 0; + // If the key has just been released. if (scancode == KEY_LEFT_SHIFT) { - bitmask_set_assign(kflags, KBD_LEFT_SHIFT); pr_debug("Press(KBD_LEFT_SHIFT)\n"); + bitmask_set_assign(kflags, KBD_LEFT_SHIFT); + } else if (scancode == KEY_RIGHT_SHIFT) { - bitmask_set_assign(kflags, KBD_RIGHT_SHIFT); pr_debug("Press(KBD_RIGHT_SHIFT)\n"); + bitmask_set_assign(kflags, KBD_RIGHT_SHIFT); + } else if (scancode == KEY_LEFT_CONTROL) { - bitmask_set_assign(kflags, KBD_LEFT_CONTROL); pr_debug("Press(KBD_LEFT_CONTROL)\n"); + bitmask_set_assign(kflags, KBD_LEFT_CONTROL); + } else if (scancode == KEY_RIGHT_CONTROL) { - bitmask_set_assign(kflags, KBD_RIGHT_CONTROL); pr_debug("Press(KBD_RIGHT_CONTROL)\n"); + bitmask_set_assign(kflags, KBD_RIGHT_CONTROL); + } else if (scancode == KEY_LEFT_ALT) { + pr_debug("Press(KBD_LEFT_ALT)\n"); bitmask_set_assign(kflags, KBD_LEFT_ALT); keyboard_push_front(scancode << 16u); - pr_debug("Press(KBD_LEFT_ALT)\n"); + } else if (scancode == KEY_RIGHT_ALT) { + pr_debug("Press(KBD_RIGHT_ALT)\n"); bitmask_set_assign(kflags, KBD_RIGHT_ALT); keyboard_push_front(scancode << 16u); - pr_debug("Press(KBD_RIGHT_ALT)\n"); + } else if (scancode == (KEY_LEFT_SHIFT | CODE_BREAK)) { - bitmask_clear_assign(kflags, KBD_LEFT_SHIFT); pr_debug("Release(KBD_LEFT_SHIFT)\n"); + bitmask_clear_assign(kflags, KBD_LEFT_SHIFT); + } else if (scancode == (KEY_RIGHT_SHIFT | CODE_BREAK)) { - bitmask_clear_assign(kflags, KBD_RIGHT_SHIFT); pr_debug("Release(KBD_RIGHT_SHIFT)\n"); + bitmask_clear_assign(kflags, KBD_RIGHT_SHIFT); + } else if (scancode == (KEY_LEFT_CONTROL | CODE_BREAK)) { - bitmask_clear_assign(kflags, KBD_LEFT_CONTROL); pr_debug("Release(KBD_LEFT_CONTROL)\n"); + bitmask_clear_assign(kflags, KBD_LEFT_CONTROL); + } else if (scancode == (KEY_RIGHT_CONTROL | CODE_BREAK)) { - bitmask_clear_assign(kflags, KBD_RIGHT_CONTROL); pr_debug("Release(KBD_RIGHT_CONTROL)\n"); + bitmask_clear_assign(kflags, KBD_RIGHT_CONTROL); + } else if (scancode == (KEY_LEFT_ALT | CODE_BREAK)) { - bitmask_clear_assign(kflags, KBD_LEFT_ALT); pr_debug("Release(KBD_LEFT_ALT)\n"); + bitmask_clear_assign(kflags, KBD_LEFT_ALT); + } else if (scancode == (KEY_RIGHT_ALT | CODE_BREAK)) { - bitmask_clear_assign(kflags, KBD_RIGHT_ALT); pr_debug("Release(KBD_RIGHT_ALT)\n"); + bitmask_clear_assign(kflags, KBD_RIGHT_ALT); + } else if (scancode == KEY_CAPS_LOCK) { + pr_debug("Toggle(KBD_CAPS_LOCK)\n"); bitmask_flip_assign(kflags, KBD_CAPS_LOCK); keyboard_update_leds(); - pr_debug("Toggle(KBD_CAPS_LOCK)\n"); + } else if (scancode == KEY_NUM_LOCK) { + pr_debug("Toggle(KBD_NUM_LOCK)\n"); bitmask_flip_assign(kflags, KBD_NUM_LOCK); keyboard_update_leds(); - pr_debug("Toggle(KBD_NUM_LOCK)\n"); + } else if (scancode == KEY_SCROLL_LOCK) { + pr_debug("Toggle(KBD_SCROLL_LOCK)\n"); bitmask_flip_assign(kflags, KBD_SCROLL_LOCK); keyboard_update_leds(); - pr_debug("Toggle(KBD_SCROLL_LOCK)\n"); + } else if (scancode == KEY_BACKSPACE) { - keyboard_push_front('\b'); pr_debug("Press(KEY_BACKSPACE)\n"); + keyboard_push_front('\b'); + } else if ((scancode == KEY_ENTER) || (scancode == KEY_KP_RETURN)) { - keyboard_push_front('\n'); pr_debug("Press(KEY_ENTER)\n"); + keyboard_push_front('\n'); + + } else if (ctrl_pressed && ((scancode == KEY_UP_ARROW) || (keypad_code == KEY_KP8))) { + pr_debug("Press(Ctrl + KEY_UP_ARROW)\n"); + keyboard_push_front_sequence(SEQ_CTRL_UP_ARROW); + + } else if (ctrl_pressed && ((scancode == KEY_DOWN_ARROW) || (keypad_code == KEY_KP2))) { + pr_debug("Press(Ctrl + KEY_DOWN_ARROW)\n"); + keyboard_push_front_sequence(SEQ_CTRL_DOWN_ARROW); + + } else if (ctrl_pressed && ((scancode == KEY_RIGHT_ARROW) || (keypad_code == KEY_KP6))) { + pr_debug("Press(Ctrl + KEY_RIGHT_ARROW)\n"); + keyboard_push_front_sequence(SEQ_CTRL_RIGHT_ARROW); + + } else if (ctrl_pressed && ((scancode == KEY_LEFT_ARROW) || (keypad_code == KEY_KP4))) { + pr_debug("Press(Ctrl + KEY_LEFT_ARROW)\n"); + keyboard_push_front_sequence(SEQ_CTRL_LEFT_ARROW); + } else if ((scancode == KEY_UP_ARROW) || (keypad_code == KEY_KP8)) { pr_debug("Press(KEY_UP_ARROW)\n"); - keyboard_push_sequence("\033[A"); + keyboard_push_front_sequence(SEQ_UP_ARROW); + } else if ((scancode == KEY_DOWN_ARROW) || (keypad_code == KEY_KP2)) { pr_debug("Press(KEY_DOWN_ARROW)\n"); - keyboard_push_sequence("\033[B"); + keyboard_push_front_sequence(SEQ_DOWN_ARROW); + } else if ((scancode == KEY_RIGHT_ARROW) || (keypad_code == KEY_KP6)) { pr_debug("Press(KEY_RIGHT_ARROW)\n"); - keyboard_push_sequence("\033[C"); + keyboard_push_front_sequence(SEQ_RIGHT_ARROW); + } else if ((scancode == KEY_LEFT_ARROW) || (keypad_code == KEY_KP4)) { pr_debug("Press(KEY_LEFT_ARROW)\n"); - keyboard_push_sequence("\033[D"); + keyboard_push_front_sequence(SEQ_LEFT_ARROW); + } else if (scancode == KEY_F1) { pr_debug("Press(KEY_F1)\n"); - keyboard_push_sequence("\033[11~"); + keyboard_push_front_sequence(SEQ_F1); + } else if (scancode == KEY_F2) { pr_debug("Press(KEY_F2)\n"); - keyboard_push_sequence("\033[12~"); + keyboard_push_front_sequence(SEQ_F2); + } else if (scancode == KEY_F3) { pr_debug("Press(KEY_F3)\n"); - keyboard_push_sequence("\033[13~"); + keyboard_push_front_sequence(SEQ_F3); + } else if (scancode == KEY_F4) { pr_debug("Press(KEY_F4)\n"); - keyboard_push_sequence("\033[14~"); + keyboard_push_front_sequence(SEQ_F4); + } else if (scancode == KEY_F5) { pr_debug("Press(KEY_F5)\n"); - keyboard_push_sequence("\033[15~"); + keyboard_push_front_sequence(SEQ_F5); + } else if (scancode == KEY_F6) { pr_debug("Press(KEY_F6)\n"); - keyboard_push_sequence("\033[17~"); + keyboard_push_front_sequence(SEQ_F6); + } else if (scancode == KEY_F7) { pr_debug("Press(KEY_F7)\n"); - keyboard_push_sequence("\033[18~"); + keyboard_push_front_sequence(SEQ_F7); + } else if (scancode == KEY_F8) { pr_debug("Press(KEY_F8)\n"); - keyboard_push_sequence("\033[19~"); + keyboard_push_front_sequence(SEQ_F8); + } else if (scancode == KEY_F9) { pr_debug("Press(KEY_F9)\n"); - keyboard_push_sequence("\033[20~"); + keyboard_push_front_sequence(SEQ_F9); + } else if (scancode == KEY_F10) { pr_debug("Press(KEY_F10)\n"); - keyboard_push_sequence("\033[21~"); + keyboard_push_front_sequence(SEQ_F10); + } else if (scancode == KEY_F11) { pr_debug("Press(KEY_F11)\n"); - keyboard_push_sequence("\033[23~"); + keyboard_push_front_sequence(SEQ_F11); + } else if (scancode == KEY_F12) { pr_debug("Press(KEY_F12)\n"); - keyboard_push_sequence("\033[24~"); + keyboard_push_front_sequence(SEQ_F12); + } else if ((scancode == KEY_INSERT) || (keypad_code == KEY_KP0)) { pr_debug("Press(KEY_INSERT)\n"); - keyboard_push_sequence("\033[2~"); + keyboard_push_front_sequence(SEQ_INSERT); + } else if ((scancode == KEY_DELETE) || (keypad_code == KEY_KP_DEC)) { pr_debug("Press(KEY_DELETE)\n"); - keyboard_push_sequence("\033[3~"); + keyboard_push_front_sequence(SEQ_DELETE); + } else if ((scancode == KEY_HOME) || (keypad_code == KEY_KP7)) { pr_debug("Press(KEY_HOME)\n"); - keyboard_push_sequence("\033[1~"); + keyboard_push_front_sequence(SEQ_HOME); + } else if ((scancode == KEY_END) || (keypad_code == KEY_KP1)) { pr_debug("Press(KEY_END)\n"); - keyboard_push_sequence("\033[4~"); + keyboard_push_front_sequence(SEQ_END); + } else if ((scancode == KEY_PAGE_UP) || (keypad_code == KEY_KP9)) { pr_debug("Press(KEY_PAGE_UP)\n"); - keyboard_push_sequence("\033[5~"); + keyboard_push_front_sequence(SEQ_PAGE_UP); + } else if ((scancode == KEY_PAGE_DOWN) || (keypad_code == KEY_KP3)) { pr_debug("Press(KEY_PAGE_DOWN)\n"); - keyboard_push_sequence("\033[6~"); + keyboard_push_front_sequence(SEQ_PAGE_DOWN); + } else if (scancode == KEY_ESCAPE) { // Nothing to do. - } else if (keypad_code == KEY_5) { - // Nothing to do. + } else if (!(scancode & CODE_BREAK)) { - pr_debug("scancode : %04x\n", scancode); // Get the current keymap. const keymap_t *keymap = get_keymap(scancode); + pr_debug("scancode : %04x (%c)\n", scancode, keymap->normal); // Get the specific keymap. - int character = 0; - if (!bitmask_check(kflags, KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT) != !bitmask_check(kflags, KBD_CAPS_LOCK)) { + if (!shift_pressed != !caps_lock_pressed) { keyboard_push_front(keymap->shift); - } else if ((get_keymap_type() == KEYMAP_IT) && bitmask_check(kflags, KBD_RIGHT_ALT) && (bitmask_check(kflags, KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { + } else if ((get_keymap_type() == KEYMAP_IT) && right_alt_pressed && shift_pressed) { keyboard_push_front(keymap->alt); - } else if (bitmask_check(kflags, KBD_RIGHT_ALT)) { + } else if (right_alt_pressed) { keyboard_push_front(keymap->alt); - } else if (bitmask_check(kflags, KBD_LEFT_CONTROL | KBD_RIGHT_CONTROL)) { + } else if (ctrl_pressed) { keyboard_push_front(keymap->ctrl); } else { keyboard_push_front(keymap->normal); @@ -305,7 +414,7 @@ void keyboard_disable(void) int keyboard_initialize(void) { // Initialize the ring-buffer for the scancodes. - fs_rb_scancode_init(&scancodes); + rb_keybuffer_init(&scancodes); // Initialize the spinlock. spinlock_init(&scancodes_lock); // Initialize the keymaps. diff --git a/mentos/src/drivers/mem.c b/mentos/src/drivers/mem.c index b64e3d98..ae9b1623 100644 --- a/mentos/src/drivers/mem.c +++ b/mentos/src/drivers/mem.c @@ -3,12 +3,11 @@ /// @copyright (c) 2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -// Include the kernel log levels. -#include "sys/kernel_levels.h" -/// Change the header. -#define __DEBUG_HEADER__ "[MEMDEV ]" -/// Set the log level. -#define __DEBUG_LEVEL__ GLOBAL_LOGLEVEL +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[MEMDEV]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "drivers/mem.h" @@ -17,69 +16,96 @@ #include "string.h" #include "sys/errno.h" #include "system/syscall.h" +#include "process/scheduler.h" +#include "fcntl.h" +#include "sys/stat.h" -struct memdev; - +/// @brief Structure representing a memory device. struct memdev { - vfs_file_t *file; - struct memdev *next; + vfs_file_t *file; ///< Pointer to the associated file. + struct memdev *next; ///< Pointer to the next memory device in the list. }; +/// @brief Head of the linked list of memory devices. static struct memdev *devices; -static void add_device(struct memdev *device) +/// @brief Adds a memory device to the linked list. +/// +/// @param device Pointer to the memory device to add. +/// @return 0 on success, -EINVAL if the device is NULL. +static int add_device(struct memdev *device) { + // Check if the device is NULL. + if (device == NULL) { + pr_crit("add_device: NULL device provided\n"); + return -EINVAL; // Return error for NULL device. + } struct memdev *dit = devices; - for (; dit != NULL; dit = dit->next); + // Traverse to the end of the list. + for (; dit && dit->next; dit = dit->next); + // Add the device to the list. if (dit == NULL) { devices = device; } else { dit->next = device; } + return 0; // Return success. } +/// @brief Finds a device file by its path. +/// +/// @details This function traverses the list of memory devices to find a file +/// that matches the provided path. +/// +/// @param path The path of the file to search for. +/// @return A pointer to the vfs_file_t if found, otherwise NULL. static vfs_file_t *find_device_file(const char *path) { + // Check if the path is NULL. + if (path == NULL) { + pr_crit("find_device_file: NULL path provided\n"); + return NULL; + } + // Traverse the linked list of devices. for (struct memdev *dev = devices; dev != NULL; dev = dev->next) { + // Check if the file is NULL to avoid dereferencing a NULL pointer. + if (dev->file == NULL) { + pr_crit("find_device_file: NULL file found in device\n"); + continue; + } + // Compare the file name with the provided path. if (strcmp(dev->file->name, path) == 0) { return dev->file; } } + // Return NULL if no device file matches the given path. return NULL; } static int mem_stat(const char *path, stat_t *stat); +/// @brief System operations for the memory device. static vfs_sys_operations_t mem_sys_operations = { .mkdir_f = NULL, .rmdir_f = NULL, .stat_f = mem_stat, }; +static vfs_file_t *null_mount_callback(const char *path, const char *device); static vfs_file_t *null_open(const char *path, int flags, mode_t mode); static int null_close(vfs_file_t *file); static ssize_t null_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size); static ssize_t null_read(vfs_file_t *file, char *buffer, off_t offset, size_t size); static int null_fstat(vfs_file_t *file, stat_t *stat); -/// @brief The mount call-back, which prepares everything and calls the actual -/// NULL mount function. -/// @param path the path where the filesystem should be mounted. -/// @param device the device we mount. -/// @return the VFS file of the filesystem. -static vfs_file_t *null_mount_callback(const char *path, const char *device) -{ - pr_err("mount_callback(%s, %s): NULL has no mount callback!\n", path, device); - return NULL; -} - -/// Filesystem information. +/// @brief Filesystem type structure for the null device. static file_system_type null_file_system_type = { .name = "null", .fs_flags = 0, .mount = null_mount_callback }; +/// @brief File operations for the null device. static vfs_file_operations_t null_fs_operations = { .open_f = null_open, .unlink_f = NULL, @@ -92,66 +118,233 @@ static vfs_file_operations_t null_fs_operations = { .getdents_f = NULL }; +/// @brief Retrieves the status information of a memory device file. +/// +/// @details This function retrieves the file status (e.g., size, permissions) +/// for a memory device file specified by the given path. +/// +/// @param path The path of the memory device file. +/// @param stat A pointer to a stat_t structure where the file status will be stored. +/// @return 0 on success, -ENOENT if the file is not found. +static int mem_stat(const char *path, stat_t *stat) +{ + // Check if the path or stat pointer is NULL. + if (path == NULL) { + pr_crit("mem_stat: NULL path provided\n"); + return -EINVAL; + } + if (stat == NULL) { + pr_crit("mem_stat: NULL stat pointer provided\n"); + return -EINVAL; + } + // Find the memory device file by path. + vfs_file_t *file = find_device_file(path); + // Check if the file was found. + if (file != NULL) { + // Call the stat_f operation to retrieve the file status. + return file->fs_operations->stat_f(file, stat); + } + // Return -ENOENT if the file was not found. + return -ENOENT; +} + +/// @brief The mount callback, which prepares everything and calls the actual +/// NULL mount function. +/// +/// @details This function serves as a callback for mounting a NULL filesystem. +/// Since the NULL device does not support mounting, this function logs an error +/// and returns NULL. +/// +/// @param path The path where the filesystem should be mounted. +/// @param device The device to be mounted (unused in this case as NULL has no mount functionality). +/// @return Always returns NULL as mounting is not supported for NULL devices. +static vfs_file_t *null_mount_callback(const char *path, const char *device) +{ + // Log an error indicating that NULL devices do not support mounting. + pr_err("null_mount_callback(%s, %s): NULL device does not support mounting!\n", path, device); + return NULL; +} + +/// @brief Creates a null memory device. +/// +/// @details This function allocates memory for a new null device and its associated +/// file, initializes the file structure, and sets appropriate operations. +/// +/// @param name The name of the null device to be created. +/// @return A pointer to the newly created memory device, or NULL if creation fails. static struct memdev *null_device_create(const char *name) { - // Create the device. + // Check if the name is NULL. + if (name == NULL) { + pr_err("null_device_create: NULL name provided\n"); + return NULL; + } + + // Allocate memory for the new device. struct memdev *dev = kmalloc(sizeof(struct memdev)); - dev->next = NULL; + if (dev == NULL) { + pr_err("null_device_create: Failed to allocate memory for device\n"); + return NULL; + } + dev->next = NULL; - // Create the file. + // Allocate memory for the associated file. vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); if (file == NULL) { - pr_err("Failed to create null device.\n"); + pr_err("null_device_create: Failed to allocate memory for file\n"); + kfree(dev); // Free the previously allocated device memory. return NULL; } dev->file = file; - // Set the device name. - strncpy(file->name, name, NAME_MAX); + // Set the device name, ensuring it doesn't exceed NAME_MAX. + strncpy(file->name, name, NAME_MAX - 1); + file->name[NAME_MAX - 1] = '\0'; // Ensure null termination. + + // Initialize file fields. file->count = 0; file->uid = 0; file->gid = 0; - file->mask = 0x2000 | 0666; - file->atime = sys_time(NULL); - file->mtime = sys_time(NULL); - file->ctime = sys_time(NULL); - file->length = 0; - // Set the operations. + file->mask = 0x2000 | 0666; // Regular file with rw-rw-rw- permissions. + file->atime = sys_time(NULL); // Set access time. + file->mtime = sys_time(NULL); // Set modification time. + file->ctime = sys_time(NULL); // Set change time. + file->length = 0; // Initialize file length to 0. + + // Set the file system and system operations for the file. file->sys_operations = &mem_sys_operations; file->fs_operations = &null_fs_operations; + return dev; } +/// @brief Opens a null device file. +/// +/// @param path The path of the null device file to open. +/// @param flags Flags specifying the access mode and other options for opening the file. +/// @param mode The mode specifying permissions for the file (not currently used). +/// @return A pointer to the opened vfs_file_t structure, or NULL if the file is not found. static vfs_file_t *null_open(const char *path, int flags, mode_t mode) { + // Check if the path is NULL. + if (path == NULL) { + pr_crit("null_open: NULL path provided\n"); + return NULL; + } + + // Find the null device file by its path. vfs_file_t *file = find_device_file(path); - if (file) { - file->count++; + if (file == NULL) { + // Log an error if the file was not found. + pr_crit("null_open: File not found for path %s\n", path); + return NULL; } - // TODO: check permissions + + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check read or write access based on the flags. + if (flags & O_RDONLY) { + // Check read permission. + if (((file->uid == task->uid) && !(file->mask & S_IRUSR)) || // Check owner read permission. + ((file->gid == task->gid) && !(file->mask & S_IRGRP)) || // Check group read permission. + (!(file->mask & S_IROTH))) { // Check others read permission. + pr_err("null_open: Permission denied for read access to %s\n", path); + return NULL; + } + } + + if (flags & O_WRONLY || flags & O_RDWR) { + // Check write permission. + if (((file->uid == task->uid) && !(file->mask & S_IWUSR)) || // Check owner write permission. + ((file->gid == task->gid) && !(file->mask & S_IWGRP)) || // Check group write permission. + (!(file->mask & S_IWOTH))) { // Check others write permission. + pr_err("null_open: Permission denied for write access to %s\n", path); + return NULL; + } + } + + // Increment the reference count for the file. + file->count++; + return file; } +/// @brief Closes a null device file. +/// +/// @param file A pointer to the vfs_file_t structure representing the file to close. +/// @return 0 on success, -EINVAL if the file is NULL. static int null_close(vfs_file_t *file) { - assert(file && "Received null file."); - file->count--; + // Check if the file is NULL. + if (file == NULL) { + pr_crit("null_close: Received NULL file\n"); + return -EINVAL; // Return an error if the file is NULL. + } + + // Decrease the reference count. + if (file->count > 0) { + file->count--; + } else { + pr_warning("null_close: Attempt to close a file with zero reference count\n"); + } + return 0; } - +/// @brief Writes data to a null device file. +/// +/// @param file A pointer to the vfs_file_t structure representing the file to write to. +/// @param buffer The buffer containing the data to write (unused). +/// @param offset The offset from where the write operation should begin (unused). +/// @param size The number of bytes to write. +/// @return The size of the data that would have been written, or -EINVAL if the file is NULL. static ssize_t null_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size) { - return size; + // Check if the file is NULL. + if (file == NULL) { + pr_crit("null_write: Received NULL file\n"); + return -EINVAL; // Return an error if the file is NULL. + } + + // Since this is a null device, the data is discarded. + return size; // Simulate a successful write by returning the size. } +/// @brief Reads data from a null device file. +/// +/// @param file A pointer to the vfs_file_t structure representing the file to read from. +/// @param buffer The buffer where the read data would be stored (unused). +/// @param offset The offset from where the read operation should begin (unused). +/// @param size The number of bytes to read (unused). +/// @return Always returns 0, or -EINVAL if the file is NULL. static ssize_t null_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) { + // Check if the file is NULL. + if (file == NULL) { + pr_crit("null_read: Received NULL file\n"); + return -EINVAL; // Return an error if the file is NULL. + } + + // Since this is a null device, no data is provided, and we always return 0. return 0; } +/// @brief Retrieves the file status for a null device. +/// +/// @param file A pointer to the vfs_file_t structure representing the file. +/// @param stat A pointer to the stat_t structure where the file status will be stored. +/// @return 0 on success, or -EINVAL if the file or stat is NULL. static int null_fstat(vfs_file_t *file, stat_t *stat) { + // Check if the file or stat pointers are NULL. + if (file == NULL || stat == NULL) { + pr_crit("null_fstat: Received NULL file or stat pointer\n"); + return -EINVAL; // Return an error if either pointer is NULL. + } + + // Log debug information. pr_debug("null_fstat(%s, %p)\n", file->name, stat); + stat->st_dev = 0; stat->st_ino = 0; stat->st_mode = file->mask; @@ -164,31 +357,32 @@ static int null_fstat(vfs_file_t *file, stat_t *stat) return 0; } -static int mem_stat(const char *path, stat_t *stat) -{ - vfs_file_t *file = find_device_file(path); - - if (file) { - return file->fs_operations->stat_f(file, stat); - } - return -ENOENT; -} - int mem_devs_initialize(void) { + // Create the /dev/null device. struct memdev *devnull = null_device_create("/dev/null"); if (!devnull) { - pr_err("Failed to create devnull"); - return -ENODEV; + pr_err("mem_devs_initialize: Failed to create /dev/null\n"); + return -ENODEV; // Return error if device creation fails. } + + // Register the null filesystem type. if (!vfs_register_filesystem(&null_file_system_type)) { - pr_err("Failed to register NULL filesystem."); - return 1; + pr_err("mem_devs_initialize: Failed to register NULL filesystem\n"); + return 1; // Return error if filesystem registration fails. } + + // Mount the /dev/null device. if (!vfs_register_superblock("null", "/dev/null", &null_file_system_type, devnull->file)) { - pr_err("Failed to mount /dev/null."); - return 1; + pr_err("mem_devs_initialize: Failed to mount /dev/null\n"); + return 1; // Return error if mounting fails. } - add_device(devnull); - return 0; + + // Add the null device to the list of memory devices. + if (add_device(devnull) < 0) { + pr_err("mem_devs_initialize: Failed to register NULL filesystem\n"); + return 1; // Return error if filesystem registration fails. + } + + return 0; // Return success. } diff --git a/mentos/src/fs/attr.c b/mentos/src/fs/attr.c index 13314200..9db3b78c 100644 --- a/mentos/src/fs/attr.c +++ b/mentos/src/fs/attr.c @@ -17,58 +17,78 @@ #include "system/printk.h" #include "system/syscall.h" -static int __setattr(const char *path, struct iattr* attr, bool_t follow_links) { - // Allocate a variable for the path. +/// @brief Sets attributes on a file or directory. +/// +/// @param path The path of the file or directory whose attributes are to be set. +/// @param attr A pointer to the iattr structure containing the attributes to set. +/// @param follow_links A flag to indicate whether symbolic links should be followed. +/// @return 0 on success, or an appropriate error code on failure. +static int __setattr(const char *path, struct iattr *attr, bool_t follow_links) +{ + // Allocate a variable for the resolved absolute path. char absolute_path[PATH_MAX]; - int ret = resolve_path(path, absolute_path, sizeof(absolute_path), follow_links ? FOLLOW_LINKS: 0); + // Resolve the path to its absolute form, optionally following symbolic links. + int ret = resolve_path(path, absolute_path, sizeof(absolute_path), follow_links ? FOLLOW_LINKS : 0); if (ret < 0) { - pr_err("__setattr(%s): Cannot get the absolute path.", path); - return ret; + pr_err("__setattr(%s): Cannot resolve the absolute path\n", path); + return ret; // Return the error from resolve_path. } - + // Retrieve the superblock for the resolved absolute path. super_block_t *sb = vfs_get_superblock(absolute_path); if (sb == NULL) { - pr_err("do_chown(%s): Cannot find the superblock!\n", absolute_path); - return -ENOENT; + pr_err("__setattr(%s): Cannot find the superblock!\n", absolute_path); + return -ENOENT; // Return error if superblock is not found. } + // Retrieve the root of the superblock. vfs_file_t *sb_root = sb->root; if (sb_root == NULL) { - pr_err("do_chown(%s): Cannot find the superblock root!\n", absolute_path); - return -ENOENT; + pr_err("__setattr(%s): Cannot find the superblock root!\n", absolute_path); + return -ENOENT; // Return error if the superblock root is not found. } - - // Check if the function is implemented. + // Check if the setattr operation is supported by the filesystem. if (sb_root->sys_operations->setattr_f == NULL) { - pr_err("setattr(%s): Function not supported in current filesystem.", absolute_path); - return -ENOSYS; + pr_err("__setattr(%s): Function not supported in current filesystem\n", absolute_path); + return -ENOSYS; // Return error if setattr is not implemented. } + // Call the setattr operation with the resolved absolute path and attribute. return sb_root->sys_operations->setattr_f(absolute_path, attr); } -static inline void __iattr_set_owner_or_group(struct iattr *attr, uid_t owner, gid_t group) { - if (owner != -1) { - attr->ia_valid |= ATTR_UID; - attr->ia_uid = owner; - } - if (group != -1) { - attr->ia_valid |= ATTR_GID; - attr->ia_gid = group; +/// @brief Sets the owner and/or group in the iattr structure. +/// +/// @param attr A pointer to the iattr structure to update. +/// @param owner The new owner UID to set (use `-1` to leave unchanged). +/// @param group The new group GID to set (use `-1` to leave unchanged). +static inline void __iattr_set_owner_or_group(struct iattr *attr, uid_t owner, gid_t group) +{ + // Set the owner UID if the provided owner is not -1. + if (owner != (uid_t)-1) { + attr->ia_valid |= ATTR_UID; // Mark the UID as valid. + attr->ia_uid = owner; // Set the UID. + } + // Set the group GID if the provided group is not -1. + if (group != (gid_t)-1) { + attr->ia_valid |= ATTR_GID; // Mark the GID as valid. + attr->ia_gid = group; // Set the GID. } } -int sys_chown(const char* path, uid_t owner, gid_t group) { - struct iattr attr = {0}; +int sys_chown(const char *path, uid_t owner, gid_t group) +{ + struct iattr attr = { 0 }; __iattr_set_owner_or_group(&attr, owner, group); return __setattr(path, &attr, true); } -int sys_lchown(const char* path, uid_t owner, gid_t group) { - struct iattr attr = {0}; +int sys_lchown(const char *path, uid_t owner, gid_t group) +{ + struct iattr attr = { 0 }; __iattr_set_owner_or_group(&attr, owner, group); return __setattr(path, &attr, false); } -int sys_fchown(int fd, uid_t owner, gid_t group) { +int sys_fchown(int fd, uid_t owner, gid_t group) +{ task_struct *task = scheduler_get_current_process(); // Check the current FD. @@ -98,17 +118,19 @@ int sys_fchown(int fd, uid_t owner, gid_t group) { pr_err("No setattr function found for the current filesystem.\n"); return -ENOSYS; } - struct iattr attr = {0}; + struct iattr attr = { 0 }; __iattr_set_owner_or_group(&attr, owner, group); return file->fs_operations->setattr_f(file, &attr); } -int sys_chmod(const char* path, mode_t mode) { +int sys_chmod(const char *path, mode_t mode) +{ struct iattr attr = IATTR_CHMOD(mode); return __setattr(path, &attr, true); } -int sys_fchmod(int fd, mode_t mode) { +int sys_fchmod(int fd, mode_t mode) +{ task_struct *task = scheduler_get_current_process(); // Check the current FD. diff --git a/mentos/src/io/proc_feedback.c b/mentos/src/io/proc_feedback.c index 95fb2f52..2eff31c3 100644 --- a/mentos/src/io/proc_feedback.c +++ b/mentos/src/io/proc_feedback.c @@ -9,15 +9,28 @@ #include "string.h" #include "sys/errno.h" +/// @brief Reads data from the /proc/feedback file. +/// +/// @param file A pointer to the vfs_file_t structure representing the file to read from. +/// @param buf A buffer to store the read data (unused in this example). +/// @param offset The offset from where the read operation should begin (unused in this example). +/// @param nbyte The number of bytes to read (unused in this example). +/// @return Always returns 0 on success, or -ENOENT if the file is NULL. static ssize_t procfb_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) { + // Check if the file pointer is NULL. if (!file) { - pr_err("Received a NULL file.\n"); - return -ENOENT; + pr_err("procfb_read: Received a NULL file.\n"); + return -ENOENT; // Return an error if the file is NULL. } + + // Check if the file name matches "/proc/feedback". if (!strcmp(file->name, "/proc/feedback")) { - pr_alert("Return scheduling feedback information.\n"); + pr_alert("procfb_read: Returning scheduling feedback information.\n"); + // TODO: Add logic to return actual feedback information here. } + + // Return 0 to indicate success. return 0; } diff --git a/mentos/src/io/proc_video.c b/mentos/src/io/proc_video.c index 957630b4..b5dfd7f5 100644 --- a/mentos/src/io/proc_video.c +++ b/mentos/src/io/proc_video.c @@ -24,14 +24,13 @@ /// @brief Prints the ring-buffer. /// @param rb the ring-buffer to print. -void print_rb(fs_rb_scancode_t *rb) +void rb_keybuffer_print(rb_keybuffer_t *rb) { - if (!fs_rb_scancode_empty(rb)) { - for (unsigned i = rb->read; (i < rb->write) && (i < rb->size); ++i) { - pr_debug("%c", rb->buffer[i]); - } - pr_debug("\n"); + pr_debug("(%u)[ ", rb->count); + for (unsigned i = 0; i < rb->count; ++i) { + pr_debug("%d ", rb_keybuffer_get(rb, i)); } + pr_debug("]\n"); } /// @brief Read function for the proc video system. @@ -50,7 +49,7 @@ static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyt // Get the currently running process. task_struct *process = scheduler_get_current_process(); // Get a pointer to its keyboard ring buffer. - fs_rb_scancode_t *rb = &process->keyboard_rb; + rb_keybuffer_t *rb = &process->keyboard_rb; // Pre-check the terminal flags. bool_t flg_icanon = bitmask_check(process->termios.c_lflag, ICANON) == ICANON; @@ -60,9 +59,10 @@ static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyt // If we are in canonical mode and the last inserted element is a newline, // pop the buffer until it's empty. - if (!fs_rb_scancode_empty(rb) && (!flg_icanon || (fs_rb_scancode_front(rb) == '\n'))) { + if (!rb_keybuffer_is_empty(rb) && (!flg_icanon || (rb_keybuffer_peek_front(rb) == '\n'))) { // Return the newline character. - *((char *)buf) = fs_rb_scancode_pop_back(rb) & 0x00FF; + rb_keybuffer_print(rb); + *((char *)buf) = rb_keybuffer_pop_back(rb) & 0x00FF; // Indicate a character has been returned. return 1; } @@ -78,81 +78,83 @@ static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyt // Keep only the character, not the scancode. c &= 0x00FF; - // Handle backspace. - if (c == '\b') { - // If ECHOE is off and ECHO is on, show the `^?` string. + if (iscntrl(c)) + pr_debug("[ ](%d)\n", c); + else + pr_debug("[%c](%d)\n", c, c); + + // Handle special characters. + switch (c) { + case '\n': + case '\t': + // Optionally display the character if echo is enabled. + if (flg_echo) { + video_putc(c); + } + // Return the character. + *((char *)buf) = c; + return 1; + + case '\b': + // Handle backspace. if (!flg_echoe && flg_echo) { video_puts("^?"); } - - // If we are in canonical mode, pop the previous character. if (flg_icanon) { - // Remove the last character from the buffer. - fs_rb_scancode_pop_front(rb); - - // Optionally display the backspace character. - if (flg_echoe) { video_putc(c); } + // Canonical mode: Remove the last character from the buffer. + if (!rb_keybuffer_is_empty(rb)) { + rb_keybuffer_pop_front(rb); + if (flg_echoe) { + video_putc(c); + } + } + return 0; // No character returned for backspace. } else { - // Add the backspace character to the buffer. - fs_rb_scancode_push_front(rb, c); - - // Return the backspace character. - *((char *)buf) = fs_rb_scancode_pop_back(rb) & 0x00FF; - - // Indicate a character has been returned. + // Non-canonical mode: Add backspace to the buffer and return it. + rb_keybuffer_push_front(rb, c); + *((char *)buf) = rb_keybuffer_pop_back(rb) & 0x00FF; return 1; } - // No character returned for backspace handling. - return 0; - } - - // Handle delete key (0x7F). - if (c == 0x7F) { + case 127: // Delete key. + // Optionally display the character if echo is enabled. if (flg_echo) { - // Print escape sequence for delete. - video_puts("^[[3~"); - } - - // Indicate no character was returned. - return 0; - } - - // Add the character to the buffer. - fs_rb_scancode_push_front(rb, c); - - if (iscntrl(c)) { - if ((c == 0x03) && (flg_isig)) { - sys_kill(process->pid, SIGTERM); - } else if ((c == 0x1A) && (flg_isig)) { - sys_kill(process->pid, SIGSTOP); + video_putc(c); } + // Return the character. + *((char *)buf) = c; + return 1; - if (isalpha('A' + (c - 1)) && (c != '\n') && (c != '\b') && (c != '\t')) { - // If echo is activated, output the character to video. - if (flg_echo) { - video_putc('^'); - video_putc('A' + (c - 1)); + default: + if (iscntrl(c)) { + // Handle control characters in both canonical and non-canonical modes. + if (flg_isig) { + if (c == 0x03) { + // Send SIGTERM on Ctrl+C. + sys_kill(process->pid, SIGTERM); + return 0; + } else if (c == 0x1A) { + // Send SIGSTOP on Ctrl+Z. + sys_kill(process->pid, SIGSTOP); + return 0; + } + } + if (isalpha('A' + (c - 1))) { + // Display control character representation (e.g., ^A). + if (flg_echo) { + video_putc('^'); + video_putc('A' + (c - 1)); + } } - - fs_rb_scancode_push_front(rb, '\033'); - fs_rb_scancode_push_front(rb, '^'); - fs_rb_scancode_push_front(rb, 'A' + (c - 1)); - - return 3; } - // Echo the character. - // if (flg_echo) { - // video_putc(c); - // } - // return 1; } - // If we are NOT in canonical mode, send the character back to user immediately. + // Add the character to the buffer. + rb_keybuffer_push_front(rb, c); + + // If not in canonical mode, return the character immediately. if (!flg_icanon) { - // Return the character. - *((char *)buf) = fs_rb_scancode_pop_back(rb) & 0x00FF; - // Indicate a character has been returned. + *((char *)buf) = rb_keybuffer_pop_back(rb) & 0x00FF; return 1; } @@ -160,6 +162,13 @@ static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyt return 0; } +/// @brief Writes data to the video output by sending each character from the buffer to the video output. +/// +/// @param file Pointer to the file structure (unused). +/// @param buf Pointer to the buffer containing the data to write. +/// @param offset Offset within the file (unused). +/// @param nbyte Number of bytes to write from the buffer. +/// @return ssize_t The number of bytes written. static ssize_t procv_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) { for (size_t i = 0; i < nbyte; ++i) { @@ -167,6 +176,13 @@ static ssize_t procv_write(vfs_file_t *file, const void *buf, off_t offset, size } return nbyte; } + +/// @brief Handles ioctl requests for the process, including terminal settings (TCGETS and TCSETS). +/// +/// @param file Pointer to the file structure (unused). +/// @param request The ioctl request code (e.g., TCGETS, TCSETS). +/// @param data Pointer to the data structure for the ioctl request (e.g., termios). +/// @return int Returns 0 on success. static int procv_ioctl(vfs_file_t *file, int request, void *data) { task_struct *process = scheduler_get_current_process(); diff --git a/mentos/src/io/video.c b/mentos/src/io/video.c index 5fd8abb7..20be4e0b 100644 --- a/mentos/src/io/video.c +++ b/mentos/src/io/video.c @@ -108,6 +108,70 @@ static inline void __draw_char(char c) *(pointer++) = color; } +/// @brief Hides the VGA cursor. +void __video_hide_cursor(void) +{ + outportb(0x3D4, 0x0A); + unsigned char cursor_start = inportb(0x3D5); + outportb(0x3D5, cursor_start | 0x20); // Set the most significant bit to disable the cursor. +} + +/// @brief Shows the VGA cursor. +void __video_show_cursor(void) +{ + outportb(0x3D4, 0x0A); + unsigned char cursor_start = inportb(0x3D5); + outportb(0x3D5, cursor_start & 0xDF); // Clear the most significant bit to enable the cursor. +} + +/// @brief Sets the VGA cursor shape by specifying the start and end scan lines. +/// +/// @param start The starting scan line of the cursor (0-15). +/// @param end The ending scan line of the cursor (0-15). +void __video_set_cursor_shape(unsigned char start, unsigned char end) +{ + // Set the cursor's start scan line + outportb(0x3D4, 0x0A); + outportb(0x3D5, start); + + // Set the cursor's end scan line + outportb(0x3D4, 0x0B); + outportb(0x3D5, end); +} + +/// @brief Issue the vide to move the cursor to the given position. +/// @param x The x coordinate. +/// @param y The y coordinate. +static inline void __video_set_cursor_position(unsigned int x, unsigned int y) +{ + uint32_t position = y * WIDTH + x; + // Cursor LOW port to VGA index register. + outportb(0x3D4, 0x0F); + outportb(0x3D5, (uint8_t)(position & 0xFFU)); + // Cursor HIGH port to VGA index register. + outportb(0x3D4, 0x0E); + outportb(0x3D5, (uint8_t)((position >> 8U) & 0xFFU)); +} + +/// @brief Retrieves the current VGA cursor position in terms of x and y coordinates. +/// +/// @param x Pointer to store the x-coordinate (column). +/// @param y Pointer to store the y-coordinate (row). +static inline void __video_get_cursor_position(unsigned int *x, unsigned int *y) +{ + uint16_t position; + + // Get the low byte of the cursor position. + outportb(0x3D4, 0x0F); + position = inportb(0x3D5); + // Get the high byte of the cursor position. + outportb(0x3D4, 0x0E); + position |= ((uint16_t)inportb(0x3D5)) << 8; + // Calculate x and y. + if (x) *x = position % WIDTH; + if (y) *y = position / WIDTH; +} + /// @brief Sets the provided ansi code. /// @param ansi_code The ansi code describing background and foreground color. static inline void __set_color(uint8_t ansi_code) @@ -139,7 +203,7 @@ static inline void __move_cursor_backward(int erase, int amount) strcpy(pointer, pointer + 2); } } - video_set_cursor_auto(); + video_update_cursor_position(); } /// @brief Moves the cursor forward. @@ -155,26 +219,43 @@ static inline void __move_cursor_forward(int erase, int amount) pointer += 2; } } - video_set_cursor_auto(); + video_update_cursor_position(); } -/// @brief Issue the vide to move the cursor to the given position. -/// @param x The x coordinate. -/// @param y The y coordinate. -static inline void __video_set_cursor(unsigned int x, unsigned int y) +/// @brief Parses the cursor shape escape code and sets the cursor shape accordingly. +/// @param shape The integer representing the cursor shape code. +static inline void __parse_cursor_escape_code(int shape) { - uint32_t position = x * WIDTH + y; - // Cursor LOW port to vga INDEX register. - outportb(0x3D4, 0x0F); - outportb(0x3D5, (uint8_t)(position & 0xFFU)); - // Cursor HIGH port to vga INDEX register. - outportb(0x3D4, 0x0E); - outportb(0x3D5, (uint8_t)((position >> 8U) & 0xFFU)); + switch (shape) { + case 0: // Default blinking block cursor + case 2: // Blinking block cursor + __video_set_cursor_shape(0, 15); + break; + case 1: // Steady block cursor + __video_set_cursor_shape(0, 15); + break; + case 3: // Blinking underline cursor + __video_set_cursor_shape(13, 15); + break; + case 4: // Steady underline cursor + __video_set_cursor_shape(13, 15); + break; + case 5: // Blinking vertical bar cursor + __video_set_cursor_shape(0, 1); + break; + case 6: // Steady vertical bar cursor + __video_set_cursor_shape(0, 1); + break; + default: + // Handle any other cases if needed + break; + } } void video_init(void) { video_clear(); + __parse_cursor_escape_code(0); } void video_update(void) @@ -200,15 +281,40 @@ void video_putc(int c) escape_buffer[escape_index] = 0; if (isalpha(c)) { escape_buffer[--escape_index] = 0; + + // Move cursor forward (e.g., ESC [ C) if (c == 'C') { __move_cursor_forward(false, atoi(escape_buffer)); - } else if (c == 'D') { + } + // Move cursor backward (e.g., ESC [ D) + else if (c == 'D') { __move_cursor_backward(false, atoi(escape_buffer)); - } else if (c == 'm') { + } + // Set color (e.g., ESC [ m) + else if (c == 'm') { __set_color(atoi(escape_buffer)); - } else if (c == 'J') { + } + // Clear screen (e.g., ESC [ J) + else if (c == 'J') { video_clear(); } + // Clear screen (e.g., ESC [ J) + else if (c == 'H') { + char *semicolon = strchr(escape_buffer, ';'); + if (semicolon != NULL) { + *semicolon = '\0'; + unsigned int y = atoi(escape_buffer); + unsigned int x = atoi(semicolon + 1); + pointer = ADDR + ((y - 1) * WIDTH * 2 + (x - 1) * 2); + } else { + pointer = ADDR; + } + video_update_cursor_position(); + } + // Handle cursor shape (e.g., ESC [ q) + else if (c == 'q') { + __parse_cursor_escape_code(atoi(escape_buffer)); + } escape_index = -1; } return; @@ -239,7 +345,7 @@ void video_putc(int c) } video_shift_one_line_up(); - video_set_cursor_auto(); + video_update_cursor_position(); } void video_puts(const char *str) @@ -255,13 +361,15 @@ void video_puts(const char *str) } } -void video_set_cursor_auto(void) +void video_update_cursor_position(void) { #ifndef VGA_TEXT_MODE if (vga_is_enabled()) return; #endif - __video_set_cursor(((pointer - ADDR) / 2U) / WIDTH, ((pointer - ADDR) / 2U) % WIDTH); + __video_set_cursor_position( + ((pointer - ADDR) / 2U) % WIDTH, + ((pointer - ADDR) / 2U) / WIDTH); } void video_move_cursor(unsigned int x, unsigned int y) @@ -273,7 +381,7 @@ void video_move_cursor(unsigned int x, unsigned int y) } #endif pointer = ADDR + ((y * WIDTH * 2) + (x * 2)); - video_set_cursor_auto(); + video_update_cursor_position(); } void video_get_cursor_position(unsigned int *x, unsigned int *y) @@ -330,7 +438,7 @@ void video_new_line(void) #endif pointer = ADDR + ((pointer - ADDR) / W2 + 1) * W2; video_shift_one_line_up(); - video_set_cursor_auto(); + video_update_cursor_position(); } void video_cartridge_return(void) @@ -344,7 +452,7 @@ void video_cartridge_return(void) pointer = ADDR + ((pointer - ADDR) / W2 - 1) * W2; video_new_line(); video_shift_one_line_up(); - video_set_cursor_auto(); + video_update_cursor_position(); } void video_shift_one_line_up(void) diff --git a/mentos/src/ipc/ipc.c b/mentos/src/ipc/ipc.c index 6076bbfd..e480e6d1 100644 --- a/mentos/src/ipc/ipc.c +++ b/mentos/src/ipc/ipc.c @@ -11,16 +11,32 @@ #include "io/debug.h" #include "process/scheduler.h" -int ipc_check_perm( +/// @brief Checks IPC permissions for a task. +/// @param task Pointer to the task structure. +/// @param perm Pointer to the IPC permissions structure. +/// @param usr Permission flag for user. +/// @param grp Permission flag for group. +/// @param oth Permission flag for others. +/// @return 1 if permissions are granted, 0 otherwise. +static inline int ipc_check_perm( task_struct *task, struct ipc_perm *perm, int usr, int grp, int oth) { - assert(task && "Received a NULL task."); - assert(perm && "Received a NULL perm."); + // Check if task is NULL. + if (!task) { + pr_err("Received a NULL task.\n"); + return 0; + } + // Check if perm is NULL. + if (!perm) { + pr_err("Received a NULL perm.\n"); + return 0; + } int check_parent = (perm->key < 0) && task->parent && (task->parent->pid != 0); + // Check user permissions. if (perm->mode & usr) { if ((perm->uid == task->uid) || (perm->cuid == task->uid)) { return 1; @@ -29,6 +45,7 @@ int ipc_check_perm( return 1; } } + // Check group permissions. if (perm->mode & grp) { if ((perm->gid == task->gid) || (perm->cgid == task->gid)) { return 1; @@ -37,6 +54,7 @@ int ipc_check_perm( return 1; } } + // Check other permissions. if (perm->mode & oth) { if (check_parent && ((perm->uid != task->parent->uid) || (perm->cuid != task->parent->uid))) { return 1; @@ -45,6 +63,7 @@ int ipc_check_perm( return 1; } } + // No permissions granted. return 0; } diff --git a/mentos/src/ipc/shm.c b/mentos/src/ipc/shm.c index 5c25fc6d..7dd7a06e 100644 --- a/mentos/src/ipc/shm.c +++ b/mentos/src/ipc/shm.c @@ -2,7 +2,6 @@ /// @brief /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. -///! @cond Doxygen_Suppress // ============================================================================ // Setup the logging for this file (do this before any other include). @@ -29,15 +28,15 @@ ///@brief A value to compute the shmid value. int __shm_id = 0; -// @brief Shared memory management structure. +/// @brief Shared memory management structure. typedef struct { - /// @brief ID associated to the shared memory. + /// @brief Shared memory ID. int id; - /// @brief The shared memory data strcutre. + /// @brief Shared memory data structure. struct shmid_ds shmid; - /// Where shm created is memorized. + /// @brief Location where shared memory is stored. page_t *shm_location; - /// Reference inside the list of shared memory management structures. + /// @brief List reference for shared memory structures. list_head list; } shm_info_t; @@ -48,19 +47,23 @@ list_head shm_list; // MEMORY MANAGEMENT (Private) // ============================================================================ -/// @brief Allocates the memory for shared memory structure. -/// @param key IPC_KEY associated with the shared memory. -/// @param shmflg flags used to create the shared memory. -/// @return a pointer to the allocated shared memory structure. +/// @brief Allocates memory for a shared memory structure. +/// @param key IPC key associated with the shared memory. +/// @param size Size of the shared memory segment. +/// @param shmflg Flags used to create the shared memory. +/// @return Pointer to the allocated shared memory structure, or NULL on failure. static inline shm_info_t *__shm_info_alloc(key_t key, size_t size, int shmflg) { - // Allocate the memory. + // Allocate memory for shm_info_t. shm_info_t *shm_info = (shm_info_t *)kmalloc(sizeof(shm_info_t)); - // Check the allocated memory. - assert(shm_info && "Failed to allocate memory for a shared memory structure."); - // Clean the memory. + // Check if memory allocation for shm_info failed. + if (!shm_info) { + pr_err("Failed to allocate memory for shared memory structure.\n"); + return NULL; + } + // Initialize the allocated memory to zero. memset(shm_info, 0, sizeof(shm_info_t)); - // Initialize its values. + // Initialize shm_info values. shm_info->id = ++__shm_id; shm_info->shmid.shm_perm = register_ipc(key, shmflg & 0x1FF); shm_info->shmid.shm_segsz = size; @@ -70,10 +73,17 @@ static inline shm_info_t *__shm_info_alloc(key_t key, size_t size, int shmflg) shm_info->shmid.shm_cpid = 0; shm_info->shmid.shm_lpid = 0; shm_info->shmid.shm_nattch = 0; - // Allocate the memory. - uint32_t order = find_nearest_order_greater(0, size); + // Determine the order for memory allocation. + uint32_t order = find_nearest_order_greater(0, size); + // Allocate the shared memory pages. shm_info->shm_location = _alloc_pages(GFP_KERNEL, order); - // Return the shared memory structure. + // Check if memory allocation for shm_location failed. + if (!shm_info->shm_location) { + pr_err("Failed to allocate shared memory pages.\n"); + kfree(shm_info); + return NULL; + } + // Return the allocated shared memory structure. return shm_info; } @@ -149,15 +159,21 @@ static inline shm_info_t *__list_find_shm_info_by_page(page_t *page) return NULL; } +/// @brief Adds a shared memory info structure to the list. +/// @param shm_info Pointer to the shared memory info structure. static inline void __list_add_shm_info(shm_info_t *shm_info) { + // Check if shm_info is NULL. assert(shm_info && "Received a NULL pointer."); // Add the new item at the end. list_head_insert_before(&shm_info->list, &shm_list); } +/// @brief Removes a shared memory info structure from the list. +/// @param shm_info Pointer to the shared memory info structure. static inline void __list_remove_shm_info(shm_info_t *shm_info) { + // Check if shm_info is NULL. assert(shm_info && "Received a NULL pointer."); // Delete the item from the list. list_head_remove(&shm_info->list); @@ -184,6 +200,9 @@ long sys_shmget(key_t key, size_t size, int shmflg) } while (__list_find_shm_info_by_key(key)); // We have a unique key, create the shared memory. shm_info = __shm_info_alloc(key, size, shmflg); + if (!shm_info) { + return -ENOENT; + } // Add the shared memory to the list. __list_add_shm_info(shm_info); } else { @@ -209,6 +228,9 @@ long sys_shmget(key_t key, size_t size, int shmflg) if (shm_info == NULL) { // Create the shared memory. shm_info = __shm_info_alloc(key, size, shmflg); + if (!shm_info) { + return -ENOENT; + } // Add the shared memory to the list. __list_add_shm_info(shm_info); } @@ -339,28 +361,38 @@ long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf) // PROCFS FUNCTIONS // ============================================================================ +/// @brief Reads data from a shared memory segment. +/// @param file Pointer to the file structure associated with the shared memory. +/// @param buf Buffer where the read data will be stored. +/// @param offset Offset from where to start reading. +/// @param nbyte Number of bytes to read. +/// @return Number of bytes read on success, or -1 on error. ssize_t procipc_shm_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) { + // Check if file is NULL. if (!file) { pr_err("Received a NULL file.\n"); return -ENOENT; } + size_t buffer_len = 0, read_pos = 0, ret = 0; ssize_t write_count = 0; shm_info_t *shm_info = NULL; char buffer[BUFSIZ]; - // Prepare a buffer. + // Prepare a buffer and initialize it to zero. memset(buffer, 0, BUFSIZ); + // Prepare the header. ret = sprintf(buffer, "key shmid perms segsz uid gid cuid cgid atime dtime ctime cpid lpid nattch\n"); // Iterate through the list of shared memory. list_for_each_decl(it, &shm_list) { - // Get the current entry. + // Get the current entry from the list. shm_info = list_entry(it, shm_info_t, list); - // Add the line. + + // Add information about the current shared memory entry to the buffer. ret += sprintf( buffer + ret, "%8d %5d %10d %7d %5d %4d %5d %9d %10d %10d %10d %5d %5d %5d\n", abs(shm_info->shmid.shm_perm.key), @@ -378,19 +410,24 @@ ssize_t procipc_shm_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte shm_info->shmid.shm_lpid, shm_info->shmid.shm_nattch); } + + // Terminate the buffer with a newline. sprintf(buffer + ret, "\n"); - // Perform read. + // Get the length of the buffer. buffer_len = strlen(buffer); read_pos = offset; + + // Perform the read operation if offset is within bounds. if (read_pos < buffer_len) { while ((write_count < nbyte) && (read_pos < buffer_len)) { buf[write_count] = buffer[read_pos]; // Move the pointers. - ++read_pos, ++write_count; + ++read_pos; + ++write_count; } } + + // Return the number of bytes read. return write_count; } - -///! @endcond diff --git a/mentos/src/ipc/shm.old.c b/mentos/src/ipc/shm.old.c deleted file mode 100644 index 85013753..00000000 --- a/mentos/src/ipc/shm.old.c +++ /dev/null @@ -1,495 +0,0 @@ -/// @file shm.c -/// @brief -/// @copyright (c) 2014-2024 This file is distributed under the MIT License. -/// See LICENSE.md for details. -///! @cond Doxygen_Suppress - -// ============================================================================ -// Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[IPCshm]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. -// ============================================================================ - -#include "ipc/ipc.h" -#include "sys/shm.h" - -#include "assert.h" -#include "mem/kheap.h" -#include "stdio.h" -#include "string.h" -#include "sys/errno.h" -#include "sys/list_head.h" - -// #include "process/process.h" - -///@brief A value to compute the shmid value. -int __shm_id = 0; - -// @brief Shared memory management structure. -typedef struct { - /// @brief ID associated to the shared memory. - int id; - /// @brief The shared memory data strcutre. - struct shmid_ds shmid; - /// Where shm created is memorized. - void *shm_location; - /// Reference inside the list of shared memory management structures. - list_head list; -} shm_info_t; - -/// @brief List of all current active shared memorys. -list_head shm_list; - -// ============================================================================ -// MEMORY MANAGEMENT (Private) -// ============================================================================ - -/// @brief Allocates the memory for shared memory structure. -/// @param key IPC_KEY associated with the shared memory. -/// @param shmflg flags used to create the shared memory. -/// @return a pointer to the allocated shared memory structure. -static inline shm_info_t *__shm_info_alloc(key_t key, size_t size, int shmflg) -{ - // Allocate the memory. - shm_info_t *shm_info = (shm_info_t *)kmalloc(sizeof(shm_info_t)); - // Check the allocated memory. - assert(shm_info && "Failed to allocate memory for a shared memory structure."); - // Clean the memory. - memset(shm_info, 0, sizeof(shm_info_t)); - // Initialize its values. - shm_info->id = ++__shm_id; - shm_info->shmid.shm_perm = register_ipc(key, shmflg & 0x1FF); - shm_info->shmid.shm_segsz = size; - shm_info->shmid.shm_atime = 0; - shm_info->shmid.shm_dtime = 0; - shm_info->shmid.shm_ctime = 0; - shm_info->shmid.shm_cpid = 0; - shm_info->shmid.shm_lpid = 0; - shm_info->shmid.shm_nattch = 0; - // Return the shared memory structure. - return shm_info; -} - -/// @brief Frees the memory of a shared memory structure. -/// @param shm_info pointer to the shared memory structure. -static inline void __shm_info_dealloc(shm_info_t *shm_info) -{ - assert(shm_info && "Received a NULL pointer."); - // Deallocate the shmid memory. - kfree(shm_info); -} - -#if 0 -struct shmid_ds *head = NULL; -static ushort shm_descriptor = 0; - -int syscall_shmctl(int *args) -{ - int shmid = args[0]; - int cmd = args[1]; - - // TODO: for IPC_STAT - // struct shmid_ds * buf = (struct shmid_ds *) args[2]; - - struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); - - if (myshmid_ds == NULL) { - return -1; - } - - // Upgrade shm info. - myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; - myshmid_ds->shm_ctime = time(NULL); - - switch (cmd) { - case IPC_RMID: - if (myshmid_ds->shm_nattch == 0) { - kfree(myshmid_ds->shm_location); - - // Manage list. - if (myshmid_ds == head) { - head = head->next; - } else { - // Finding the previous shmid_ds. - struct shmid_ds *prev = head; - while (prev->next != myshmid_ds) { - prev = prev->next; - } - prev->next = myshmid_ds->next; - } - kfree(myshmid_ds); - } else { - (myshmid_ds->shm_perm).mode |= SHM_DEST; - } - - return 0; - - case IPC_STAT: - break; - case IPC_SET: - break; - case SHM_LOCK: - break; - case SHM_UNLOCK: - break; - default: - break; - } - - return -1; -} - -// Get shared memory segment. -int syscall_shmget(int *args) -{ - int flags = args[2]; - key_t key = (key_t)args[0]; - size_t size = (size_t)args[1]; - - struct shmid_ds *shmid_ds; - - if (flags & IPC_EXCL) { - return -1; - } - - if (flags & IPC_CREAT) { - shmid_ds = find_shm_fromkey(key); - - if (shmid_ds != NULL) { - return -1; - } - - shmid_ds = kmalloc(sizeof(struct shmid_ds)); - pr_default("\n[SHM] shmget() shmid_ds : 0x%p", shmid_ds); - - shmid_ds->shm_location = kmalloc_align(size); - pr_default("\n[SHM] shmget() Location : 0x%p", - shmid_ds->shm_location); - pr_default("\n[SHM] shmget() physLocation : 0x%p", - paging_virtual_to_physical(get_current_page_directory(), - shmid_ds->shm_location)); - - shmid_ds->next = head; - head = shmid_ds; - - shmid_ds->shm_segsz = size; - shmid_ds->shm_atime = 0; - shmid_ds->shm_dtime = 0; - shmid_ds->shm_ctime = 0; - shmid_ds->shm_cpid = scheduler_get_current_process()->pid; - shmid_ds->shm_lpid = scheduler_get_current_process()->pid; - shmid_ds->shm_nattch = 0; - - // No user implementation. - (shmid_ds->shm_perm).cuid = 0; - // No group implementation. - (shmid_ds->shm_perm).cgid = 0; - // No user implementation - (shmid_ds->shm_perm).uid = 0; - // No group implementation. - (shmid_ds->shm_perm).gid = 0; - (shmid_ds->shm_perm).mode = flags & 0777; - (shmid_ds->shm_perm).seq = shm_descriptor++; - (shmid_ds->shm_perm).key = key; - } else { - shmid_ds = find_shm_fromkey(key); - pr_default("\n[SHM] shmget() shmid_ds found : 0x%p", shmid_ds); - - if (shmid_ds == NULL) { - return -1; - } - - if ((flags & 0777) > ((shmid_ds->shm_perm).mode & 0777)) { - return -1; - } - shmid_ds->shm_lpid = scheduler_get_current_process()->pid; - } - - return (shmid_ds->shm_perm).seq; -} - -// Attach shared memory segment. -void *syscall_shmat(int *args) -{ - int shmid = args[0]; - void *shmaddr = (void *)args[1]; - - // TODO: for more settings - // int flags = args[2]; - - struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); - pr_default("\n[SHM] shmat() shmid_ds found : 0x%p", myshmid_ds); - - if (myshmid_ds == NULL) { - return (void *)-1; - } - - void *shm_start = myshmid_ds->shm_location; - - if (shmaddr == NULL) { - void *ret = kmalloc_align(myshmid_ds->shm_segsz); - - uint32_t shm_vaddr_start = (uint32_t)ret & 0xfffff000; - uint32_t shm_vaddr_end = - ((uint32_t)ret + myshmid_ds->shm_segsz) & 0xfffff000; - - uint32_t shm_paddr_start = (uint32_t)paging_virtual_to_physical( - get_current_page_directory(), shm_start); - - free_map_region(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_end, true); - - while (shm_vaddr_start <= shm_vaddr_end) { - paging_allocate_page(get_current_page_directory(), shm_vaddr_start, - shm_paddr_start / PAGE_SIZE, true, true); - shm_vaddr_start += PAGE_SIZE; - shm_paddr_start += PAGE_SIZE; - } - - pr_default("\n[SHM] shmat() vaddr : 0x%p", ret); - pr_default("\n[SHM] shmat() paddr : 0x%p", - (void *)shm_paddr_start); - pr_default("\n[SHM] shmat() paddr after map: 0x%p", - paging_virtual_to_physical(get_current_page_directory(), - ret)); - - // Upgrade shm info. - myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; - (myshmid_ds->shm_nattch)++; - myshmid_ds->shm_atime = time(NULL); - - return ret; - } - - return (void *)-1; -} - -// Detach shared memory segment. -int syscall_shmdt(int *args) -{ - void *shmaddr = (void *)args[0]; - - if (shmaddr == NULL) { - return -1; - } - - struct shmid_ds *myshmid_ds = find_shm_fromvaddr(shmaddr); - pr_default("\n[SHM] shmdt() shmid_ds found : 0x%p", myshmid_ds); - - if (myshmid_ds == NULL) { - return -1; - } - - // ===== Test ============================================================== - uint32_t shm_vaddr_start = (uint32_t)shmaddr & 0xfffff000; - uint32_t shm_vaddr_end = - ((uint32_t)shmaddr + myshmid_ds->shm_segsz) & 0xfffff000; - - free_map_region(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_end, false); - - while (shm_vaddr_start <= shm_vaddr_end) { - paging_allocate_page(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_start / PAGE_SIZE, true, true); - shm_vaddr_start += PAGE_SIZE; - } - // ========================================================================= - - kfree(shmaddr); - - // Upgrade shm info. - myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; - (myshmid_ds->shm_nattch)--; - myshmid_ds->shm_dtime = time(NULL); - - // Manage SHM_DEST flag on. - if (myshmid_ds->shm_nattch == 0 && (myshmid_ds->shm_perm).mode & SHM_DEST) { - kfree(myshmid_ds->shm_location); - - // Manage list. - if (myshmid_ds == head) { - head = head->next; - } else { - // Finding the previous shmid_ds. - struct shmid_ds *prev = head; - while (prev->next != myshmid_ds) { - prev = prev->next; - } - prev->next = myshmid_ds->next; - } - kfree(myshmid_ds); - } - - return 0; -} - -int shmctl(int shmid, int cmd, struct shmid_ds *buf) -{ - int error; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $6, %%eax\n" - "int $80\n" - : - : "r"(shmid), "r"(cmd), "r"(buf)); - __asm__("movl %%eax, %0\n\t" - : "=r"(error)); - - return error; -} - -int shmget(key_t key, size_t size, int flags) -{ - int id; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $3, %%eax\n" - "int $80\n" - : - : "r"(key), "r"(size), "r"(flags)); - __asm__("movl %%eax, %0\n\t" - : "=r"(id)); - - return id; -} - -void *shmat(int shmid, void *shmaddr, int flag) -{ - void *addr; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $4, %%eax\n" - "int $80\n" - : - : "r"(shmid), "r"(shmaddr), "r"(flag)); - // The kernel is serving my system call - - // Now I have the control - __asm__("movl %%eax, %0\n\t" - : "=r"(addr)); - - return addr; -} - -int shmdt(void *shmaddr) -{ - int error; - - __asm__("movl %0, %%ecx\n" - "movl $5, %%eax\n" - "int $80\n" - : - : "r"(shmaddr)); - __asm__("movl %%eax, %0\n\t" - : "=r"(error)); - - return error; -} - -struct shmid_ds *find_shm_fromid(int shmid) -{ - struct shmid_ds *res = head; - - while (res != NULL) { - if ((res->shm_perm).seq == shmid) { - return res; - } - res = res->next; - } - - return NULL; -} - -struct shmid_ds *find_shm_fromkey(key_t key) -{ - struct shmid_ds *res = head; - - while (res != NULL) { - if ((res->shm_perm).key == key) { - return res; - } - res = res->next; - } - - return NULL; -} - -struct shmid_ds *find_shm_fromvaddr(void *shmvaddr) -{ - void *shmpaddr = - paging_virtual_to_physical(get_current_page_directory(), shmvaddr); - void *paddr; - struct shmid_ds *res = head; - - while (res != NULL) { - paddr = paging_virtual_to_physical(get_current_page_directory(), - res->shm_location); - if (paddr == shmpaddr) { - return res; - } - res = res->next; - } - - return NULL; -} -#endif - -void *sys_shmat(int shmid, const void *shmaddr, int shmflg) -{ - return 0; -} - -int sys_shmget(key_t key, size_t size, int flag) -{ - return 0; -} - -long sys_shmdt(const void *shmaddr) -{ - return 0; -} - -long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf) -{ - return 0; -} - -ssize_t procipc_shm_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) -{ - if (!file) { - pr_err("Received a NULL file.\n"); - return -ENOENT; - } - size_t buffer_len = 0, read_pos = 0, write_count = 0, ret = 0; - char buffer[BUFSIZ]; - - // Prepare a buffer. - memset(buffer, 0, BUFSIZ); - // Prepare the header. - ret = sprintf(buffer, "key shmid ...\n"); - - // Implementation goes here... - sprintf(buffer + ret, "\n"); - - // Perform read. - buffer_len = strlen(buffer); - read_pos = offset; - if (read_pos < buffer_len) { - while ((write_count < nbyte) && (read_pos < buffer_len)) { - buf[write_count] = buffer[read_pos]; - // Move the pointers. - ++read_pos, ++write_count; - } - } - return write_count; -} - -///! @endcond diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index 8a4652de..043a0c8e 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -37,6 +37,7 @@ #include "sys/msg.h" #include "sys/sem.h" #include "sys/shm.h" +#include "ipc/ipc.h" #include "system/syscall.h" #include "version.h" diff --git a/mentos/src/klib/vsprintf.c b/mentos/src/klib/vsprintf.c index 7fded041..32649592 100644 --- a/mentos/src/klib/vsprintf.c +++ b/mentos/src/klib/vsprintf.c @@ -12,6 +12,7 @@ #include "stdint.h" #include "stdio.h" #include "string.h" +#include "sys/bitops.h" /// Size of the buffer used to call cvt functions. #define CVTBUFSIZE 500 @@ -42,494 +43,670 @@ static inline int skip_atoi(const char **s) return i; } -/// @brief Places the number inside the string. -/// @param str the string where the number will end up in. -/// @param num the number. -/// @param base the base used to transform the number. -/// @param size the size available for storing the number. -/// @param precision the precision. -/// @param flags support flags. -/// @return the string itself, or NULL. -static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags) +/// @brief Transforms the number into a string. +/// @param str the output string. +/// @param end the end of the buffer to prevent overflow. +/// @param num the number to transform to string. +/// @param base the base to use for number transformation (e.g., 10 for decimal, 16 for hex). +/// @param size the minimum size of the output string (pads with '0' or spaces if necessary). +/// @param precision the precision for number conversion (affects floating point numbers and zero padding). +/// @param flags control flags (e.g., for padding, sign, and case sensitivity). +/// @return the resulting string after number transformation. +static char *number(char *str, char *end, long num, int base, int size, int32_t precision, unsigned flags) { - char c, tmp[66] = { 0 }; - char *dig = _digits; + char tmp[66] = { 0 }; // Temporary buffer to hold the number in reverse order + char *dig = _digits; // Default digits for base conversion (lowercase) - if (flags & FLAGS_UPPERCASE) { - dig = _upper_digits; + // Check for uppercase conversion flag. + if (bitmask_check(flags, FLAGS_UPPERCASE)) { + dig = _upper_digits; // Use uppercase digits if the flag is set } - if (flags & FLAGS_LEFT) { - flags &= ~FLAGS_ZEROPAD; + + // Disable zero padding if left alignment is specified. + if (bitmask_check(flags, FLAGS_LEFT)) { + bitmask_clear_assign(flags, FLAGS_ZEROPAD); } + + // Error handling: base must be between 2 and 36. if (base < 2 || base > 36) { - return NULL; + return 0; // Return NULL if the base is invalid. } - c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; + // Set padding character based on the FLAGS_ZEROPAD flag (either '0' or ' '). + const int paddingc = bitmask_check(flags, FLAGS_ZEROPAD) ? '0' : ' '; - // -------------------------------- - // Set the sign. - // -------------------------------- + // Set the sign (for signed numbers). char sign = 0; - if (flags & FLAGS_SIGN) { + if (bitmask_check(flags, FLAGS_SIGN)) { if (num < 0) { - sign = '-'; - num = -num; + sign = '-'; // Negative sign for negative numbers + num = -num; // Make number positive + size--; // Reduce size for the sign + } else if (bitmask_check(flags, FLAGS_PLUS)) { + sign = '+'; // Positive sign for positive numbers size--; - } else if (flags & FLAGS_PLUS) { - sign = '+'; - size--; - } else if (flags & FLAGS_SPACE) { - sign = ' '; + } else if (bitmask_check(flags, FLAGS_SPACE)) { + sign = ' '; // Space for positive numbers size--; } } - // Sice I've removed the sign (if negative), i can transform it to unsigned. + + // Convert the number to unsigned for further processing. uint32_t uns_num = (uint32_t)num; - if (flags & FLAGS_HASH) { + + // Handle the FLAGS_HASH for octal (base 8) and hexadecimal (base 16). + if (bitmask_check(flags, FLAGS_HASH)) { if (base == 16) { - size -= 2; + size -= 2; // Hexadecimal prefix "0x" or "0X" uses 2 characters. } else if (base == 8) { - size--; + size--; // Octal prefix "0" uses 1 character. } } + // Convert the number to the target base. int32_t i = 0; if (uns_num == 0) { - tmp[i++] = '0'; + tmp[i++] = '0'; // Handle zero case explicitly. } else { while (uns_num != 0) { - tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; - uns_num = ((unsigned long)uns_num) / (unsigned)base; + tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; // Convert to base + uns_num = ((unsigned long)uns_num) / (unsigned)base; // Divide by base } } + + // Ensure precision is at least as large as the number's length. if (i > precision) { precision = i; } + + // Adjust the size based on the precision. size -= precision; - if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { - while (size-- > 0) { + + // Write padding spaces if right-aligned and no zero padding. + if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } } - if (sign) { + + // Write the sign character if necessary. + if (sign && (end == NULL || str < end)) { *str++ = sign; } - if (flags & FLAGS_HASH) { - if (base == 8) { - *str++ = '0'; - } else if (base == 16) { - *str++ = '0'; - *str++ = _digits[33]; + + // Write the prefix for octal and hexadecimal if the FLAGS_HASH is set. + if (bitmask_check(flags, FLAGS_HASH)) { + if (base == 8 && (end == NULL || str < end)) { + *str++ = '0'; // Octal prefix. + } else if (base == 16 && (str + 1) < end) { + *str++ = '0'; // Hexadecimal prefix "0x". + *str++ = _digits[33]; // 'x' or 'X' based on FLAGS_UPPERCASE. } } - if (!(flags & FLAGS_LEFT)) { - while (size-- > 0) { - *str++ = c; + + // Write zero-padding if necessary. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (size-- > 0 && (end == NULL || str < end)) { + *str++ = paddingc; // Pad with '0' or ' '. } } - while (i < precision--) { + + // Write any additional zeros required by the precision. + while (i < precision-- && (end == NULL || str < end)) { *str++ = '0'; } - while (i-- > 0) { + + // Write the number in reverse order (tmp array holds the reversed digits). + while (i-- > 0 && (end == NULL || str < end)) { *str++ = tmp[i]; } - while (size-- > 0) { + + // If the number is left-aligned, pad remaining space with spaces. + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } - return str; + + return str; // Return the resulting string pointer. } -/// @brief Prints a MAC address. -/// @param str the string where we store the address. -/// @param addr the address we need to store. -/// @param size the size available in str. -/// @param precision the precision to use. -/// @param flags support flags. -/// @return a pointer to str itself, or NULL. -static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +/// @brief Converts a MAC address into a human-readable string format. +/// @param str The output string where the MAC address will be written. +/// @param end The end of the buffer to prevent overflow. +/// @param addr The 6-byte MAC address to be formatted. +/// @param size The minimum field width for the output (pads with spaces if necessary). +/// @param precision Unused in this function (for compatibility with similar functions). +/// @param flags Control flags that affect the format (e.g., uppercase and left alignment). +/// @return Pointer to the end of the output string. +static char *eaddr(char *str, char *end, unsigned char *addr, int size, int precision, unsigned flags) { - (void)precision; - char tmp[24]; - char *dig = _digits; - int i, len; + (void)precision; // Precision is unused in this function. - if (flags & FLAGS_UPPERCASE) { + char tmp[24]; // Temporary buffer to hold the formatted MAC address. + char *dig = _digits; // Default digits for hex conversion (lowercase by default). + int i, len = 0; + + // Use uppercase hex digits if the FLAGS_UPPERCASE flag is set. + if (bitmask_check(flags, FLAGS_UPPERCASE)) { dig = _upper_digits; } - len = 0; + // Convert each byte of the MAC address to hex format. for (i = 0; i < 6; i++) { + // Add colon separator between address bytes. if (i != 0) { - tmp[len++] = ':'; + if (len < sizeof(tmp)) { + tmp[len++] = ':'; + } + } + + if (len < sizeof(tmp)) { + tmp[len++] = dig[addr[i] >> 4]; // Convert upper nibble to hex. + } + if (len < sizeof(tmp)) { + tmp[len++] = dig[addr[i] & 0x0F]; // Convert lower nibble to hex. } - tmp[len++] = dig[addr[i] >> 4]; - tmp[len++] = dig[addr[i] & 0x0F]; } - if (!(flags & FLAGS_LEFT)) { - while (len < size--) { - *str++ = ' '; + // Handle right alignment if the FLAGS_LEFT flag is not set. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < size-- && (end == NULL || str < end)) { + *str++ = ' '; // Pad with spaces on the left if needed. } } - for (i = 0; i < len; ++i) { + // Copy the formatted MAC address from tmp buffer to the output string. + for (i = 0; i < len && (end == NULL || str < end); ++i) { *str++ = tmp[i]; } - while (len < size--) { - *str++ = ' '; + // Handle left padding if the FLAGS_LEFT flag is set. + while (len < size-- && (end == NULL || str < end)) { + *str++ = ' '; // Pad with spaces on the right if needed. } - return str; + return str; // Return the pointer to the end of the output string. } -/// @brief Prints an internet address. -/// @param str the string where we store the address. -/// @param addr the address we need to store. -/// @param size the size available in str. -/// @param precision the precision to use. -/// @param flags support flags. -/// @return a pointer to str itself, or NULL. -static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +/// @brief Converts an IPv4 address into a human-readable string format. +/// @param str The output string where the IPv4 address will be written. +/// @param end The end of the buffer to prevent overflow. +/// @param addr The 4-byte IPv4 address to be formatted. +/// @param size The minimum field width for the output (pads with spaces if necessary). +/// @param precision Unused in this function (for compatibility with similar functions). +/// @param flags Control flags that affect the format (e.g., left alignment). +/// @return Pointer to the end of the output string. +static char *iaddr(char *str, char *end, unsigned char *addr, int size, int precision, unsigned flags) { - (void)precision; + (void)precision; // Precision is unused in this function. + + // Temporary buffer to hold the formatted IP address. char tmp[24]; - int i, n, len; + int i, n, len = 0; - len = 0; + // Convert each byte of the IP address to decimal format. for (i = 0; i < 4; i++) { - if (i != 0) { + // Add a dot between each octet. + if (i != 0 && len < sizeof(tmp)) { tmp[len++] = '.'; } + + // Current octet of the IP address. n = addr[i]; + // Convert the current octet to decimal digits. if (n == 0) { - tmp[len++] = _digits[0]; + // Handle the case where the octet is zero. + if (len < sizeof(tmp)) { + tmp[len++] = _digits[0]; + } } else { - if (n >= 100) { - tmp[len++] = _digits[n / 100]; + // If the number is greater than or equal to 100, we need to extract + // hundreds, tens, and units. + if (n >= 100 && len < sizeof(tmp)) { + tmp[len++] = _digits[n / 100]; // Hundreds place. n = n % 100; - tmp[len++] = _digits[n / 10]; - n = n % 10; - } else if (n >= 10) { - tmp[len++] = _digits[n / 10]; + } + if (n >= 10 && len < sizeof(tmp)) { + tmp[len++] = _digits[n / 10]; // Tens place. n = n % 10; } - - tmp[len++] = _digits[n]; + // Finally, add the unit digit. + if (len < sizeof(tmp)) { + tmp[len++] = _digits[n]; + } } } - if (!(flags & FLAGS_LEFT)) { - while (len < size--) { + // Handle right alignment if the FLAGS_LEFT flag is not set. + if (!bitmask_check(flags, FLAGS_LEFT)) { + // Pad with spaces on the left if needed. + while (len < size-- && (end == NULL || str < end)) { *str++ = ' '; } } - for (i = 0; i < len; ++i) { + // Copy the formatted IP address from tmp buffer to the output string. + for (i = 0; i < len && (end == NULL || str < end); ++i) { *str++ = tmp[i]; } - while (len < size--) { + // Handle left padding if the FLAGS_LEFT flag is set. Pad with spaces on the + // right if needed. + while (len < size-- && (end == NULL || str < end)) { *str++ = ' '; } - return str; + return str; // Return the pointer to the end of the output string. } -/// @brief Prints a floating point value. -/// @param value the value to print. -/// @param buffer the buffer where the value is stored. -/// @param fmt the format. -/// @param precision the precision. -static void cfltcvt(double value, char *buffer, char fmt, int precision) +/// @brief Converts a floating-point number to a string with a specified format. +/// @param value The floating-point value to be converted. +/// @param buffer The output buffer to store the resulting string. +/// @param bufsize The size of the output buffer. +/// @param fmt The format specifier ('e', 'f', or 'g'). +/// @param precision The number of digits to be displayed after the decimal +/// point. +static void cfltcvt(double value, char *buffer, size_t bufsize, char fmt, int precision) { int decpt, sign, exp, pos; - char cvtbuf[CVTBUFSIZE]; - char *digits = cvtbuf; - int capexp = 0; + char cvtbuf[CVTBUFSIZE]; // Temporary buffer to store the digits. + char *digits = cvtbuf; // Pointer to the digit buffer. + int capexp = 0; // Flag to check for uppercase exponent. int magnitude; + char *end = buffer + bufsize - 1; // Pointer to the end of the buffer. + + // Handle uppercase 'G' or 'E' format specifier. + // Convert them to lowercase 'g' or 'e' for uniform processing. if (fmt == 'G' || fmt == 'E') { - capexp = 1; - fmt += 'a' - 'A'; + capexp = 1; // Set capexp to handle uppercase exponent. + fmt += 'a' - 'A'; // Convert to lowercase. } + // Handle 'g' format: choose between 'e' or 'f' based on magnitude. if (fmt == 'g') { ecvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); - magnitude = decpt - 1; + magnitude = decpt - 1; // Calculate magnitude of the number. + + // If magnitude is out of range for 'f', use scientific notation 'e'. if (magnitude < -4 || magnitude > precision - 1) { fmt = 'e'; - precision -= 1; + precision -= 1; // Adjust precision for 'e' format. } else { fmt = 'f'; - precision -= decpt; + precision -= decpt; // Adjust precision for 'f' format. } } + // Handle scientific notation ('e' format). if (fmt == 'e') { + // Convert the number to scientific format. ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE); - if (sign) { + // Add the sign to the output buffer if necessary. + if (sign && buffer < end) { *buffer++ = '-'; } - *buffer++ = *digits; - if (precision > 0) { + + // Add the first digit. + if (buffer < end) { + *buffer++ = *digits; + } + + // Add the decimal point and remaining digits. + if (precision > 0 && buffer < end) { *buffer++ = '.'; } - memcpy(buffer, digits + 1, precision); - buffer += precision; - *buffer++ = capexp ? 'E' : 'e'; + // Copy the remaining digits. + for (int i = 1; i <= precision && buffer < end; i++) { + *buffer++ = digits[i]; + } + + // Add the exponent character ('e' or 'E'). + if (buffer < end) { + *buffer++ = capexp ? 'E' : 'e'; + } + + // Calculate the exponent. if (decpt == 0) { - if (value == 0.0) { - exp = 0; - } else { - exp = -1; - } + exp = (value == 0.0) ? 0 : -1; } else { exp = decpt - 1; } - if (exp < 0) { + // Add the sign of the exponent. + if (exp < 0 && buffer < end) { *buffer++ = '-'; exp = -exp; - } else { + } else if (buffer < end) { *buffer++ = '+'; } - buffer[2] = (char)((exp % 10) + '0'); - exp = exp / 10; - buffer[1] = (char)((exp % 10) + '0'); - exp = exp / 10; - buffer[0] = (char)((exp % 10) + '0'); + // Add the exponent digits (e.g., '01', '02'). + for (int i = 2; i >= 0 && buffer < end; i--) { + buffer[i] = (char)((exp % 10) + '0'); + exp /= 10; + } buffer += 3; - } else if (fmt == 'f') { + } + // Handle fixed-point notation ('f' format). + else if (fmt == 'f') { + // Convert the number to fixed-point format. fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); - if (sign) { + + // Add the sign to the output buffer if necessary. + if (sign && buffer < end) { *buffer++ = '-'; } + + // If digits exist, process them. if (*digits) { + // Handle the case where the decimal point is before the first + // digit. if (decpt <= 0) { - *buffer++ = '0'; - *buffer++ = '.'; - for (pos = 0; pos < -decpt; pos++) { + if (buffer < end) { + *buffer++ = '0'; + } + if (buffer < end) { + *buffer++ = '.'; + } + + // Add leading zeros before the first significant digit. + for (pos = 0; pos < -decpt && buffer < end; pos++) { *buffer++ = '0'; } - while (*digits) { + + // Copy the digits. + while (*digits && buffer < end) { *buffer++ = *digits++; } - } else { + } + // Handle normal case where decimal point is after some digits. + else { pos = 0; - while (*digits) { - if (pos++ == decpt) { + while (*digits && buffer < end) { + if (pos++ == decpt && buffer < end) { *buffer++ = '.'; } - *buffer++ = *digits++; + if (buffer < end) { + *buffer++ = *digits++; + } } } - } else { - *buffer++ = '0'; - if (precision > 0) { + } + // Handle case where the value is zero. + else { + if (buffer < end) { + *buffer++ = '0'; + } + if (precision > 0 && buffer < end) { *buffer++ = '.'; - for (pos = 0; pos < precision; pos++) { + for (pos = 0; pos < precision && buffer < end; pos++) { *buffer++ = '0'; } } } } - *buffer = '\0'; + if (buffer < end) { + *buffer = '\0'; // Null-terminate the string. + } else { + *end = '\0'; // Ensure null-termination if buffer exceeded. + } } -/// @brief Should we force the decimal point. -/// @param buffer the buffer where we force the decimal point. -static void forcdecpt(char *buffer) +/// @brief Ensures that a decimal point is present in the given number string. +/// @param buffer The string representation of a number. +/// @param bufsize The size of the output buffer. +static void forcdecpt(char *buffer, size_t bufsize) { - while (*buffer) { + // Traverse the buffer to check if there is already a decimal point or an + // exponent ('e' or 'E'). + char *end = buffer + bufsize - 1; // Pointer to the end of the buffer. + while (*buffer && buffer < end) { if (*buffer == '.') { - return; + return; // Decimal point found, no need to modify. } if (*buffer == 'e' || *buffer == 'E') { - break; + break; // Reached exponent part of the number, stop checking. } - - buffer++; + buffer++; // Move to the next character. } - if (*buffer) { + // If an exponent ('e' or 'E') is found, shift the exponent part to make + // space for the decimal point. + if (*buffer && buffer < end) { + // Get the length of the exponent part. long n = (long)strlen(buffer); - while (n > 0) { - buffer[n + 1] = buffer[n]; - n--; + + // Check if there is enough space to shift and add the decimal point. + if (buffer + n + 1 < end) { + // Shift the buffer contents one position to the right to make space for + // the decimal point. + while (n >= 0) { + buffer[n + 1] = buffer[n]; + n--; + } + + // Insert the decimal point. + *buffer = '.'; } - *buffer = '.'; - } else { - *buffer++ = '.'; - *buffer = '\0'; + } + // If no exponent is found, append the decimal point at the end of the string. + else if (buffer < end) { + *buffer++ = '.'; // Add decimal point at the current end. + *buffer = '\0'; // Null-terminate the string. } } -/// @brief Crop zero unless '#' given. -/// @param buffer the buffer to work on. -static void cropzeros(char *buffer) +/// @brief Removes trailing zeros after the decimal point in a number string. +/// @param buffer The string representation of a number. +/// @param bufsize The size of the output buffer. +static void cropzeros(char *buffer, size_t bufsize) { char *stop; + char *end = buffer + bufsize - 1; // Pointer to the end of the buffer. - while (*buffer && *buffer != '.') { + // Traverse until a decimal point is found or the end of the string is + // reached. + while (*buffer && *buffer != '.' && buffer < end) { buffer++; } - if (*buffer++) { - while (*buffer && *buffer != 'e' && *buffer != 'E') { + // If there is a decimal point, find the position of the exponent or the end + // of the number. + if (*buffer++ && buffer < end) { // Move past the decimal point. + // Continue until 'e', 'E', or end of string is found. + while (*buffer && *buffer != 'e' && *buffer != 'E' && buffer < end) { buffer++; } + + // Store position of 'e', 'E', or end of the string, and backtrack one + // step. stop = buffer--; - while (*buffer == '0') { + + // Backtrack over trailing zeros. + while (*buffer == '0' && buffer > (buffer - bufsize)) { buffer--; } + + // If a decimal point is found with no significant digits after, + // backtrack to remove it. if (*buffer == '.') { buffer--; } - while ((*++buffer = *stop++)) {} + + // Shift the string forward to overwrite any unnecessary characters + // (trailing zeros or decimal point). + while (buffer < end && (*++buffer = *stop++)) {} + + // Ensure null-termination if buffer exceeded. + if (buffer >= end) { + *end = '\0'; + } } } -/// @brief Transforms a floating point value to string. -/// @param str the string where the floating point value should be stored. -/// @param num the number to store. -/// @param size the size available for storing the floating point value. -/// @param precision the precision. -/// @param fmt the format. -/// @param flags the support flags. -/// @return a pointer to str itself. -static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags) +/// @brief Formats a floating-point number into a string with specified options. +/// +/// @details This function converts a floating-point number into a string +/// representation based on the specified format, precision, and flags. It +/// handles alignment, padding, and sign appropriately. +/// +/// @param str Pointer to the output string where the formatted number will be stored. +/// @param end Pointer to the end of the buffer to prevent overflow. +/// @param num The floating-point number to format. +/// @param size The total size of the output string, including padding. +/// @param precision The number of digits to display after the decimal point. +/// @param fmt The format specifier for the output ('f', 'g', 'e', etc.). +/// @param flags Control flags that modify the output format (e.g., left alignment, zero padding). +/// +/// @return Pointer to the next position in the output string after the formatted number. +static char *flt(char *str, char *end, double num, int size, int precision, char fmt, unsigned flags) { - char tmp[80]; + char workbuf[80]; char c, sign; int n, i; - // Left align means no zero padding. - if (flags & FLAGS_LEFT) { - flags &= ~FLAGS_ZEROPAD; + /// If the `FLAGS_LEFT` is set, clear the `FLAGS_ZEROPAD` flag. + /// Left alignment implies no zero padding. + if (bitmask_check(flags, FLAGS_LEFT)) { + bitmask_clear_assign(flags, FLAGS_ZEROPAD); } - // Determine padding and sign char. - c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; + /// Determine the padding character (`c`) and the sign of the number. + /// If `FLAGS_ZEROPAD` is set, the padding will be '0', otherwise it will be + /// a space (' '). + c = bitmask_check(flags, FLAGS_ZEROPAD) ? '0' : ' '; sign = 0; - if (flags & FLAGS_SIGN) { + + /// Check the `FLAGS_SIGN` flag to determine if the sign should be added. + if (bitmask_check(flags, FLAGS_SIGN)) { if (num < 0.0) { + /// If the number is negative, set `sign` to '-' and make the number + /// positive. sign = '-'; num = -num; - size--; - } else if (flags & FLAGS_PLUS) { + size--; // Reduce size for the sign character. + } else if (bitmask_check(flags, FLAGS_PLUS)) { + /// If `FLAGS_PLUS` is set, prepend a '+' to positive numbers. sign = '+'; size--; - } else if (flags & FLAGS_SPACE) { + } else if (bitmask_check(flags, FLAGS_SPACE)) { + /// If `FLAGS_SPACE` is set, prepend a space to positive numbers. sign = ' '; size--; } } - // Compute the precision value. + /// Set the default precision if no precision is provided. if (precision < 0) { - // Default precision: 6. - precision = 6; + precision = 6; // Default precision is 6 decimal places. } else if (precision == 0 && fmt == 'g') { - // ANSI specified. - precision = 1; + precision = 1; // For format 'g', default precision is 1. } - // Convert floating point number to text. - cfltcvt(num, tmp, fmt, precision); + /// Convert the floating-point number `num` into a string `workbuf` using the + /// given format `fmt`. + cfltcvt(num, workbuf, sizeof(workbuf), fmt, precision); - // '#' and precision == 0 means force a decimal point. - if ((flags & FLAGS_HASH) && precision == 0) { - forcdecpt(tmp); + /// If the `FLAGS_HASH` is set and precision is 0, force a decimal point in + /// the output. + if (bitmask_check(flags, FLAGS_HASH) && (precision == 0)) { + forcdecpt(workbuf, sizeof(workbuf)); } - // 'g' format means crop zero unless '#' given. - if (fmt == 'g' && !(flags & FLAGS_HASH)) { - cropzeros(tmp); + /// For format 'g', remove trailing zeros unless `FLAGS_HASH` is set. + if ((fmt == 'g') && !bitmask_check(flags, FLAGS_HASH)) { + cropzeros(workbuf, sizeof(workbuf)); } - n = strlen(tmp); + /// Calculate the length of the resulting string `workbuf`. + n = strnlen(workbuf, 80); - // Output number with alignment and padding. + /// Adjust size to account for the length of the output string. size -= n; - if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { - while (size-- > 0) { + + /// Add padding spaces before the number if neither `FLAGS_ZEROPAD` nor `FLAGS_LEFT` are set. + if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } } - if (sign) { + /// Add the sign character (if any) before the number. + if (sign && (end == NULL || str < end)) { *str++ = sign; } - if (!(flags & FLAGS_LEFT)) { - while (size-- > 0) { + /// Add padding characters (either '0' or spaces) before the number if `FLAGS_ZEROPAD` is set. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (size-- > 0 && (end == NULL || str < end)) { *str++ = c; } } - for (i = 0; i < n; i++) { - *str++ = tmp[i]; + /// Copy the formatted number string to the output `str`. + for (i = 0; i < n && (end == NULL || str < end); i++) { + *str++ = workbuf[i]; } - while (size-- > 0) { + /// Add padding spaces after the number if `FLAGS_LEFT` is set (left-aligned output). + while (size-- > 0 && (end == NULL || str < end)) { *str++ = ' '; } - return str; + return str; /// Return the resulting string after formatting the number. } int vsprintf(char *str, const char *fmt, va_list args) { - int base; - char *tmp; - char *s; - - // Flags to number(). - unsigned flags; - - // 'h', 'l', or 'L' for integer fields. - char qualifier; + int base; // Base for number formatting. + char *tmp; // Pointer to current position in the output buffer. + char *s; // Pointer for string formatting. + unsigned flags; // Flags for number formatting. + char qualifier; // Character qualifier for integer fields ('h', 'l', or 'L'). + + // Check for null input buffer or format string. + if (str == NULL || fmt == NULL) { + return -1; // Error: null pointer provided. + } for (tmp = str; *fmt; fmt++) { if (*fmt != '%') { - *tmp++ = *fmt; - + *tmp++ = *fmt; // Directly copy non-format characters. continue; } - // Process flags- + // Reset flags for each new format specifier. flags = 0; + repeat: - // This also skips first '%'. + + // Process format flags (skips first '%'). fmt++; + switch (*fmt) { case '-': - flags |= FLAGS_LEFT; + bitmask_set_assign(flags, FLAGS_LEFT); goto repeat; case '+': - flags |= FLAGS_PLUS; + bitmask_set_assign(flags, FLAGS_PLUS); goto repeat; case ' ': - flags |= FLAGS_SPACE; + bitmask_set_assign(flags, FLAGS_SPACE); goto repeat; case '#': - flags |= FLAGS_HASH; + bitmask_set_assign(flags, FLAGS_HASH); goto repeat; case '0': - flags |= FLAGS_ZEROPAD; + bitmask_set_assign(flags, FLAGS_ZEROPAD); goto repeat; } // Get the width of the output field. - int32_t field_width; - field_width = -1; + int32_t field_width = -1; if (isdigit(*fmt)) { field_width = skip_atoi(&fmt); @@ -538,13 +715,12 @@ int vsprintf(char *str, const char *fmt, va_list args) field_width = va_arg(args, int32_t); if (field_width < 0) { field_width = -field_width; - flags |= FLAGS_LEFT; + bitmask_set_assign(flags, FLAGS_LEFT); } } - /* Get the precision, thus the minimum number of digits for - * integers; max number of chars for from string. - */ + // Get the precision, thus the minimum number of digits for integers; + // max number of chars for from string. int32_t precision = -1; if (*fmt == '.') { ++fmt; @@ -566,52 +742,62 @@ int vsprintf(char *str, const char *fmt, va_list args) fmt++; } - // Default base. + // Default base for integer conversion. base = 10; switch (*fmt) { case 'c': - if (!(flags & FLAGS_LEFT)) { + // Handle left padding. + if (!bitmask_check(flags, FLAGS_LEFT)) { while (--field_width > 0) { *tmp++ = ' '; } } + // Add the character. *tmp++ = va_arg(args, char); + // Handle right padding. while (--field_width > 0) { *tmp++ = ' '; } continue; case 's': + // Handle string formatting. s = va_arg(args, char *); if (!s) { s = ""; } - + // Get the length of the string. int32_t len = (int32_t)strnlen(s, (uint32_t)precision); - if (!(flags & FLAGS_LEFT)) { + // Handle left padding. + if (!bitmask_check(flags, FLAGS_LEFT)) { while (len < field_width--) { *tmp++ = ' '; } } - - int32_t it; - for (it = 0; it < len; ++it) { + // Add the string. + for (int32_t it = 0; it < len; ++it) { *tmp++ = *s++; } + // Handle right padding. while (len < field_width--) { *tmp++ = ' '; } continue; case 'p': + + // Handle pointer formatting. if (field_width == -1) { field_width = 2 * sizeof(void *); - flags |= FLAGS_ZEROPAD; + // Zero pad for pointers. + bitmask_set_assign(flags, FLAGS_ZEROPAD); } - tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + tmp = number(tmp, NULL, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); continue; + case 'n': + // Handle writing the number of characters written. if (qualifier == 'l') { long *ip = va_arg(args, long *); *ip = (tmp - str); @@ -620,89 +806,330 @@ int vsprintf(char *str, const char *fmt, va_list args) *ip = (tmp - str); } continue; + case 'A': - flags |= FLAGS_UPPERCASE; + // Handle hexadecimal formatting with uppercase. + bitmask_set_assign(flags, FLAGS_UPPERCASE); break; + case 'a': + // Handle address formatting (either Ethernet or IP). if (qualifier == 'l') { - tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + tmp = eaddr(tmp, NULL, va_arg(args, unsigned char *), field_width, precision, flags); } else { - tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + tmp = iaddr(tmp, NULL, va_arg(args, unsigned char *), field_width, precision, flags); } continue; - // Integer number formats - set up the flags and "break". + case 'o': + // Integer number formats. base = 8; break; case 'X': - flags |= FLAGS_UPPERCASE; + // Handle hexadecimal formatting with uppercase. + bitmask_set_assign(flags, FLAGS_UPPERCASE); break; case 'x': + // Handle hexadecimal formatting. base = 16; break; case 'd': case 'i': - flags |= FLAGS_SIGN; + // Handle signed integer formatting. + bitmask_set_assign(flags, FLAGS_SIGN); case 'u': + // Handle unsigned integer formatting. break; + case 'E': case 'G': case 'e': case 'f': case 'g': - tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, flags | FLAGS_SIGN); + // Handle floating-point formatting. + tmp = flt(tmp, NULL, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); continue; + default: if (*fmt != '%') { - *tmp++ = '%'; + *tmp++ = '%'; // Output '%' if not a format specifier. } if (*fmt) { - *tmp++ = *fmt; + *tmp++ = *fmt; // Output the current character. } else { - --fmt; + --fmt; // Handle the case of trailing '%'. + } + continue; + } + + // Process the integer value. + if (bitmask_check(flags, FLAGS_SIGN)) { + long num = (qualifier == 'l') ? va_arg(args, long) : + (qualifier == 'h') ? va_arg(args, short) : + va_arg(args, int); + // Add the number. + tmp = number(tmp, NULL, num, base, field_width, precision, flags); + } else { + unsigned long num = (qualifier == 'l') ? va_arg(args, unsigned long) : + (qualifier == 'h') ? va_arg(args, unsigned short) : + va_arg(args, unsigned int); + // Add the number. + tmp = number(tmp, NULL, num, base, field_width, precision, flags); + } + } + + *tmp = '\0'; // Null-terminate the output string. + return (int)(tmp - str); // Return the number of characters written. +} + +int vsnprintf(char *str, size_t bufsize, const char *fmt, va_list args) +{ + int base; // Base for number formatting. + char *tmp; // Pointer to current position in the output buffer. + char *s; // Pointer for string formatting. + unsigned flags; // Flags for number formatting. + char qualifier; // Character qualifier for integer fields ('h', 'l', or 'L'). + + // Check for null input buffer or format string. + if (str == NULL || fmt == NULL) { + return -1; // Error: null pointer provided. + } + + char *end = str + bufsize - 1; // Reserve space for null-terminator. + + for (tmp = str; *fmt && tmp < end; fmt++) { + if (*fmt != '%') { + if (tmp < end) { + *tmp++ = *fmt; // Directly copy non-format characters. } continue; } - if (flags & FLAGS_SIGN) { - long num; + // Reset flags for each new format specifier. + flags = 0; + + repeat: + + // Process format flags (skips first '%'). + fmt++; + + switch (*fmt) { + case '-': + bitmask_set_assign(flags, FLAGS_LEFT); + goto repeat; + case '+': + bitmask_set_assign(flags, FLAGS_PLUS); + goto repeat; + case ' ': + bitmask_set_assign(flags, FLAGS_SPACE); + goto repeat; + case '#': + bitmask_set_assign(flags, FLAGS_HASH); + goto repeat; + case '0': + bitmask_set_assign(flags, FLAGS_ZEROPAD); + goto repeat; + } + + // Get the width of the output field. + int32_t field_width = -1; + + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int32_t); + if (field_width < 0) { + field_width = -field_width; + bitmask_set_assign(flags, FLAGS_LEFT); + } + } + + // Get the precision, thus the minimum number of digits for integers; + // max number of chars for from string. + int32_t precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + // Get the conversion qualifier. + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base for integer conversion. + base = 10; + + switch (*fmt) { + case 'c': + // Handle left padding. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (--field_width > 0 && tmp < end) { + *tmp++ = ' '; + } + } + // Add the character. + if (tmp < end) { + *tmp++ = va_arg(args, int); + } + // Handle right padding. + while (--field_width > 0 && tmp < end) { + *tmp++ = ' '; + } + continue; + + case 's': + // Handle string formatting. + s = va_arg(args, char *); + if (!s) { + s = ""; + } + // Get the length of the string. + int32_t len = (int32_t)strnlen(s, (uint32_t)precision); + // Handle left padding. + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < field_width-- && tmp < end) { + *tmp++ = ' '; + } + } + // Add the string. + for (int32_t it = 0; it < len && tmp < end; ++it) { + *tmp++ = *s++; + } + // Handle right padding. + while (len < field_width-- && tmp < end) { + *tmp++ = ' '; + } + continue; + + case 'p': + // Handle pointer formatting. + if (field_width == -1) { + field_width = 2 * sizeof(void *); + // Zero pad for pointers. + bitmask_set_assign(flags, FLAGS_ZEROPAD); + } + tmp = number(tmp, end, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + continue; + + case 'n': + // Handle writing the number of characters written. if (qualifier == 'l') { - num = va_arg(args, long); - } else if (qualifier == 'h') { - num = va_arg(args, short); + long *ip = va_arg(args, long *); + *ip = (tmp - str); } else { - num = va_arg(args, int); + int *ip = va_arg(args, int *); + *ip = (tmp - str); } - tmp = number(tmp, num, base, field_width, precision, flags); - } else { - unsigned long num; + continue; + + case 'A': + // Handle hexadecimal formatting with uppercase. + bitmask_set_assign(flags, FLAGS_UPPERCASE); + break; + + case 'a': + // Handle address formatting (either Ethernet or IP). if (qualifier == 'l') { - num = va_arg(args, unsigned long); - } else if (qualifier == 'h') { - num = va_arg(args, unsigned short); + tmp = eaddr(tmp, end, va_arg(args, unsigned char *), field_width, precision, flags); } else { - num = va_arg(args, unsigned int); + tmp = iaddr(tmp, end, va_arg(args, unsigned char *), field_width, precision, flags); } - tmp = number(tmp, num, base, field_width, precision, flags); + continue; + + case 'o': + // Integer number formats. + base = 8; + break; + + case 'X': + // Handle hexadecimal formatting with uppercase. + bitmask_set_assign(flags, FLAGS_UPPERCASE); + // Fall through. + case 'x': + // Handle hexadecimal formatting. + base = 16; + break; + + case 'd': + case 'i': + // Handle signed integer formatting. + bitmask_set_assign(flags, FLAGS_SIGN); + // Fall through. + case 'u': + // Handle unsigned integer formatting. + break; + + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + // Handle floating-point formatting. + tmp = flt(tmp, end, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); + continue; + + default: + if (*fmt != '%') { + if (tmp < end) { + *tmp++ = '%'; // Output '%' if not a format specifier. + } + } + if (*fmt && tmp < end) { + *tmp++ = *fmt; // Output the current character. + } else { + --fmt; // Handle the case of trailing '%'. + } + continue; + } + + // Process the integer value. + if (bitmask_check(flags, FLAGS_SIGN)) { + long num = (qualifier == 'l') ? va_arg(args, long) : + (qualifier == 'h') ? va_arg(args, short) : + va_arg(args, int); + // Add the number. + tmp = number(tmp, end, num, base, field_width, precision, flags); + } else { + unsigned long num = (qualifier == 'l') ? va_arg(args, unsigned long) : + (qualifier == 'h') ? va_arg(args, unsigned short) : + va_arg(args, unsigned int); + // Add the number. + tmp = number(tmp, end, num, base, field_width, precision, flags); } } - *tmp = '\0'; - return tmp - str; + if (tmp < end) { + *tmp = '\0'; // Null-terminate the output string. + } else { + *end = '\0'; // Ensure null-termination if buffer exceeded. + } + return (int)(tmp - str); // Return the number of characters written. } int printf(const char *format, ...) { - char buffer[4096]; + char buffer[4096] = { 0 }; va_list ap; int len; // Start variabile argument's list. va_start(ap, format); - len = vsprintf(buffer, format, ap); + len = vsnprintf(buffer, 4096, format, ap); va_end(ap); video_puts(buffer); return len; @@ -719,3 +1146,15 @@ int sprintf(char *str, const char *fmt, ...) return len; } + +int snprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list args; + int len; + + va_start(args, fmt); + len = vsnprintf(str, size, fmt, args); + va_end(args); + + return len; +} diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index caf9d1a0..f2842d31 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -327,7 +327,7 @@ static inline task_struct *__alloc_task(task_struct *source, task_struct *parent .c_iflag = 0 }; // Initialize the ringbuffer. - fs_rb_scancode_init(&proc->keyboard_rb); + rb_keybuffer_init(&proc->keyboard_rb); return proc; } diff --git a/mentos/src/process/scheduler_feedback.c b/mentos/src/process/scheduler_feedback.c index 01b7e8f1..d63c91bd 100644 --- a/mentos/src/process/scheduler_feedback.c +++ b/mentos/src/process/scheduler_feedback.c @@ -58,11 +58,16 @@ unsigned long next_log; /// session. size_t total_occurrences; -/// @brief A structure that keeps track of scheduling statistics. +/// @brief Structure that keeps track of scheduling statistics. struct statistic { + /// @brief Pointer to the task structure. task_struct *task; + /// @brief Number of times the task has been scheduled. unsigned long occur; -} arr_stats[PID_MAX_LIMIT]; +}; + +/// @brief Keeps track of scheduling statistics. +struct statistic arr_stats[PID_MAX_LIMIT]; /// @brief Updates when the logging should happen. static inline void __scheduler_feedback_deadline_advance(void) diff --git a/programs/edit.c b/programs/edit.c index c24e3833..5fee7696 100644 --- a/programs/edit.c +++ b/programs/edit.c @@ -3,126 +3,599 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[EDIT ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#include "io/debug.h" // Include debugging functions. + #include #include #include #include #include #include -#include #include #include #include -#define CTRL_KEY(k) ((k)&0x1f) -#define BUFFER_SIZE 4096 +#define MAX_LINE_LENGTH 160 +#define MAX_LINES 512 + +#define BUFFER_SIZE (MAX_LINE_LENGTH * MAX_LINES) + +#define TAB_SIZE 4 +#define PAGE_SIZE 4 + +static struct termios orig_termios; -static inline void __set_echo(bool_t active) +/// @brief Loads the contents of a file into a buffer and calculates the file +/// length and number of lines. +/// +/// @param filename The name of the file to load. +/// @param buffer The buffer where the file contents will be stored. +/// @param bufsize The size of the buffer. +/// @param file_length Pointer to an integer that will hold the length of the file in bytes. +/// @return int The number of lines in the file. +int load_file(const char *filename, char *buffer, size_t bufsize, int *file_length) { - struct termios _termios; - tcgetattr(STDIN_FILENO, &_termios); - if (active) { - _termios.c_lflag |= (ICANON | ECHO); - } else { - _termios.c_lflag &= ~(ICANON | ECHO); - } - tcsetattr(STDIN_FILENO, 0, &_termios); + // Open the file with read-only permissions. + int fd = open(filename, O_RDONLY, 666); + if (fd == -1) { + perror("Error opening file"); + exit(1); + } + // Read the file into the buffer. + ssize_t bytes_read = read(fd, buffer, bufsize); + if (bytes_read == -1) { + perror("Error reading file"); + close(fd); + exit(1); + } + // Ensure null termination of the buffer. + buffer[bytes_read] = '\0'; + // Initialize the number of lines and set the file length. + int num_lines = 0; + // Set file_length to the number of bytes read. + *file_length = bytes_read; + // Count the number of lines by counting '\n'. + for (char *p = buffer; *p != '\0'; p++) { + if (*p == '\n') { + num_lines++; + } + } + // Close the file. + if (close(fd) == -1) { + perror("Error closing file"); + exit(1); + } + // Return the number of lines in the file. + return num_lines; } -int main(int argc, char *argv[]) +/// @brief Saves the given buffer to a file, writing only up to the given buffer +/// size. +/// +/// @param filename The name of the file to save the buffer to. +/// @param buffer The buffer containing the data to be written to the file. +/// @param bufsize The size of the buffer to be written to the file. +/// @return int Returns 1 on success, 0 on failure. +int save_file(const char *filename, char *buffer, size_t bufsize) { - if (argc != 2) { - printf("edit: missing operand.\n"); - printf("Try 'edit --help' for more information.\n"); - return 1; - } - if (strcmp(argv[1], "--help") == 0) { - printf("Prints the content of the given file.\n"); - printf("Usage:\n"); - printf(" edit \n"); + // Open the file with write-only permissions, create it if it doesn't exist, + // truncate it if it does. + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + perror("Error opening file for writing"); return 0; } - int fd = open(argv[1], O_RDONLY, 42); - if (fd < 0) { - printf("edit: %s: %s\n", argv[1], strerror(errno)); - return 1; - } - stat_t statbuf; - if (fstat(fd, &statbuf) == -1) { - printf("edit: %s: %s\n", argv[1], strerror(errno)); - // Close the file descriptor. + // Write the buffer to the file, limited to the given bufsize. + ssize_t bytes_written = write(fd, buffer, bufsize); + if (bytes_written == -1) { + perror("Error writing to file"); close(fd); - return 1; + return 0; } - if (S_ISDIR(statbuf.st_mode)) { - printf("edit: %s: %s\n", argv[1], strerror(EISDIR)); - // Close the file descriptor. + // Ensure all data was written. + if (bytes_written != bufsize) { + fprintf(stderr, "Partial write: expected %zu bytes, wrote %zd bytes\n", bufsize, bytes_written); close(fd); - return 1; + return 0; } - // Put on the standard output the characters. - char buffer[BUFFER_SIZE]; - while (read(fd, buffer, BUFFER_SIZE) > 0) { - puts(buffer); - } - // Close the file descriptor. - close(fd); - - struct termios _termios; - tcgetattr(STDIN_FILENO, &_termios); - _termios.c_lflag &= ~ISIG; - tcsetattr(STDIN_FILENO, 0, &_termios); - - __set_echo(false); - - do { - int c = getchar(); - // Return Key - if (c == '\n') { - putchar('\n'); - // Break the while loop. - continue; - } else if (c == '\033') { + // Close the file after writing. + if (close(fd) == -1) { + perror("Error closing file"); + return 0; + } + // Return success. + return 1; +} + +/// @brief Trims empty lines and trailing spaces at the end of the file buffer, +/// ensuring the file ends with a newline. +/// +/// @param lines The buffer containing the file contents. +/// @param file_length Pointer to the length of the file, which will be adjusted after trimming. +/// @return void +void trim_empty_lines_at_end(char *lines, int *file_length) +{ + // Ensure file_length is valid before attempting to trim. + if (*file_length <= 0) { + return; + } + // Start from the end of the file (file_length - 1) and move backward. + while (*file_length > 0 && (lines[*file_length - 1] == '\n' || lines[*file_length - 1] == ' ')) { + // Reduce the file length for each newline or space found at the end. + (*file_length)--; + } + // If there is still content in the file, add a newline at the end. + if (*file_length > 0) { + // Ensure the file ends with a newline. + lines[*file_length] = '\n'; + // Account for the newly added newline. + *file_length += 1; + } + // Null-terminate the string to mark the end of the file + lines[*file_length] = '\0'; +} + +/// @brief Shifts the lines in the buffer up, moving the next line into the +/// current one and adjusting file length. +/// +/// @param lines The buffer containing all lines of the file. +/// @param cy The current line index. +/// @param cx The cursor position in the current line. +/// @param num_lines The total number of lines in the buffer. +/// @param file_length Pointer to the file length to adjust after shifting lines. +/// @return void +void shift_lines_up(char *lines, int cy, int cx, int num_lines, int *file_length) +{ + // Error checks to ensure valid input. Ensure that cy is within a valid range. + if (cy < 0 || cy >= num_lines - 1) { + return; + } + // Get pointers to the current line and the next line + char *line_start = lines; + // Traverse lines until the current line is found. + for (int i = 0; i < cy; i++) { + line_start = strchr(line_start, '\n') + 1; + // Error handling in case '\n' is not found. + if (!line_start) { + return; + } + } + char *next_line_start = strchr(line_start, '\n') + 1; + // Error handling in case '\n' is not found for the next line. + if (!next_line_start) { + return; + } + // Get the position of cx within the current line. + int line_len = strchr(line_start, '\n') - line_start; + // Calculate the current position in the file. + int current_position = line_start - lines; + // Append the next line's content after cx in the current line. + memmove(&line_start[cx], next_line_start, *file_length - (next_line_start - lines)); + // Shift everything up by removing the newline between cy and cy + 1. + memmove(next_line_start - 1, next_line_start, *file_length - (next_line_start - lines)); + // Adjust the file length after shifting up. + *file_length -= (next_line_start - line_start); +} + +/// @brief Shifts lines down in the buffer, creating space in the current line +/// and moving part of the line down. +/// +/// @param lines The buffer containing all lines of the file. +/// @param cy The current line index. +/// @param cx The cursor position in the current line. +/// @param num_lines The total number of lines in the buffer. +/// @param file_length Pointer to the file length to adjust after shifting lines. +/// @return void +void shift_lines_down(char *lines, int cy, int cx, int num_lines, int *file_length) +{ + // Error checks to ensure valid input. Ensure that cy is within a valid range. + if (cy < 0 || cy >= num_lines - 1) { + return; + } + // Get pointers to the current line and the next line + char *line_start = lines; + // Traverse lines until the current line is found. + for (int i = 0; i < cy; i++) { + line_start = strchr(line_start, '\n') + 1; + // Error handling in case '\n' is not found. + if (!line_start) { + return; + } + } + char *next_line_start = strchr(line_start, '\n') + 1; + // Error handling in case '\n' is not found for the next line. + if (!next_line_start) { + return; + } + // Get the length of the current line and calculate the remaining part after cx + int line_len = strchr(line_start, '\n') - line_start; + // Calculate the current position in the file + int current_position = line_start - lines; + // Shift the next lines down to create space for the new part + memmove(next_line_start + (line_len - cx), next_line_start, *file_length - (next_line_start - lines)); + // Copy the part of the current line after cx to the next line + memcpy(next_line_start, &line_start[cx], line_len - cx); + // Insert a newline character at position cx in the current line + line_start[cx] = '\n'; + // Adjust the file length after shifting down + *file_length += (line_len - cx); +} + +/// @brief Computes the start and end of the current line in the buffer. +/// +/// @param lines The buffer containing the file contents. +/// @param cy The current line index (0-based). +/// @param line_start Pointer to a pointer that will be set to the start of the line. +/// @param line_end Pointer to a pointer that will be set to the end of the line. +/// @param file_length The total length of the file in bytes. +/// @return int The length of the current line, or 0 if no valid line is found. +int get_line_start_end(char *lines, int cy, char **line_start, char **line_end, const int file_length) +{ + *line_start = lines; + // Find the start of the current line by iterating through newlines. + for (int i = 0; i < cy; i++) { + *line_start = strchr(*line_start, '\n') + 1; + // If '\n' was not found, strchr would return NULL. + if (*line_start == (char *)1) { + *line_end = NULL; + return 0; + } + } + // Ensure we don't go beyond the file_length. + if (*line_start - lines >= file_length) { + // No valid line at this position. + *line_end = NULL; + return 0; + } + // Find the end of the current line (until the next newline character or end + // of file). + *line_end = strchr(*line_start, '\n'); + if (*line_end == NULL) { + // If no newline is found, the line ends at the end of the file. + *line_end = lines + file_length; + } + // Return the length of the current line + return *line_end - *line_start; +} + +/// @brief Returns the length of the current line based on the given line index +/// (cy). +/// +/// @param lines The buffer containing the file contents. +/// @param cy The current line index (0-based). +/// @param file_length The total length of the file in bytes. +/// @return int The length of the current line, or 0 if no valid line is found. +int get_line_length(char *lines, int cy, const int file_length) +{ + char *line_start, *line_end; + // Compute the start and end of the current line. + int line_length = get_line_start_end(lines, cy, &line_start, &line_end, file_length); + // If line_end is NULL, there is no valid line, so return 0. + if (line_end == NULL) { + return 0; + } + // Return the length of the current line. + return line_length; +} + +/// @brief Updates the status message with the current editor state. +/// +/// @param buffer The buffer where the status message will be stored. +/// @param bufsize The size of the buffer. +/// @param cy The current line index (0-based). +/// @param cx The current cursor position within the line. +/// @param lines The buffer containing the file contents. +/// @param num_lines The total number of lines in the file. +/// @param file_length Pointer to the current length of the file in bytes. +/// @param insert_active The flag indicating if insert mode is active. +void update_status_message(char *buffer, size_t bufsize, int cy, int cx, char *lines, int num_lines, int *file_length, int insert_active) +{ + // Prepare the status message + snprintf(buffer, bufsize, "(y:%3d, x:%3d, line_len:%3d, lines:%3d, file_length:%3d %s)\n", + cy, cx, get_line_length(lines, cy, *file_length), num_lines, *file_length, + insert_active ? "INS" : " "); +} + +/// @brief Edits the file buffer interactively, allowing navigation and editing. +/// +/// @param lines The buffer containing the file contents. +/// @param bufsize The size of the buffer. +/// @param num_lines The number of lines in the file. +/// @param filename The name of the file to save changes to. +/// @param file_length Pointer to the current length of the file in bytes. +/// @return void +void edit_file(char *lines, size_t bufsize, int num_lines, const char *filename, int *file_length) +{ + int cx = 0, cy = 0; // Cursor position (cx: column, cy: row) + int c, insert_active = 0; // Insert mode flag + + char *line_start = lines, *line_end; + int line_len = get_line_start_end(lines, cy, &line_start, &line_end, *file_length); + + char message[MAX_LINE_LENGTH] = { 0 }; + + update_status_message(message, MAX_LINE_LENGTH, cy, cx, lines, num_lines, file_length, insert_active); + + while (1) { + // Clear the screen and move the cursor to the top-left corner. + printf("\033[H\033[J"); + + // Print the initial file content. + puts(lines); + putchar('\n'); + puts("================================================================================"); + puts("[ \033[1;32m^W Save \033[1;31m^C Quit\033[0m ]\n"); + puts(message); + message[0] = '\0'; + + // Move cursor to the start of the file. + printf("\033[%d;%dH", cy + 1, cx + 1); + + c = getchar(); + + line_len = get_line_start_end(lines, cy, &line_start, &line_end, *file_length); + + // Handle arrow keys and escape sequences. + if (c == '\033') { + pr_debug("[ ](%d)\n", c); c = getchar(); + pr_debug("[%c](%d)\n", c, c); if (c == '[') { - c = getchar(); // Get the char. - if ((c == 'A') || (c == 'B')) { - pr_debug("UP\n"); - } else if (c == 'D') { - pr_debug("RIGHT\n"); - } else if (c == 'C') { - pr_debug("LEFT\n"); - } else if (c == 'H') { - pr_debug("HOME\n"); - } else if (c == 'F') { - pr_debug("END\n"); - } else if (c == '3') { - pr_debug("NO-IDEA\n"); + c = getchar(); + pr_debug("[%c](%d)\n", c, c); + // UP Arrow. + if (c == 'A') { + if (cy > 0) { + int curr_line_len = line_len; + int next_line_len = get_line_length(lines, cy - 1, *file_length); + cy--; + if (((cx == curr_line_len) && curr_line_len) || (cx > next_line_len)) { + cx = next_line_len; + } + } + } + // DOWN Arrow. + else if (c == 'B') { + if ((line_start - lines + cx) < *file_length - 1) { + int curr_line_len = line_len; + int next_line_len = get_line_length(lines, cy + 1, *file_length); + cy++; + if (((cx == curr_line_len) && curr_line_len) || (cx > next_line_len)) { + cx = next_line_len; + } + } + } + // RIGHT Arrow. + else if (c == 'C') { + // Move right within the current line. + if (cx < line_len) { + cx++; + } + // If at the end of the current line, move to the next line + // if it exists. + else if (cy < num_lines - 1) { + cy++; + // Move cursor to the beginning of the next line. + cx = 0; + } + } + // LEFT Arrow. + else if (c == 'D') { + // Move left within the current line + if (cx > 0) { + cx--; + } + // If at the beginning of the current line, move to the end + // of the previous line if it exists. + else if (cy > 0) { + cy--; + // Move cursor to the end of the previous line. + cx = get_line_length(lines, cy, *file_length); + } + } + // INSERT + else if (c == '2') { + if (getchar() == '~') { + insert_active = !insert_active; + printf(insert_active ? "\033[3 q" : "\033[0 q"); // Change cursor appearance. + } + } + // HOME + else if (c == 'H') { + cx = 0; + } + // END + else if (c == 'F') { + cx = line_len; + } + // PAGE UP + else if (c == '5') { + if (getchar() == '~') { + cy = (cy < PAGE_SIZE) ? 0 : cy - PAGE_SIZE; + line_len = get_line_start_end(lines, cy, &line_start, &line_end, *file_length); + if (cx > line_len) { + cx = line_len; + } + } + } + // PAGE DOWN + else if (c == '6') { + if (getchar() == '~') { + cy = (cy + PAGE_SIZE >= num_lines) ? num_lines - 1 : cy + PAGE_SIZE; + line_len = get_line_start_end(lines, cy, &line_start, &line_end, *file_length); + if (cx > line_len) { + cx = line_len; + } + } + } + // Handle Ctrl + Arrows. + else if (c == '1') { + c = getchar(); + pr_debug("[%c](%d)\n", c, c); + + // CTRL + ARROW + if (c == ';') { + c = getchar(); + pr_debug("[%c](%d)\n", c, c); + if (c == '5') { + c = getchar(); + pr_debug("[%c](%d)\n", c, c); + // Handle Ctrl+Right Arrow (Move to the beginning of the next word). + if (c == 'C') { + // Skip spaces first + while (cx < line_len && line_start[cx] == ' ') { + cx++; + } + // Move to the end of the current word (non-space characters) + while (cx < line_len && line_start[cx] != ' ') { + cx++; + } + } + // Handle Ctrl+Left Arrow (Move to the beginning of the previous word) + else if (c == 'D') { + // Move left past spaces first + while (cx > 0 && line_start[cx - 1] == ' ') { + cx--; + } + // Move left to the beginning of the current word (non-space characters) + while (cx > 0 && line_start[cx - 1] != ' ') { + cx--; + } + } + } + } } } - } else if (c == '\b') { - putchar('\b'); - } else if (c == '\t') { - pr_debug("TAB\n"); - } else if (c == 127) { - pr_debug("127\n"); - putchar(127); - } else if (iscntrl(c)) { - if (c == CTRL('C')) { - pr_debug("CTRL(C)\n"); - break; - } else if (c == CTRL('U')) { - pr_debug("CTRL(U)\n"); - } else if (c == CTRL('D')) { - pr_debug("CTRL(D)\n"); + } + // Ctrl+W (Save file) + else if (c == 0x17) { + if (save_file(filename, lines, *file_length)) { + sprintf(message, "\033[1;33mFile saved!\033[0m\n"); + continue; } - } else if ((c > 0) && (c != '\n')) { - putchar(c); - } else { - pr_debug("Unrecognized character %02x (%c)\n", c, c); } - } while (true); + // Ctrl+C (Exit) + else if (c == 0x03) { + printf("\033[%d;%dH", num_lines + 4, 0); + printf("**Exiting without saving**\n"); + printf("\033[0 q"); + break; + } + // DELETE + else if (c == 127) { + // Check if we're at the end of the file (i.e., cx is at the last character) + if ((line_start - lines + cx) < *file_length - 1) { + memmove(&line_start[cx], &line_start[cx + 1], *file_length - (line_start - lines + cx + 1)); + if (line_start[cx] == '\n') { + num_lines--; + } + // Decrease file length after deleting. + (*file_length)--; + } + } + // Backspace + else if (c == '\b') { + if (cx > 0) { + // If not at the beginning of the line, remove the character at the cursor + memmove(&line_start[cx - 1], &line_start[cx], *file_length - (line_start - lines + cx)); + cx--; + } else if (cy > 0) { + line_len = get_line_length(lines, cy - 1, *file_length); + // If not at the beginning of the line, remove the character at the cursor + memmove(&line_start[cx - 1], &line_start[cx], *file_length - (line_start - lines + cx)); + // If at the beginning of the line (cx == 0), move to the previous line + cy--; + cx = line_len; + num_lines--; + } + (*file_length)--; + } + // Handle Tab key ('\t') + else if (c == '\t') { + int spaces_to_add = TAB_SIZE - (cx % TAB_SIZE); + // Shift file content forward by the number of spaces needed for the tab. + memmove(&line_start[cx + spaces_to_add], &line_start[cx], *file_length - (line_start - lines + cx)); + // Insert spaces to simulate the tab + memset(&line_start[cx], ' ', spaces_to_add); + // Move cursor forward by the tab size and update file length + cx += spaces_to_add; + // Increment file length to account for the new character. + (*file_length) += spaces_to_add; + } + // Handle Enter key ('\n') + else if (c == '\n') { + // Shift file content forward by 1 byte to make space for '\n' + memmove(&line_start[cx + 1], &line_start[cx], *file_length - (line_start - lines + cx)); + // Insert the '\n' character at the current cursor position + line_start[cx] = '\n'; + // Move to the next line, reset column, and increment number of lines + cy++; + cx = 0; + num_lines++; + // Increment file length to account for the new character. + (*file_length)++; + } + // Printable characters + else if (c >= 32 && c <= 126) { + // If not in insert mode, shift content to the right by 1 byte to make space for new character. + if (!insert_active) { + memmove(&line_start[cx + 1], &line_start[cx], *file_length - (line_start - lines + cx)); + } + // Insert the character at the current cursor position. + line_start[cx] = c; + // Move cursor forward by 1. + cx++; + // Increment file length to account for the new character. + (*file_length)++; + } + + update_status_message(message, MAX_LINE_LENGTH, cy, cx, lines, num_lines, file_length, insert_active); + } +} + +void enable_raw_mode(void) +{ + tcgetattr(STDIN_FILENO, &orig_termios); + struct termios raw = orig_termios; + raw.c_lflag &= ~(ECHO | ICANON | ISIG); // Disable echo, canonical mode, and signals + tcsetattr(STDIN_FILENO, 0, &raw); +} + +void disable_raw_mode(void) +{ + tcsetattr(STDIN_FILENO, 0, &orig_termios); +} + +// Main function remains the same as before... +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + char filename[256]; + strncpy(filename, argv[1], 256); + + char buffer[BUFFER_SIZE]; + + int file_length = 0; + + // Load the file + int num_lines = load_file(filename, buffer, BUFFER_SIZE, &file_length); + + // Enable raw mode for real-time editing + enable_raw_mode(); + + // Edit the file + edit_file(buffer, BUFFER_SIZE, num_lines, filename, &file_length); + + // Restore terminal mode + disable_raw_mode(); - __set_echo(true); return 0; } diff --git a/programs/login.c b/programs/login.c index 1858125c..c0a4c989 100644 --- a/programs/login.c +++ b/programs/login.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[LOGIN ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[LOGIN ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. #include #include @@ -78,6 +78,8 @@ static inline int __read_input(char *buffer, size_t size, int show) do { c = getchar(); // Read a character from input + pr_debug("%c\n", c); + // Ignore EOF and null or tab characters if (c == EOF || c == 0 || c == '\t') { continue; @@ -113,85 +115,117 @@ static inline int __read_input(char *buffer, size_t size, int show) continue; } + // Ctrl+C + if (c == 0x03) { + memset(buffer, 0, size); // Clear buffer + putchar('\n'); + return -1; // Return -1 on Ctrl+C + } + + // CTRL+U + if (c == 0x15) { + memset(buffer, 0, size); // Clear the current command + if (show) { + // Clear the current command from display + while (index--) { + putchar('\b'); // Move cursor back + } + } + index = 0; // Reset index + continue; + } + // Handle escape sequences (for arrow keys, home, end, etc.) if (c == '\033') { c = getchar(); // Get the next character if (c == '[') { // Get the direction key (Left, Right, Home, End, Insert, Delete) c = getchar(); - - if (c == 'D') { // LEFT Arrow + // LEFT Arrow + if (c == 'D') { if (index > 0) { if (show) { puts("\033[1D"); } // Move the cursor left index--; // Decrease index } - } else if (c == 'C') { // RIGHT Arrow + } + // RIGHT Arrow + else if (c == 'C') { if (index < length) { if (show) { puts("\033[1C"); } // Move the cursor right index++; // Increase index } - } else if (c == '1') { // HOME - if (show) { printf("\033[%dD", index); } // Move cursor to the beginning - index = 0; // Set index to the start - } else if (c == '4') { // END + } + // HOME + else if (c == 'H') { + if (show) { printf("\033[%dD", index); } // Move cursor to the beginning + index = 0; // Set index to the start + } + // END + else if (c == 'F') { if (show) { printf("\033[%dC", length - index); } // Move cursor to the end index = length; // Set index to the end - } else if (c == '2') { // INSERT - insert_active = !insert_active; // Toggle insert mode - } else if (c == '3') { // DELETE - if (index < length) { - --length; // Decrease length - if (show) { putchar(0x7F); } // Show delete character - // Shift left to remove character at index - memmove(buffer + index, buffer + index + 1, length - index + 1); - } } - - } else if (c == '^') { - // Handle special commands (Ctrl+C, Ctrl+U) - c = getchar(); - if (c == 'C') { - memset(buffer, 0, size); // Clear buffer - putchar('\n'); - return -1; // Return -1 on Ctrl+C + // INSERT + else if (c == '2') { + if (getchar() == '~') { + // Toggle insert mode. + insert_active = !insert_active; + if (insert_active) { + // Change cursor to an underline cursor. + printf("\033[3 q"); + } else { + // Change cursor back to a block cursor (default). + printf("\033[0 q"); + } + } } - - if (c == 'U') { - memset(buffer, 0, size); // Clear the current command - if (show) { - // Clear the current command from display - while (index--) { - putchar('\b'); // Move cursor back + // DELETE + else if (c == '3') { + if (getchar() == '~') { + if (index < length) { + --length; // Decrease length + if (show) { putchar(0x7F); } // Show delete character + // Shift left to remove character at index + memmove(buffer + index, buffer + index + 1, length - index + 1); } } - index = 0; // Reset index + } + // PAGE_UP + else if (c == '5') { + if (getchar() == '~') { + // Nothing to do. + } + } + // PAGE_DOWN + else if (c == '6') { + if (getchar() == '~') { + // Nothing to do. + } } } continue; } - // Handle alphanumeric input - if (isdigit(c) || isalpha(c)) { - // Handle insertion based on insert mode - if (!insert_active) { - // Shift buffer to the right to insert new character - memmove(buffer + index + 1, buffer + index, length - index + 1); - } else if (show && (index < length - 1)) { - puts("\033[1C"); // Move cursor right - putchar('\b'); // Prepare to delete the character - } + // Handle insertion based on insert mode + if (!insert_active) { + // Shift buffer to the right to insert new character + memmove(buffer + index + 1, buffer + index, length - index + 1); + } else if (show && (index < length - 1)) { + puts("\033[1C"); // Move cursor right + putchar('\b'); // Prepare to delete the character + } - buffer[index++] = c; // Insert new character - length++; // Increase length + buffer[index++] = c; // Insert new character + length++; // Increase length - if (show) { putchar(c); } // Show new character + if (show) { putchar(c); } // Show new character - // Check if we reached the buffer limit - if (index == (size - 1)) { - buffer[index] = 0; // Null-terminate the buffer - break; // Exit loop if buffer is full - } + // Check if we reached the buffer limit + if (index == (size - 1)) { + buffer[index] = 0; // Null-terminate the buffer + break; // Exit loop if buffer is full } + } while (length < size); return length; // Return total length of input diff --git a/programs/shell.c b/programs/shell.c index 7025e9a8..3f59518a 100644 --- a/programs/shell.c +++ b/programs/shell.c @@ -3,6 +3,12 @@ /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. +// Setup the logging for this file (do this before any other include). +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SHELL ]" ///< Change header. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. +#include "io/debug.h" // Include debugging functions. + #include #include #include @@ -22,29 +28,30 @@ #include #include #include +#include /// Maximum length of commands. #define CMD_LEN 64 /// Maximum lenght of the history. #define HISTORY_MAX 10 +static inline void rb_history_entry_copy(char *dest, const char *src, unsigned size) +{ + strncpy(dest, src, size); +} + +/// Initialize the two-dimensional ring buffer for integers. +DECLARE_FIXED_SIZE_2D_RING_BUFFER(char, history, HISTORY_MAX, CMD_LEN, 0) + // Required by `export` #define ENV_NORM 1 #define ENV_BRAK 2 #define ENV_PROT 3 -// The input command. -static char cmd[CMD_LEN] = { 0 }; -// The index of the cursor. -static size_t cmd_cursor_index = 0; // History of commands. -static char history[HISTORY_MAX][CMD_LEN] = { 0 }; -// The current write index inside the history. -static int history_write_index = 0; -// The current read index inside the history. -static int history_read_index = 0; -// Boolean used to check if the history is full. -static bool_t history_full = false; +static rb_history_t history; +// History reading index. +static unsigned history_index; // Store the last command status static int status = 0; // Store the last command status as string @@ -71,708 +78,1104 @@ static inline int __is_separator(char c) return ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')); } +/// @brief Count the number of words in a given sentence. +/// @param sentence Pointer to the input sentence. +/// @return The number of words in the sentence or -1 if input is invalid. static inline int __count_words(const char *sentence) { + // Check if the input sentence is valid. + if (sentence == NULL) { + pr_crit("__count_words: Invalid input, sentence is NULL.\n"); + return -1; // Return -1 to indicate an error. + } int result = 0; bool_t inword = false; const char *it = sentence; + // Iterate over each character in the sentence. do { + // Check if the current character is a word separator. if (__is_separator(*it)) { + // If we are currently inside a word, mark the end of the word. if (inword) { inword = false; - result++; + result++; // Increment the word count. } } else { + // If the character is not a separator, mark that we are inside a word. inword = true; } } while (*it++); return result; } -static inline void __set_echo(bool_t active) -{ - struct termios _termios; - tcgetattr(STDIN_FILENO, &_termios); - if (active) { - _termios.c_lflag |= (ICANON | ECHO); - } else { - _termios.c_lflag &= ~(ICANON | ECHO); - } - tcsetattr(STDIN_FILENO, 0, &_termios); -} - +/// @brief Checks if a specified folder contains a specific entry of a certain type. +/// @param folder The path to the folder to search in. +/// @param entry The name of the entry to search for. +/// @param accepted_type The type of entry to search for (e.g., file, directory). +/// @param result Pointer to store the found entry, if any. +/// @return Returns 1 if the entry is found, 0 otherwise or in case of an error. static inline int __folder_contains( const char *folder, const char *entry, int accepted_type, dirent_t *result) { + // Validate input parameters. + if ((folder == NULL) || (entry == NULL) || (result == NULL)) { + pr_crit("__folder_contains: Invalid input parameters.\n"); + return 0; // Return 0 to indicate an error. + } + // Attempt to open the folder with read-only and directory flags. int fd = open(folder, O_RDONLY | O_DIRECTORY, 0); if (fd == -1) { - return 0; + pr_crit("__folder_contains: Failed to open folder: %s\n", folder); + return 0; // Return 0 if the folder couldn't be opened. + } + // Prepare variables for the search. + dirent_t dent; // Variable to hold the directory entry during iteration. + size_t entry_len; // Length of the entry name. + int found = 0; // Flag to indicate if the entry was found. + // Calculate the length of the entry name. + entry_len = strlen(entry); + if (entry_len == 0) { + pr_crit("__folder_contains: Invalid entry name (empty).\n"); + close(fd); // Close the folder before returning. + return 0; // Return 0 if the entry name is empty. } - // Prepare the variables for the search. - dirent_t dent; - size_t entry_len = strlen(entry); - int found = 0; + // Iterate over the directory entries. while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + // If an accepted type is specified and doesn't match the current entry type, skip. if (accepted_type && (accepted_type != dent.d_type)) { continue; } + // Compare the entry name with the current directory entry name. if (strncmp(entry, dent.d_name, entry_len) == 0) { + // If a match is found, store the result and mark the entry as found. *result = dent; found = 1; - break; + break; // Exit the loop as the entry was found. } } + // Close the directory file descriptor. close(fd); + // Return whether the entry was found. return found; } +/// @brief Searches for a specified entry in the system's PATH directories. +/// @param entry The name of the entry to search for. +/// @param result Pointer to store the found entry, if any. +/// @return Returns 1 if the entry is found, 0 otherwise. static inline int __search_in_path(const char *entry, dirent_t *result) { - // Determine the search path. + // Validate input parameters. + if ((entry == NULL) || (result == NULL)) { + pr_crit("__search_in_path: Invalid input parameters.\n"); + return 0; // Return 0 to indicate an error. + } + // Retrieve the PATH environment variable. char *PATH_VAR = getenv("PATH"); - if (PATH_VAR == NULL) + if (PATH_VAR == NULL) { + // If PATH is not set, default to commonly used binary directories. PATH_VAR = "/bin:/usr/bin"; - // Copy the path. - char *path = strdup(PATH_VAR); - // Iterate through the path entries. - char *token = strtok(path, ":"); - if (token == NULL) { - free(path); - return 0; } - do { - if (__folder_contains(token, entry, DT_REG, result)) - return 1; - } while ((token = strtok(NULL, ":"))); - free(path); - return 0; + // Prepare for tokenizing the path using custom logic. + char token[NAME_MAX] = { 0 }; // Buffer to hold each token (directory). + size_t offset = 0; // Offset for the tokenizer. + // Iterate through each directory in the PATH. + while (tokenize(PATH_VAR, ":", &offset, token, NAME_MAX)) { + // Search for the entry in the current directory (tokenized directory). + if (__folder_contains(token, entry, DT_REG, result)) { + return 1; // Return 1 if the entry is found. + } + } + return 0; // Return 0 if the entry was not found. } -/// @brief Prints the prompt. +/// @brief Prints the command prompt with user, hostname, time, and current +/// working directory. static inline void __prompt_print(void) { // Get the current working directory. char CWD[PATH_MAX]; - getcwd(CWD, PATH_MAX); - // If the current working directory is equal to HOME, show ~. + if (getcwd(CWD, PATH_MAX) == NULL) { + pr_crit("__prompt_print: Failed to get current working directory.\n"); + strcpy(CWD, "error"); + } + // Get the HOME environment variable. char *HOME = getenv("HOME"); - if (HOME != NULL) - if (strcmp(CWD, HOME) == 0) + if (HOME != NULL) { + // If the current working directory is equal to HOME, replace it with '~'. + if (strcmp(CWD, HOME) == 0) { strcpy(CWD, "~\0"); - // Get the user. + } + } + // Get the USER environment variable. char *USER = getenv("USER"); if (USER == NULL) { + pr_crit("__prompt_print: Failed to get USER environment variable.\n"); USER = "error"; } + // Get the current time. time_t rawtime = time(NULL); + if (rawtime == (time_t)(-1)) { + pr_crit("__prompt_print: Failed to get current time.\n"); + rawtime = 0; // Set to 0 in case of failure. + } + // Convert time to local time format. tm_t *timeinfo = localtime(&rawtime); - // Get the hostname. + if (timeinfo == NULL) { + pr_crit("__prompt_print: Failed to convert time to local time.\n"); + // Use a default value to avoid segmentation faults. + static tm_t default_time = { 0 }; + timeinfo = &default_time; + } + // Get the hostname using uname. char *HOSTNAME; utsname_t buffer; if (uname(&buffer) < 0) { + pr_crit("__prompt_print: Failed to get hostname using uname.\n"); HOSTNAME = "error"; } else { HOSTNAME = buffer.nodename; } + // Print the formatted prompt. printf(FG_GREEN "%s" FG_WHITE "@" FG_CYAN "%s " FG_BLUE_BRIGHT "[%02d:%02d:%02d]" FG_WHITE " [%s] " FG_RESET "\n-> %% ", USER, HOSTNAME, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, CWD); } +/// @brief Retrieves the value of the specified environment variable or special shell variables. +/// @param var The name of the environment variable or special variable to retrieve. +/// @return Returns the value of the variable if found, or NULL if not. static char *__getenv(const char *var) { + // Ensure the input variable name is valid (non-NULL and non-empty). + if (var == NULL || strlen(var) == 0) { + return NULL; + } + // If the variable has a length greater than 1, retrieve it as a standard environment variable. if (strlen(var) > 1) { return getenv(var); } - + // Handle special variables like `$?`, which represents the status of the last command. if (var[0] == '?') { - sprintf(status_buf, "%d", status); - return status_buf; + // Assuming 'status' is a global or accessible variable containing the last command status. + sprintf(status_buf, "%d", status); // Convert the status to a string. + return status_buf; // Return the status as a string. } - - // TODO: implement access to argv - /* int arg = strtol(var, NULL, 10); */ - /* if (arg < argc) { */ - /* return argv[arg]; */ - /* } */ - + // TODO: Implement access to `argv` for positional parameters (e.g., $1, $2). +#if 0 + int arg = strtol(var, NULL, 10); // Convert the variable name to an integer. + if (arg < argc) { // Ensure the argument index is within bounds. + return argv[arg]; // Return the corresponding argument from `argv`. + } +#endif + // If no match is found, return NULL. return NULL; } -static void ___expand_env(char *str, char *buf, size_t buf_len, size_t str_len, bool_t null_terminate) +/// @brief Expands environmental variables in a string and stores the result in the buffer. +/// @param str The input string containing potential environmental variables. +/// @param buf The buffer where the expanded string will be stored. +/// @param buf_len The maximum length of the buffer. +/// @param str_len The length of the input string (if provided, otherwise it will be calculated). +/// @param null_terminate If true, the resulting buffer will be null-terminated. +static void ___expand_env(char *str, size_t str_len, char *buf, size_t buf_len, bool_t null_terminate) { + // Input validation: Ensure that str and buf are not NULL, and that buf_len is valid. + if ((str == NULL) || (buf == NULL) || (buf_len == 0)) { + pr_crit("Error: Invalid input parameters to ___expand_env.\n"); + return; + } // Buffer where we store the name of the variable. char buffer[BUFSIZ] = { 0 }; // Flags used to keep track of the special characters. unsigned flags = 0; - // We keep track of where the variable names starts + // Pointer to where the variable name starts. char *env_start = NULL; - // Where we store the retrieved environmental variable value. + // Pointer to store the retrieved environmental variable value. char *ENV = NULL; - // Get the length of the string. - if (!str_len) { - str_len = strlen(str); - } // Position where we are writing on the buffer. int b_pos = 0; - // Iterate the string. + // Iterate through the input string. for (int s_pos = 0; s_pos < str_len; ++s_pos) { - if ((s_pos == 0) && str[s_pos] == '"') - continue; - if ((s_pos == (str_len - 1)) && str[s_pos] == '"') + // Skip quotes at the start and end of the string. + if ((s_pos == 0 && str[s_pos] == '"') || (s_pos == (str_len - 1) && str[s_pos] == '"')) { continue; - // If we find the backslash, we need to protect the next character. + } + // Handle backslash as escape character. if (str[s_pos] == '\\') { - if (bit_check(flags, ENV_PROT)) - buf[b_pos++] = '\\'; - else - bit_set_assign(flags, ENV_PROT); + if (bit_check(flags, ENV_PROT)) { + if (b_pos < buf_len - 1) { + buf[b_pos++] = '\\'; // If protected, add the backslash. + } + } else { + bit_set_assign(flags, ENV_PROT); // Set the protection flag. + } continue; } - // If we find the dollar, we need to catch the meaning. + // Handle environmental variable expansion with $. if (str[s_pos] == '$') { - // If the previous character is a backslash, we just need to print the dollar. if (bit_check(flags, ENV_PROT)) { - buf[b_pos++] = '$'; - } else if ((s_pos < (str_len - 2)) && ((str[s_pos + 1] == '{'))) { - // Toggle the open bracket method of accessing env variables. + // If protected by backslash, just add the dollar sign. + if (b_pos < buf_len - 1) { + buf[b_pos++] = '$'; + } + } else if ((s_pos < (str_len - 2)) && (str[s_pos + 1] == '{')) { + // Handle ${VAR} syntax for environmental variables. bit_set_assign(flags, ENV_BRAK); - // We need to skip both the dollar and the open bracket `${`. - env_start = &str[s_pos + 2]; + env_start = &str[s_pos + 2]; // Start of the variable name. } else { - // Toggle the normal method of accessing env variables. + // Handle normal $VAR syntax. bit_set_assign(flags, ENV_NORM); - // We need to skip the dollar `$`. - env_start = &str[s_pos + 1]; + env_start = &str[s_pos + 1]; // Start of the variable name. } continue; } + // Handle ${VAR} style environmental variables. if (bit_check(flags, ENV_BRAK)) { if (str[s_pos] == '}') { - // Copy the environmental variable name. + // Extract and expand the environmental variable name. strncpy(buffer, env_start, &str[s_pos] - env_start); - // Search for the environmental variable, and print it. - if ((ENV = __getenv(buffer))) - for (int k = 0; k < strlen(ENV); ++k) + buffer[&str[s_pos] - env_start] = '\0'; // Null-terminate. + // Retrieve the value of the environmental variable. + if ((ENV = __getenv(buffer)) != NULL) { + // Copy the value into the buffer. + for (int k = 0; k < strlen(ENV) && b_pos < buf_len - 1; ++k) { buf[b_pos++] = ENV[k]; - // Remove the flag. - bit_clear_assign(flags, ENV_BRAK); + } + } + bit_clear_assign(flags, ENV_BRAK); // Clear the flag. } continue; } + // Handle $VAR style environmental variables. if (bit_check(flags, ENV_NORM)) { if (str[s_pos] == ':') { - // Copy the environmental variable name. strncpy(buffer, env_start, &str[s_pos] - env_start); - // Search for the environmental variable, and print it. - if ((ENV = __getenv(buffer))) - for (int k = 0; k < strlen(ENV); ++k) + buffer[&str[s_pos] - env_start] = '\0'; // Null-terminate. + // Retrieve the value of the environmental variable. + if ((ENV = __getenv(buffer)) != NULL) { + for (int k = 0; k < strlen(ENV) && b_pos < buf_len - 1; ++k) { buf[b_pos++] = ENV[k]; - // Copy the `:`. - buf[b_pos++] = str[s_pos]; - // Remove the flag. + } + } + // Add the ':' character and clear the flag. + if (b_pos < buf_len - 1) { + buf[b_pos++] = str[s_pos]; + } bit_clear_assign(flags, ENV_NORM); } continue; } - buf[b_pos++] = str[s_pos]; + // Add normal characters to the buffer. + if (b_pos < buf_len - 1) { + buf[b_pos++] = str[s_pos]; + } } + // Handle any remaining environmental variable in $VAR style. if (bit_check(flags, ENV_NORM)) { - // Copy the environmental variable name. - size_t var_len = str_len - (env_start - str); - strncpy(buffer, env_start, var_len); - // Search for the environmental variable, and print it. - if ((ENV = __getenv(buffer))) { - for (int k = 0; k < strlen(ENV); ++k) { + strncpy(buffer, env_start, str_len - (env_start - str)); + buffer[str_len - (env_start - str)] = '\0'; + if ((ENV = __getenv(buffer)) != NULL) { + for (int k = 0; k < strlen(ENV) && b_pos < buf_len - 1; ++k) { buf[b_pos++] = ENV[k]; } } } - - if (null_terminate) { - buf[b_pos] = 0; + // Null-terminate the buffer if requested. + if (null_terminate && b_pos < buf_len) { + buf[b_pos] = '\0'; } } +/// @brief Simplified version of ___expand_env for use without specifying length and null-termination. +/// @param str The input string containing environmental variables. +/// @param buf The buffer where the expanded result will be stored. +/// @param buf_len The size of the buffer. static void __expand_env(char *str, char *buf, size_t buf_len) { - ___expand_env(str, buf, buf_len, 0, false); + ___expand_env(str, 0, buf, buf_len, false); } +/// @brief Sets environment variables based on arguments. +/// @param argc The number of arguments passed. +/// @param argv The array of arguments, where each argument is a name-value pair (e.g., NAME=value). +/// @return Returns 0 on success, 1 on failure. static int __export(int argc, char *argv[]) { char name[BUFSIZ] = { 0 }, value[BUFSIZ] = { 0 }; char *first, *last; size_t name_len, value_start; + // Loop through each argument, starting from argv[1]. for (int i = 1; i < argc; ++i) { // Get a pointer to the first and last occurrence of `=` inside the argument. - first = strchr(argv[i], '='), last = strrchr(argv[i], '='); - // Check validity of first and last, and check that they are the same. - if (!first || !last || (last < argv[i]) || (first != last)) - continue; - // Length of the name. + first = strchr(argv[i], '='); + last = strrchr(argv[i], '='); + // Check the validity of first and last, and ensure they are the same (i.e., a single `=`). + if (!first || !last || (last < argv[i]) || (first != last)) { + printf("Invalid format: '%s'. Expected NAME=value format.\n", argv[i]); + continue; // Skip this argument if invalid. + } + // Calculate the length of the name (part before `=`). name_len = last - argv[i]; - // Set where the value starts. + + // Ensure that the name is not empty. + if (name_len == 0) { + printf("Invalid format: '%s'. Name cannot be empty.\n", argv[i]); + continue; // Skip this argument if the name is empty. + } + // Set the starting index of the value (part after `=`). value_start = (last + 1) - argv[i]; - // Copy the name. + // Copy the name into the `name` buffer. strncpy(name, argv[i], name_len); - // Expand the environmental variables inside the argument. + name[name_len] = '\0'; // Null-terminate the name string. + // Expand the environmental variables inside the value part of the argument. __expand_env(&argv[i][value_start], value, BUFSIZ); - // Try to set the environmental variable. + // Try to set the environmental variable if both name and value are valid. if ((strlen(name) > 0) && (strlen(value) > 0)) { if (setenv(name, value, 1) == -1) { - printf("Failed to set environmental variable.\n"); - return 1; + printf("Failed to set environmental variable: %s\n", name); + return 1; // Return 1 on failure to set the environment variable. } + } else { + printf("Invalid variable assignment: '%s'. Name and value must be non-empty.\n", argv[i]); } } - return 0; + return 0; // Return 0 on success. } +/// @brief Changes the current working directory. +/// @param argc The number of arguments passed. +/// @param argv The array of arguments, where argv[0] is the command and argv[1] is the target directory. +/// @return Returns 0 on success, 1 on failure. static int __cd(int argc, char *argv[]) { + // Check if too many arguments are provided. if (argc > 2) { - printf("%s: too many arguments\n", argv[0]); + puts("cd: too many arguments\n"); return 1; } + // Determine the path to change to. const char *path = NULL; if (argc == 2) { path = argv[1]; } else { + // If no argument is provided, use the HOME directory. path = getenv("HOME"); if (path == NULL) { - printf("cd: There is no home directory set.\n"); + puts("cd: There is no home directory set.\n"); return 1; } } - // Get the real path. + // Get the real path of the target directory. char real_path[PATH_MAX]; if (realpath(path, real_path, PATH_MAX) != real_path) { - printf("cd: Failed to resolve directory.\n"); + printf("cd: Failed to resolve directory '%s': %s\n", path, strerror(errno)); return 1; } - // Stat the directory. + // Stat the directory to ensure it exists and get information about it. stat_t dstat; if (stat(real_path, &dstat) == -1) { printf("cd: cannot stat '%s': %s\n", real_path, strerror(errno)); return 1; } - // Check if the directory is actually a symbolic link. + // Check if the path is a symbolic link. if (S_ISLNK(dstat.st_mode)) { char link_buffer[PATH_MAX]; ssize_t len; - // First, read the link. - if ((len = readlink(real_path, link_buffer, sizeof(link_buffer))) < 0) { - printf("cd: Failed to read symlink.\n"); + // Read the symbolic link. + if ((len = readlink(real_path, link_buffer, sizeof(link_buffer) - 1)) < 0) { + printf("cd: Failed to read symlink '%s': %s\n", real_path, strerror(errno)); return 1; } + // Null-terminate the link buffer. link_buffer[len] = '\0'; - // Resolve the link, it might still be a relative path. + // Resolve the symlink to an absolute path. if (realpath(link_buffer, real_path, PATH_MAX) != real_path) { - printf("cd: Failed to resolve symlink to directory.\n"); + printf("cd: Failed to resolve symlink '%s': %s\n", link_buffer, strerror(errno)); return 1; } } - // Open the given directory. - int fd = open(real_path, O_RDONLY | O_DIRECTORY, S_IXUSR); + // Open the directory to ensure it's accessible. + int fd = open(real_path, O_RDONLY | O_DIRECTORY, S_IXUSR | S_IXOTH); if (fd == -1) { printf("cd: %s: %s\n", real_path, strerror(errno)); return 1; } - // Set current working directory. - chdir(real_path); + // Change the current working directory. + if (chdir(real_path) == -1) { + printf("cd: Failed to change directory to '%s': %s\n", real_path, strerror(errno)); + close(fd); + return 1; + } + // Close the directory file descriptor. close(fd); - // Get the updated working directory. + // Get the updated current working directory. char cwd[PATH_MAX]; - getcwd(cwd, PATH_MAX); - // Update the environmental variable. + if (getcwd(cwd, sizeof(cwd)) == NULL) { + printf("cd: Failed to get current working directory: %s\n", strerror(errno)); + return 1; + } + // Update the PWD environment variable to the new directory. if (setenv("PWD", cwd, 1) == -1) { - printf("cd: Failed to set current working directory.\n"); + printf("cd: Failed to set current working directory in environment: %s\n", strerror(errno)); return 1; } putchar('\n'); return 0; } -/// @brief Push the command inside the history. -static inline void __hst_push(char *_cmd) +void __history_print(void) { - // Reset the read index. - history_read_index = history_write_index; - // Check if it is a duplicated entry. - if (history_write_index > 0) { - if (strcmp(history[history_write_index - 1], _cmd) == 0) { - return; - } - } - // Insert the node. - strcpy(history[history_write_index], _cmd); - if (++history_write_index >= HISTORY_MAX) { - history_write_index = 0; - history_full = true; - } - // Reset the read index. - history_read_index = history_write_index; + rb_history_entry_t entry; + rb_history_init_entry(&entry); + pr_notice("H[S:%2u, C:%2u] :\n", history.size, history.count); + for (unsigned i = 0; i < history.count; i++) { + rb_history_get(&history, i, &entry); + pr_notice("[%2u] %s\n", i, entry.buffer); + } + pr_notice("\n"); } -/// @brief Give the key allows to navigate through the history. -static char *__hst_fetch(bool_t up) +/// @brief Push the command inside the history. +/// @param entry The history entry to be added. +/// @return Returns 1 if the entry was successfully added, 0 if it was a duplicate. +static inline int __history_push(rb_history_entry_t *entry) { - if ((history_write_index == 0) && (history_full == false)) { - return NULL; + rb_history_entry_t previous_entry; + // Validate input parameter. + if (entry == NULL) { + pr_crit("__history_push: Invalid entry.\n"); + return 0; } - // If the history is empty do nothing. - char *_cmd = NULL; - // Update the position inside the history. - int next_index = history_read_index + (up ? -1 : +1); - // Check the next index. - if (history_full) { - if (next_index < 0) { - next_index = HISTORY_MAX - 1; - } else if (next_index >= HISTORY_MAX) { - next_index = 0; - } - // Do no read where ne will have to write next. - if (next_index == history_write_index) { - next_index = history_read_index; - return NULL; - } - } else { - if (next_index < 0) { - next_index = 0; - } else if (next_index >= history_write_index) { - next_index = history_read_index; - return NULL; + // Check if there's an existing entry at the back of the history. + if (!rb_history_peek_back(&history, &previous_entry)) { + // Compare the new entry with the last entry to avoid duplicates. + if (strcmp(entry->buffer, previous_entry.buffer) == 0) { + // Return 0 if the new entry is the same as the previous one (duplicate). + return 0; } } - history_read_index = next_index; - _cmd = history[history_read_index]; - // Return the command. - return _cmd; + // Push the new entry to the back of the history ring buffer. + rb_history_push_back(&history, entry); + // Set the history index to the current count, pointing to the end. + history_index = history.count; + // Return 1 to indicate the new entry was successfully added. + return 1; } -/// @brief Completely delete the current command. -static inline void __cmd_clr(void) -{ - // First we need to get back to the end of the line. - while (cmd[cmd_cursor_index] != 0) { - ++cmd_cursor_index; - puts("\033[1C"); - } - memset(cmd, '\0', CMD_LEN); - // Then we delete all the character. - for (size_t it = 0; it < cmd_cursor_index; ++it) { - putchar('\b'); - } - // Reset the index. - cmd_cursor_index = 0; -} - -/// @brief Sets the new command. -static inline void __cmd_set(char *_cmd) +/// @brief Give the key allows to navigate through the history. +static rb_history_entry_t *__history_fetch(char direction) { - // Outputs the command. - printf(_cmd); - // Moves the cursore. - cmd_cursor_index += strlen(_cmd); - // Copies the command. - strcpy(cmd, _cmd); + // If history is empty, return NULL. + if (history.count == 0) { + return NULL; + } + // Move to the previous entry if direction is UP and index is greater than 0. + if ((direction == 'A') && (history_index > 0)) { + history_index--; + } + // Move to the next entry if direction is DOWN and index is less than the history count. + else if ((direction == 'B') && (history_index < history.count)) { + history_index++; + } + // Check if we reached the end of the history in DOWN direction. + if ((direction == 'B') && (history_index == history.count)) { + return NULL; + } + // Return the current history entry, adjusting for buffer wrap-around. + return history.buffer + ((history.tail + history_index) % history.size); } -/// @brief Erases one character from the console. -static inline void __cmd_ers(char c) +/// @brief Append a character to the history entry buffer. +/// @param entry Pointer to the history entry structure. +/// @param index Pointer to the current index in the buffer. +/// @param length Pointer to the current length of the buffer. +/// @param c Character to append to the buffer. +/// @return Returns 1 if the buffer limit is reached, 0 otherwise. +static inline int __command_append( + rb_history_entry_t *entry, + int *index, + int *length, + char c) { - if ((c == '\b') && (cmd_cursor_index > 0)) { - strcpy(cmd + cmd_cursor_index - 1, cmd + cmd_cursor_index); - putchar('\b'); - --cmd_cursor_index; - } else if ((c == 0x7F) && (cmd[0] != 0) && ((cmd_cursor_index + 1) < CMD_LEN)) { - strcpy(cmd + cmd_cursor_index, cmd + cmd_cursor_index + 1); - putchar(0x7F); + // Input validation: Ensure entry, index, and length are valid. + if ((entry == NULL) || (index == NULL) || (length == NULL)) { + pr_crit("Error: Invalid input to __command_append.\n"); + return 1; } + // Ensure index does not exceed the buffer size limit. + if ((*index) >= entry->size) { + pr_crit("Error: Index exceeds buffer size.\n"); + return 1; + } + // Insert the new character at the current index in the buffer, then + // increment the index. + entry->buffer[(*index)++] = c; + // Increase the length of the buffer to reflect the added character. + (*length)++; + // Display the newly added character to the standard output. + putchar(c); + // Check if the buffer limit has been reached. + if ((*index) == (entry->size - 1)) { + // Null-terminate the buffer to ensure it is a valid string. + entry->buffer[(*index)] = 0; + // Indicate that the buffer is full. + return 1; + } + // The buffer limit has not been reached. + return 0; } -/// @brief Appends the character `c` on the command. -static inline int __cmd_app(char c) +/// @brief Clears the current command from both the display and the buffer. +/// @param entry The history entry containing the command buffer. +/// @param index The current index in the command buffer. +/// @param length The total length of the command buffer. +static inline void __command_clear(rb_history_entry_t *entry, int *index, int *length) { - if ((cmd_cursor_index + 1) < CMD_LEN) { - // If at the current index there is a character, shift the entire - // command ahead. - if (cmd[cmd_cursor_index] != 0) { - // Move forward the entire string. - for (unsigned long i = strlen(cmd); i > cmd_cursor_index; --i) { - cmd[i] = cmd[i - 1]; - } - } - // Place the new character. - cmd[cmd_cursor_index++] = c; - return 1; + // Validate the input parameters to avoid null pointer dereference. + if ((entry == NULL) || (index == NULL) || (length == NULL)) { + pr_crit("Invalid parameters passed to __command_clear.\n"); + return; } - return 0; + // Ensure index and length are greater than zero and index does not exceed + // length. + if ((*index < 0) || (*length < 0) || (*index > *length)) { + pr_crit("Invalid index or length values: index=%d, length=%d.\n", *index, *length); + return; + } + // Move the cursor to the end of the current command. + printf("\033[%dC", (*length) - (*index)); + // Clear the current command from the display by moving backwards. + while ((*length)--) { putchar('\b'); } + // Clear the current command from the buffer by setting it to zero. + memset(entry->buffer, 0, entry->size); + // Reset both index and length to zero, as the command is now cleared. + (*index) = (*length) = 0; } -static inline void __cmd_sug(dirent_t *suggestion, size_t starting_position) +/// @brief Suggests a directory entry to be appended to the current command +/// buffer. +/// @param filename Pointer to the file name. +/// @param filetype The file type. +/// @param entry Pointer to the history entry structure. +/// @param index Pointer to the current index in the buffer. +/// @param length Pointer to the current length of the buffer. +static inline void __command_suggest( + const char *filename, + int filetype, + int offset, + rb_history_entry_t *entry, + int *index, + int *length) { - if (suggestion) { - for (size_t i = starting_position; i < strlen(suggestion->d_name); ++i) { - if (__cmd_app(suggestion->d_name[i])) { - putchar(suggestion->d_name[i]); + // Check if there is a valid suggestion to process. + if (filename) { + // Iterate through the characters of the suggested directory entry name. + for (int i = offset; i < strlen(filename); ++i) { + // If there is a character inside the buffer, we + if (entry->buffer[(*index)]) { + if (entry->buffer[(*index)] != filename[i]) { + return; + } else if (entry->buffer[(*index)] != ' ') { + // Move the cursor right by 1. + puts("\033[1C"); + // Increment the index. + (*index)++; + continue; + } } - } - // If we suggested a directory, append a slash. - if (suggestion->d_type == DT_DIR) { - if (__cmd_app('/')) { - putchar('/'); + // Append the current character to the buffer. + // If the buffer is full, break the loop. + if (__command_append(entry, index, length, filename[i])) { + break; } } + // If the suggestion is a directory, append a trailing slash. + if ((filetype == DT_DIR) && (entry->buffer[(*index) - 1] != '/')) { + // Append the slash character to indicate a directory. + __command_append(entry, index, length, '/'); + } } } -static void __cmd_complete(void) +/// @brief Completes the current command based on the given entry, index, and length. +/// @param entry The history entry containing the command buffer. +/// @param index The current index in the command buffer. +/// @param length The total length of the command buffer. +/// @return Returns 0 on success, 1 on failure. +static int __command_complete( + rb_history_entry_t *entry, + int *index, + int *length) { - // Get the lenght of the command. - size_t cmd_len = strlen(cmd); - // Count the number of words. - int words = __count_words(cmd); - // If there are no words, skip. + pr_debug("__command_complete(%s, %2d, %2d)\n", entry->buffer, *index, *length); + + char cwd[PATH_MAX]; // Buffer to store the current working directory. + int words; // Variable to store the word count. + dirent_t dent; // Prepare the dirent variable. + + // Count the number of words in the command buffer. + words = __count_words(entry->buffer); + + // If there are no words in the command buffer, log it and return. if (words == 0) { - return; + pr_debug("__command_complete(%s, %2d, %2d) : No words.\n", entry->buffer, *index, *length); + return 0; } + // Determines if we are at the beginning of a new argument, last character is space. - if (__is_separator(cmd[cmd_len - 1])) { - return; + if (__is_separator(entry->buffer[(*index) - 1])) { + pr_debug("__command_complete(%s, %2d, %2d) : Separator.\n", entry->buffer, *index, *length); + return 0; } - // If the last two characters are two dots `..` append a slash `/`, - // and continue. - if ((cmd_len >= 2) && ((cmd[cmd_len - 2] == '.') && (cmd[cmd_len - 1] == '.'))) { - if (__cmd_app('/')) { - putchar('/'); - return; + + // If the last two characters are two dots `..` append a slash `/`, and + // continue. + if (((*index) >= 2) && ((entry->buffer[(*index) - 1] == '.') && (entry->buffer[(*index) - 2] == '.'))) { + pr_debug("__command_complete(%s, %2d, %2d) : Append '/'.\n", entry->buffer, *index, *length); + if (__command_append(entry, index, length, '/')) { + pr_crit("Failed to append character.\n"); + return 1; } } - char cwd[PATH_MAX]; - getcwd(cwd, PATH_MAX); + + // Attempt to retrieve the current working directory. + if (getcwd(cwd, PATH_MAX) == NULL) { + // Error handling: If getcwd fails, it returns NULL + pr_crit("Failed to get current working directory.\n"); + return 1; + } + // Determines if we are executing a command from current directory. - int is_run_cmd = (words == 1) && (cmd[0] == '.') && (cmd_len > 3) && (cmd[1] == '/'); + int is_run_cmd = ((*index) >= 2) && (entry->buffer[0] == '.') && (entry->buffer[1] == '/'); // Determines if we are entering an absolute path. - int is_abs_path = (words == 1) && (cmd[0] == '/'); - // Prepare the dirent variable. - dirent_t dent; + int is_abs_path = ((*index) >= 1) && (entry->buffer[0] == '/'); + // If there is only one word, we are searching for a command. if (is_run_cmd) { - if (__folder_contains(cwd, cmd + 2, 0, &dent)) { - __cmd_sug(&dent, cmd_len - 2); + if (__folder_contains(cwd, entry->buffer + 2, 0, &dent)) { + pr_debug("__command_complete(%s, %2d, %2d) : Suggest run '%s' -> '%s'.\n", entry->buffer, *index, *length, + entry->buffer + 2, dent.d_name); + __command_suggest( + dent.d_name, + dent.d_type, + (*index) - 2, + entry, + index, + length); } } else if (is_abs_path) { char _dirname[PATH_MAX]; - if (!dirname(cmd, _dirname, sizeof(_dirname))) { - return; + if (!dirname(entry->buffer, _dirname, sizeof(_dirname))) { + return 0; } - const char *_basename = basename(cmd); + const char *_basename = basename(entry->buffer); if (!_basename) { - return; + return 0; } if ((*_dirname == 0) || (*_basename == 0)) { - return; + return 0; } if (__folder_contains(_dirname, _basename, 0, &dent)) { - __cmd_sug(&dent, strlen(_basename)); + pr_debug("__command_complete(%s, %2d, %2d) : Suggest abs '%s' -> '%s'.\n", entry->buffer, *index, *length, + entry->buffer, dent.d_name); + __command_suggest( + dent.d_name, + dent.d_type, + strlen(_basename), + entry, + index, + length); } } else if (words == 1) { - if (__search_in_path(cmd, &dent)) { - __cmd_sug(&dent, cmd_len); + if (__search_in_path(entry->buffer, &dent)) { + pr_debug("__command_complete(%s, %2d, %2d) : Suggest in path '%s' -> '%s'.\n", entry->buffer, *index, *length, + entry->buffer, dent.d_name); + __command_suggest( + dent.d_name, + dent.d_type, + *index, + entry, + index, + length); } } else { // Search the last occurrence of a space, from there on // we will have the last argument. - char *last_argument = strrchr(cmd, ' '); + char *last_argument = strrchr(entry->buffer, ' '); // We need to move ahead of one character if we found the space. last_argument = last_argument ? last_argument + 1 : NULL; // If there is no last argument. if (last_argument == NULL) { - return; + pr_crit("__command_complete: No last argument found in buffer '%s'.\n", entry->buffer); + return 0; } char _dirname[PATH_MAX]; if (!dirname(last_argument, _dirname, sizeof(_dirname))) { - return; + pr_crit("__command_complete: Failed to extract directory name from '%s'.\n", last_argument); + return 0; } const char *_basename = basename(last_argument); if (!_basename) { - return; + pr_crit("__command_complete: Failed to extract basename from '%s'.\n", last_argument); + return 0; } + pr_debug("__command_complete(%s, %2d, %2d) : dirname='%s', basename='%s', last_argument='%s'.\n", entry->buffer, *index, *length, + _dirname, _basename, last_argument); if ((*_dirname != 0) && (*_basename != 0)) { if (__folder_contains(_dirname, _basename, 0, &dent)) { - __cmd_sug(&dent, strlen(_basename)); + pr_debug("__command_complete(%s, %2d, %2d) : Suggest 1 '%s' -> '%s'.\n", entry->buffer, *index, *length, + last_argument, dent.d_name); + __command_suggest( + dent.d_name, + dent.d_type, + strlen(_basename), + entry, + index, + length); } } else if (*_basename != 0) { if (__folder_contains(cwd, _basename, 0, &dent)) { - __cmd_sug(&dent, strlen(_basename)); + pr_debug("__command_complete(%s, %2d, %2d) : Suggest 2 '%s' -> '%s'.\n", entry->buffer, *index, *length, + last_argument, dent.d_name); + __command_suggest( + dent.d_name, + dent.d_type, + strlen(_basename), + entry, + index, + length); } } } + return 0; } -static void __move_cursor_back(int n) +/// @brief Reads user input into a buffer, supporting basic editing features. +/// @param buffer The buffer to store the input string. +/// @param bufsize The maximum size of the buffer. +/// @return The length of the input read, or -1 if a special command (Ctrl+C) is +/// detected. +static inline int __read_command(rb_history_entry_t *entry) { - printf("\033[%dD", n); - cmd_cursor_index -= n; -} + int index = 0, c, length = 0, insert_active = 0; -static void __move_cursor_forward(int n) -{ - printf("\033[%dC", n); - cmd_cursor_index += n; -} + // Clear the buffer at the start + memset(entry->buffer, 0, entry->size); -/// @brief Gets the inserted command. -static void __cmd_get(void) -{ - // Re-Initialize the cursor index. - cmd_cursor_index = 0; - // Initializing the current command line buffer - memset(cmd, '\0', CMD_LEN); - __set_echo(false); do { - int c = getchar(); - // Return Key + // Read a character from input. + c = getchar(); + + // Ignore EOF and null or tab characters + if (c == EOF || c == 0) { + continue; + } + + // Handle newline character to finish input if (c == '\n') { + putchar('\n'); // Display a newline + return length; // Return length of input + } + + // Handle backspace for deletion + if (c == '\b') { + if (index > 0) { + --length; // Decrease length + --index; // Move index back + // Shift the buffer left to remove the character + memmove(entry->buffer + index, entry->buffer + index + 1, length - index + 1); + // Show backspace action. + putchar('\b'); + } + continue; + } + + if (c == '\t') { + __command_complete(entry, &index, &length); + continue; + } + + // Handle space character + if (c == ' ') { + // Shift the content of the buffer to the right to create space for the new character. + memmove(entry->buffer + index + 1, entry->buffer + index, length - index + 1); + // Insert the character at the current index and increment the index. + entry->buffer[index++] = c; + // If insert mode is not active, increase the length of the buffer. + if (!insert_active) { + length++; + } + // Display the newly inserted character. + putchar(c); + continue; + } + + // Ctrl+C + if (c == 0x03) { + // Clear the entire buffer by setting all elements to zero. + memset(entry->buffer, 0, entry->size); + // Print a newline to indicate that Ctrl+C was pressed. putchar('\n'); - // Break the while loop. - __cmd_app(0); - break; + // Return -1 to indicate the Ctrl+C operation. + return -1; } - // It is a special character. + + // CTRL+U + if (c == 0x15) { + // Clear the current command. + __command_clear(entry, &index, &length); + continue; + } + if (c == '\033') { c = getchar(); if (c == '[') { - c = getchar(); // Get the char. + c = getchar(); + // UP/DOWN ARROW if ((c == 'A') || (c == 'B')) { - char *old_cmd = __hst_fetch(c == 'A'); - if (old_cmd != NULL) { - // Clear the current command. - __cmd_clr(); + // Clear the current command. + __command_clear(entry, &index, &length); + // Fetch the history element. + rb_history_entry_t *history_entry = __history_fetch(c); + if (history_entry) { // Sets the command. - __cmd_set(old_cmd); + rb_history_entry_copy(entry->buffer, history_entry->buffer, entry->size); + // Print the old command. + printf(entry->buffer); + // Set index to the end. + index = length = strnlen(entry->buffer, entry->size); } - } else if (c == 'D') { - if (cmd_cursor_index > 0) { - __move_cursor_back(1); + } + // LEFT ARROW + else if (c == 'D') { + pr_debug("%d > 0\n", index); + if (index > 0) { + puts("\033[1D"); // Move the cursor left + index--; // Decrease index } - } else if (c == 'C') { - if ((cmd_cursor_index + 1) < CMD_LEN && (cmd_cursor_index + 1) <= strlen(cmd)) { - __move_cursor_forward(1); + } + // RIGHT ARROW + else if (c == 'C') { + pr_debug("%d < %d\n", index, length); + if (index < length) { + puts("\033[1C"); // Move the cursor right + index++; // Increase index } - } else if (c == 'H') { - __move_cursor_back(cmd_cursor_index); - } else if (c == 'F') { - // Compute the offest to the end of the line, and move only if necessary. - size_t offset = strlen(cmd) - cmd_cursor_index; - if (offset > 0) { - __move_cursor_forward(offset); + } + // HOME + else if (c == 'H') { + printf("\033[%dD", index); // Move cursor to the beginning + index = 0; // Set index to the start + } + // END + else if (c == 'F') { + printf("\033[%dC", length - index); // Move cursor to the end + index = length; // Set index to the end + } + // INSERT + else if (c == '2') { + if (getchar() == '~') { + // Toggle insert mode. + insert_active = !insert_active; + if (insert_active) { + // Change cursor to an underline cursor. + printf("\033[3 q"); + } else { + // Change cursor back to a block cursor (default). + printf("\033[0 q"); + } } - } else if (c == '3') { - c = getchar(); // Get the char. - if (c == '~') { - __cmd_ers(0x7F); + } + // DELETE + else if (c == '3') { + if (getchar() == '~') { + if (index < length) { + --length; // Decrease length + putchar(127); // Show delete character. + // Shift left to remove character at index + memmove(entry->buffer + index, entry->buffer + index + 1, length - index + 1); + } } } - } - } else if (c == '\b') { - __cmd_ers('\b'); - } else if (c == '\t') { - __cmd_complete(); - } else if (c == 127) { - if ((cmd_cursor_index + 1) <= strlen(cmd)) { - strcpy(cmd + cmd_cursor_index, cmd + cmd_cursor_index + 1); - putchar(127); - } - } else if (iscntrl(c)) { - if (c == CTRL('C')) { - // Re-set the index to the beginning. - cmd_cursor_index = 0; - // Go to the new line. - printf("\n"); - // Sets the command. - __cmd_set("\0"); - // Break the while loop. - break; - } else if (c == CTRL('U')) { - // Clear the current command. - __cmd_clr(); - // Sets the command. - __cmd_set("\0"); - } else if (c == CTRL('A')) { - __move_cursor_back(cmd_cursor_index); - } else if (c == CTRL('E')) { - // Compute the offest to the end of the line, and move only if necessary. - size_t offset = strlen(cmd) - cmd_cursor_index; - if (offset > 0) { - __move_cursor_forward(offset); + // PAGE_UP + else if (c == '5') { + if (getchar() == '~') { + // Nothing to do. + } + } + // PAGE_DOWN + else if (c == '6') { + if (getchar() == '~') { + // Nothing to do. + } + } + // CTRL+ARROW + else if (c == '1') { + c = getchar(); + if (c == ';') { + c = getchar(); + if (c == '5') { + c = getchar(); + int move_count = 0; + + // CTRL+RIGHT ARROW + if (c == 'C') { + // Move to the beginning of the next word + // Skip spaces first + while (index < length && entry->buffer[index] == ' ') { + index++; + move_count++; + } + // Move to the end of the current word (non-space characters) + while (index < length && entry->buffer[index] != ' ') { + index++; + move_count++; + } + // Apply all movements to the right in one go. + if (move_count > 0) { + printf("\033[%dC", move_count); + } + } + // CTRL+LEFTY ARROW + else if (c == 'D') { + // Move left past spaces first + while (index > 0 && entry->buffer[index - 1] == ' ') { + index--; + move_count++; + } + // Move left to the beginning of the current word (non-space characters) + while (index > 0 && entry->buffer[index - 1] != ' ') { + index--; + move_count++; + } + // Apply all movements to the left in one go. + if (move_count > 0) { + printf("\033[%dD", move_count); + } + } + } + } } - } else if (c == CTRL('D')) { - // Go to the new line. - printf("\n"); - exit(0); - } - } else if ((c > 0) && (c != '\n')) { - if (__cmd_app(c)) { - putchar(c); } - } else { - pr_debug("Unrecognized character %02x (%c)\n", c, c); + continue; } - } while (cmd_cursor_index < CMD_LEN); - // Cleans all blanks at the beginning of the command. - trim(cmd); - __set_echo(true); + // Handle insertion based on insert mode. + if (insert_active) { + // Move cursor right. + puts("\033[1C"); + // Prepare to delete the character. + putchar('\b'); + } else if (index < length - 1) { + // Shift buffer to the right to insert new character. + memmove(entry->buffer + index + 1, entry->buffer + index, length - index + 1); + } + + // Append the character. + if (__command_append(entry, &index, &length, c)) { + break; // Exit loop if buffer is full. + } + + // In insert mode, the length stays the same, unless we are at the end of the line. + if (insert_active && (index < length)) { + --length; + } + + } while (length < entry->size); + + return length; // Return total length of input } -/// @brief Gets the options from the command. +/// @brief Allocates and parses the arguments (argv) from the provided command string. /// @param command The executed command. +/// @param argc Pointer to the argument count (to be set by this function). +/// @param argv Pointer to the argument list (to be set by this function). static void __alloc_argv(char *command, int *argc, char ***argv) { + // Input validation: Check if command, argc, or argv are NULL. + if (command == NULL || argc == NULL || argv == NULL) { + printf("Error: Invalid input. 'command', 'argc', or 'argv' is NULL.\n"); + return; + } + // Count the number of words (arguments) in the command. (*argc) = __count_words(command); - // Get the number of arguments, return if zero. + // If there are no arguments, return early. if ((*argc) == 0) { + *argv = NULL; + return; + } + // Allocate memory for the arguments array (argc + 1 for the NULL terminator). + (*argv) = (char **)malloc(sizeof(char *) * ((*argc) + 1)); + if (*argv == NULL) { + printf("Error: Failed to allocate memory for arguments.\n"); return; } - (*argv) = (char **)malloc(sizeof(char *) * ((*argc) + 1)); bool_t inword = false; - char *cit = command; - char *argStart = command; - size_t argcIt = 0; + char *cit = command; // Command iterator. + char *argStart = command; // Start of the current argument. + size_t argcIt = 0; // Iterator for arguments. + // Iterate through the command string. do { + // Check if the current character is not a separator (indicating part of a word). if (!__is_separator(*cit)) { if (!inword) { + // If we are entering a new word, mark the start of the argument. argStart = cit; inword = true; } continue; } - + // If we were inside a word and encountered a separator, process the word. if (inword) { inword = false; - // Expand possible environment variables in the current argument + // Expand possible environment variables in the current argument. char expand_env_buf[BUFSIZ]; - ___expand_env(argStart, expand_env_buf, BUFSIZ, cit - argStart, true); + ___expand_env(argStart, cit - argStart, expand_env_buf, BUFSIZ, true); + // Allocate memory for the expanded argument. (*argv)[argcIt] = (char *)malloc(strlen(expand_env_buf) + 1); + if ((*argv)[argcIt] == NULL) { + printf("Error: Failed to allocate memory for argument %zu.\n", argcIt); + // Free previously allocated arguments to prevent memory leaks. + for (size_t j = 0; j < argcIt; ++j) { + free((*argv)[j]); + } + free(*argv); + *argv = NULL; + return; + } + // Copy the expanded argument to the argv array. strcpy((*argv)[argcIt++], expand_env_buf); } } while (*cit++); + // Null-terminate the argv array. (*argv)[argcIt] = NULL; } +/// @brief Frees the memory allocated for argv and its arguments. +/// @param argc The number of arguments. +/// @param argv The array of argument strings to be freed. static inline void __free_argv(int argc, char **argv) { + // Input validation: Check if argv is NULL before proceeding. + if (argv == NULL) { + return; + } + // Free each argument in the argv array. for (int i = 0; i < argc; ++i) { - free(argv[i]); + if (argv[i] != NULL) { + free(argv[i]); + } } + // Free the argv array itself. free(argv); } +/// @brief Sets up file redirections based on arguments. +/// @param argcp Pointer to the argument count (to be updated if redirects are removed). +/// @param argvp Pointer to the argument list (to be updated if redirects are removed). static void __setup_redirects(int *argcp, char ***argvp) { char **argv = *argvp; @@ -782,17 +1185,22 @@ static void __setup_redirects(int *argcp, char ***argvp) int flags = O_CREAT | O_WRONLY; mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; - bool_t rd_stdout, rd_stderr; - rd_stdout = rd_stderr = 0; + bool_t rd_stdout = false; + bool_t rd_stderr = false; + // Iterate through arguments to find redirects. for (int i = 1; i < argc - 1; ++i) { + // Skip if the argument doesn't contain '>'. if (!strstr(argv[i], ">")) { continue; } - - path = argv[i + 1]; - - // Determine stream to redirect + // Check if the next argument (i + 1) is within bounds. + if (i + 1 >= argc || argv[i + 1] == NULL) { + printf("Error: Missing path for redirection after '%s'.\n", argv[i]); + exit(1); // Exit if no path is provided for redirection. + } + path = argv[i + 1]; // Set the path for redirection. + // Determine the stream to redirect based on the first character of the argument. switch (*argv[i]) { case '&': rd_stdout = rd_stderr = true; @@ -804,99 +1212,108 @@ static void __setup_redirects(int *argcp, char ***argvp) rd_stdout = true; break; default: - continue; + continue; // If no valid redirection is found, continue. } - - // Determine open flags + // Determine the open flags for append or truncate. if (strstr(argv[i], ">>")) { flags |= O_APPEND; } else { flags |= O_TRUNC; } - - // Remove redirects from argv + // Remove redirection arguments from argv. *argcp -= 2; free(argv[i]); - (*argvp)[i] = 0; + (*argvp)[i] = NULL; free(argv[i + 1]); - (*argvp)[i + 1] = 0; - + (*argvp)[i + 1] = NULL; + // Open the file for redirection. int fd = open(path, flags, mode); if (fd < 0) { - printf("%s: Failed to open file\n", path); + printf("Error: Failed to open file '%s' for redirection.\n", path); exit(1); } - + // Redirect stdout if necessary. if (rd_stdout) { close(STDOUT_FILENO); - dup(fd); + if (dup(fd) < 0) { + printf("Error: Failed to redirect stdout to file '%s'.\n", path); + close(fd); + exit(1); + } } - if (rd_stderr) { close(STDERR_FILENO); - dup(fd); + if (dup(fd) < 0) { + printf("Error: Failed to redirect stderr to file '%s'.\n", path); + close(fd); + exit(1); + } } - close(fd); - break; + close(fd); // Close the file descriptor after redirection. + break; // Stop after handling one redirection. } -} - -static int __execute_cmd(char *command, bool_t add_to_history) +} /// @brief Executes the command stored in the history entry. +/// @param entry The history entry containing the command. +/// @return Returns the exit status of the command. +static int __execute_command(rb_history_entry_t *entry) { int _status = 0; - // Retrieve the options from the command. - // The current number of arguments. - int _argc = 1; - // The vector of arguments. - char **_argv; - __alloc_argv(command, &_argc, &_argv); - // Check if the command is empty. + + // Retrieve the arguments from the command buffer. + int _argc = 1; // Initialize the argument count. + char **_argv; // Argument vector. + + __alloc_argv(entry->buffer, &_argc, &_argv); + + // Check if the command is empty (no arguments parsed). if (_argc == 0) { return 0; } - // Add the command to the history. - if (add_to_history) { - __hst_push(cmd); - } - + // Handle built-in commands. if (!strcmp(_argv[0], "init")) { + // Placeholder for the 'init' command. } else if (!strcmp(_argv[0], "cd")) { + // Execute the 'cd' command. __cd(_argc, _argv); } else if (!strcmp(_argv[0], "..")) { + // Shortcut for 'cd ..'. const char *__argv[] = { "cd", "..", NULL }; __cd(2, (char **)__argv); } else if (!strcmp(_argv[0], "export")) { + // Execute the 'export' command. __export(_argc, _argv); } else { + // Handle external commands (executed as child processes). bool_t blocking = true; + // Check if the command should be run in the background (indicated by '&'). if (strcmp(_argv[_argc - 1], "&") == 0) { - blocking = false; - _argc--; - free(_argv[_argc]); + blocking = false; // Non-blocking execution (background process). + _argc--; // Remove the '&' from the argument list. + free(_argv[_argc]); // Free the memory for the '&'. _argv[_argc] = NULL; } - + // Block SIGCHLD signal to prevent interference with child processes. __block_sigchld(); - - // Is a shell path, execute it! + // Fork the current process to create a child process. pid_t cpid = fork(); if (cpid == 0) { - // Makes the new process a group leader + // Child process: Execute the command. pid_t pid = getpid(); - setpgid(cpid, pid); - - __unblock_sigchld(); - + setpgid(cpid, pid); // Make the new process a group leader. + __unblock_sigchld(); // Unblock SIGCHLD signals in the child process. + // Handle redirections (e.g., stdout, stderr). __setup_redirects(&_argc, &_argv); - + // Attempt to execute the command using execvp. if (execvp(_argv[0], _argv) == -1) { printf("\nUnknown command: %s\n", _argv[0]); - exit(127); + exit(127); // Exit with status 127 if the command is not found. } } if (blocking) { + // Parent process: Wait for the child process to finish. waitpid(cpid, &_status, 0); + // Handle different exit statuses of the child process. if (WIFSIGNALED(_status)) { printf(FG_RED "\nExit status %d, killed by signal %d\n" FG_RESET, WEXITSTATUS(_status), WTERMSIG(_status)); @@ -907,28 +1324,31 @@ static int __execute_cmd(char *command, bool_t add_to_history) printf(FG_RED "\nExit status %d\n" FG_RESET, WEXITSTATUS(_status)); } } - __unblock_sigchld(); + __unblock_sigchld(); // Unblock SIGCHLD signals after command execution. } - // Free up the memory reserved for the arguments. + // Free the memory allocated for the argument list. __free_argv(_argc, _argv); + // Update the global status variable with the exit status of the command. status = WEXITSTATUS(_status); - return status; + return status; // Return the exit status of the command. } static int __execute_file(char *path) { + rb_history_entry_t entry; + rb_history_init_entry(&entry); int fd; if ((fd = open(path, O_RDONLY, 0)) == -1) { printf("%s: %s\n", path, strerror(errno)); return -errno; } - while (fgets(cmd, sizeof(cmd), fd)) { - if (cmd[0] == '#') { + while (fgets(entry.buffer, entry.size, fd)) { + if (entry.buffer[0] == '#') { continue; } - if ((status = __execute_cmd(cmd, false)) != 0) { - printf("\n%s: exited with %d\n", cmd, status); + if ((status = __execute_command(&entry)) != 0) { + printf("\n%s: exited with %d\n", entry.buffer, status); } } @@ -938,6 +1358,9 @@ static int __execute_file(char *path) static void __interactive_mode(void) { + rb_history_entry_t entry; + rb_history_init_entry(&entry); + stat_t buf; if (stat(".shellrc", &buf) == 0) { int ret = __execute_file(".shellrc"); @@ -950,9 +1373,35 @@ static void __interactive_mode(void) while (true) { // First print the prompt. __prompt_print(); + + // Get terminal attributes for input handling + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + _termios.c_lflag &= ~(ICANON | ECHO | ISIG); // Disable canonical mode and echo + tcsetattr(STDIN_FILENO, 0, &_termios); // Set modified attributes + // Get the input command. - __cmd_get(); - __execute_cmd(cmd, true); + if (__read_command(&entry) < 0) { + pr_crit("Error reading command...\n"); + // Restore terminal attributes + tcgetattr(STDIN_FILENO, &_termios); + _termios.c_lflag |= (ICANON | ECHO | ISIG); // Re-enable canonical mode and echo + tcsetattr(STDIN_FILENO, 0, &_termios); + continue; + } + + // Restore terminal attributes + tcgetattr(STDIN_FILENO, &_termios); + _termios.c_lflag |= (ICANON | ECHO | ISIG); // Re-enable canonical mode and echo + tcsetattr(STDIN_FILENO, 0, &_termios); + + // Add the command to the history. + if (strnlen(entry.buffer, entry.size) > 0) { + __history_push(&entry); + } + + // Execute the command. + __execute_command(&entry); } #pragma clang diagnostic pop } @@ -965,11 +1414,8 @@ void wait_for_child(int signum) int main(int argc, char *argv[]) { setsid(); - - struct termios _termios; - tcgetattr(STDIN_FILENO, &_termios); - _termios.c_lflag &= ~ISIG; - tcsetattr(STDIN_FILENO, 0, &_termios); + // Initialize the history. + rb_history_init(&history, rb_history_entry_copy); char *USER = getenv("USER"); if (USER == NULL) { diff --git a/programs/tests/t_msgget.c b/programs/tests/t_msgget.c index ef4a76f3..1318ce5e 100644 --- a/programs/tests/t_msgget.c +++ b/programs/tests/t_msgget.c @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) // ======================================================================== // Generating a key using ftok - key = ftok("/README", 5); + key = ftok("/README.md", 5); if (key < 0) { perror("Failed to generate key using ftok"); return EXIT_FAILURE; diff --git a/programs/tests/t_semget.c b/programs/tests/t_semget.c index 8e13dc73..292e255e 100644 --- a/programs/tests/t_semget.c +++ b/programs/tests/t_semget.c @@ -27,7 +27,7 @@ int main(int argc, char *argv[]) // ======================================================================== // Generate a unique key using ftok. - key = ftok("/README", 5); + key = ftok("/README.md", 5); if (key < 0) { perror("Failed to generate key using ftok"); return 1;