Files
ke/main.c
2025-11-29 14:56:51 -08:00

239 lines
4.2 KiB
C

/*
* ke - kyle's editor
*
* ke started off following along with the kilo walkthrough at
* https://viewsourcecode.org/snaptoken/kilo/
*
* It is inspired heavily by mg(1) and VDE. This is a single file and
* can be built with
* $(CC) -D_DEFAULT_SOURCE -D_XOPEN_SOURCE -Wall -Wextra -pedantic \
* -Wshadow -Werror -std=c99 -g -o ke main.c
*
* It builds and runs on Linux and Darwin. I can't confirm BSD compatibility.
*
* commit 59d3fa1dab68e8683d5f5a9341f5f42ef3308876
* Author: Kyle Isom <kyle@imap.cc>
* Date: Fri Feb 7 20:46:43 2020 -0800
*
* Initial import, starting with kyle's editor.
*/
#include <sys/ioctl.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <wchar.h>
#include <sys/stat.h>
#include "abuf.h"
#include "buffer.h"
#include "editor.h"
#include "editing.h"
#include "process.h"
#include "term.h"
void loop(void);
void enable_debugging(void);
void deathknell(void);
static void signal_handler(int sig);
static void install_signal_handlers(void);
int
kbhit(void)
{
int bytes_waiting = 0;
ioctl(STDIN_FILENO, FIONREAD, &bytes_waiting);
if (bytes_waiting < 0) {
editor_set_status("kbhit: FIONREAD failed: %s", strerror(errno));
/* if FIONREAD fails, we need to assume we should read. this
* will default to a much slower input sequence, but it'll work.
*/
return 1;
}
return bytes_waiting > 0;
}
void
loop(void)
{
int up = 1; /* update on the first runthrough */
while (1) {
if (up) {
display_refresh();
}
/*
* ke should only refresh the display if it has received keyboard
* input; if it has, drain all the inputs. This is useful for
* handling pastes without massive screen flicker.
*
*/
up = process_keypress();
if (up != 0) {
while (kbhit()) {
process_keypress();
}
}
}
}
void
enable_debugging(void)
{
dump_pidfile();
}
void
deathknell(void)
{
fflush(stderr);
if (editor.killring != NULL) {
ab_free(editor.killring);
free(editor.killring);
editor.killring = NULL;
}
reset_editor();
disable_termraw();
}
static void
signal_handler(int sig)
{
signal(sig, SIG_DFL);
fprintf(stderr, "caught signal %d\n", sig);
deathknell();
raise(sig);
_exit(127 + sig);
}
static void
install_signal_handlers(void)
{
/* Block all these signals while inside any of them */
const int fatal_signals[] = {
SIGABRT, SIGFPE, SIGILL, SIGSEGV,
#ifdef SIGBUS
SIGBUS,
#endif
#ifdef SIGQUIT
SIGQUIT,
#endif
#ifdef SIGSYS
SIGSYS,
#endif
-1 /* sentinel */
};
int i = 0;
for (i = 0; fatal_signals[i] != -1; i++) {
signal(fatal_signals[i], signal_handler);
}
atexit(deathknell);
}
int
main(int argc, char *argv[])
{
const char *arg = NULL;
const char *path = NULL;
int i = 0;
int v = 0;
int nb = 0;
int opt = 0;
int debug = 0;
int pending_line = 0; /* line number for the next filename */
int first_loaded = 0; /* has a filed been loaded already? */
install_signal_handlers();
while ((opt = getopt(argc, argv, "df:")) != -1) {
if (opt == 'd') {
debug = 1;
} else {
fprintf(stderr, "Usage: ke [-d] [-f logfile] [ +N ] [file ...]\n");
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
setlocale(LC_ALL, "");
if (debug) {
enable_debugging();
}
setup_terminal();
init_editor();
/* start processing file names. if an arg starts with a '+',
* interpret it as the line to jump to.
*/
for (i = 0; i < argc; i++) {
arg = argv[i];
if (arg[0] == '+') {
path = arg + 1;
v = 0;
if (*path != '\0') {
v = atoi(path);
if (v < 1) v = 0;
}
pending_line = v;
continue;
}
if (!first_loaded) {
open_file(arg);
if (pending_line > 0) {
jump_to_position(0, pending_line - 1);
pending_line = 0;
}
first_loaded = 1;
} else {
nb = buffer_add_empty();
buffer_switch(nb);
open_file(arg);
if (pending_line > 0) {
jump_to_position(0, pending_line - 1);
pending_line = 0;
}
}
}
editor_set_status("C-k q to exit / C-k d to dump core");
display_clear(NULL);
loop();
return 0;
}