summaryrefslogtreecommitdiff
path: root/examples/dte/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/dte/main.c')
-rw-r--r--examples/dte/main.c575
1 files changed, 0 insertions, 575 deletions
diff --git a/examples/dte/main.c b/examples/dte/main.c
deleted file mode 100644
index 11025af..0000000
--- a/examples/dte/main.c
+++ /dev/null
@@ -1,575 +0,0 @@
-#include <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#include <unistd.h>
-#include "block.h"
-#include "commands.h"
-#include "compiler.h"
-#include "config.h"
-#include "editor.h"
-#include "error.h"
-#include "file-history.h"
-#include "frame.h"
-#include "history.h"
-#include "load-save.h"
-#include "move.h"
-#include "screen.h"
-#include "search.h"
-#include "signals.h"
-#include "syntax/state.h"
-#include "syntax/syntax.h"
-#include "tag.h"
-#include "terminal/input.h"
-#include "terminal/key.h"
-#include "terminal/mode.h"
-#include "terminal/output.h"
-#include "terminal/terminal.h"
-#include "util/debug.h"
-#include "util/exitcode.h"
-#include "util/fd.h"
-#include "util/log.h"
-#include "util/macros.h"
-#include "util/path.h"
-#include "util/ptr-array.h"
-#include "util/strtonum.h"
-#include "util/xmalloc.h"
-#include "util/xreadwrite.h"
-#include "util/xsnprintf.h"
-#include "view.h"
-#include "window.h"
-#include "../build/version.h"
-
-static void term_cleanup(EditorState *e)
-{
- set_fatal_error_cleanup_handler(NULL, NULL);
- if (!e->child_controls_terminal) {
- ui_end(e);
- }
-}
-
-static void cleanup_handler(void *userdata)
-{
- term_cleanup(userdata);
-}
-
-static ExitCode write_stdout(const char *str, size_t len)
-{
- if (xwrite_all(STDOUT_FILENO, str, len) < 0) {
- perror("write");
- return EX_IOERR;
- }
- return EX_OK;
-}
-
-static ExitCode list_builtin_configs(void)
-{
- String str = dump_builtin_configs();
- BUG_ON(!str.buffer);
- ExitCode e = write_stdout(str.buffer, str.len);
- string_free(&str);
- return e;
-}
-
-static ExitCode dump_builtin_config(const char *name)
-{
- const BuiltinConfig *cfg = get_builtin_config(name);
- if (!cfg) {
- fprintf(stderr, "Error: no built-in config with name '%s'\n", name);
- return EX_USAGE;
- }
- return write_stdout(cfg->text.data, cfg->text.length);
-}
-
-static ExitCode lint_syntax(const char *filename)
-{
- EditorState *e = init_editor_state();
- int err;
- BUG_ON(e->status != EDITOR_INITIALIZING);
- const Syntax *s = load_syntax_file(e, filename, CFG_MUST_EXIST, &err);
- if (s) {
- const size_t n = s->states.count;
- const char *plural = (n == 1) ? "" : "s";
- printf("OK: loaded syntax '%s' with %zu state%s\n", s->name, n, plural);
- } else if (err == EINVAL) {
- error_msg("%s: no default syntax found", filename);
- }
- free_editor_state(e);
- return get_nr_errors() ? EX_DATAERR : EX_OK;
-}
-
-static ExitCode showkey_loop(const char *term_name, const char *colorterm)
-{
- if (unlikely(!term_raw())) {
- perror("tcsetattr");
- return EX_IOERR;
- }
-
- Terminal term;
- TermOutputBuffer *obuf = &term.obuf;
- TermInputBuffer *ibuf = &term.ibuf;
- term_init(&term, term_name, colorterm);
- term_input_init(ibuf);
- term_output_init(obuf);
- term_enable_private_modes(&term);
- term_add_literal(obuf, "Press any key combination, or use Ctrl+D to exit\r\n");
- term_output_flush(obuf);
-
- char keystr[KEYCODE_STR_MAX];
- for (bool loop = true; loop; ) {
- KeyCode key = term_read_key(&term, 100);
- switch (key) {
- case KEY_NONE:
- case KEY_IGNORE:
- continue;
- case KEY_BRACKETED_PASTE:
- case KEY_DETECTED_PASTE:
- term_discard_paste(ibuf, key == KEY_BRACKETED_PASTE);
- continue;
- case MOD_CTRL | 'd':
- loop = false;
- }
- size_t keylen = keycode_to_string(key, keystr);
- term_add_literal(obuf, " ");
- term_add_bytes(obuf, keystr, keylen);
- term_add_literal(obuf, "\r\n");
- term_output_flush(obuf);
- }
-
- term_restore_private_modes(&term);
- term_output_flush(obuf);
- term_cooked();
- term_input_free(ibuf);
- term_output_free(obuf);
- return EX_OK;
-}
-
-static ExitCode init_std_fds(int std_fds[2])
-{
- FILE *streams[3] = {stdin, stdout, stderr};
- for (int i = 0; i < ARRAYLEN(streams); i++) {
- if (is_controlling_tty(i)) {
- continue;
- }
-
- if (i < STDERR_FILENO) {
- // Try to create a duplicate fd for redirected stdin/stdout; to
- // allow reading/writing after freopen(3) closes the original
- int fd = fcntl(i, F_DUPFD_CLOEXEC, 3);
- if (fd == -1 && errno != EBADF) {
- perror("fcntl");
- return EX_OSERR;
- }
- std_fds[i] = fd;
- }
-
- // Ensure standard streams are connected to the terminal during
- // editor operation, regardless of how they were redirected
- if (unlikely(!freopen("/dev/tty", i ? "w" : "r", streams[i]))) {
- const char *err = strerror(errno);
- fprintf(stderr, "Failed to open tty for fd %d: %s\n", i, err);
- return EX_IOERR;
- }
-
- int new_fd = fileno(streams[i]);
- if (unlikely(new_fd != i)) {
- // This should never happen in a single-threaded program.
- // freopen() should call fclose() followed by open() and
- // POSIX requires a successful call to open() to return the
- // lowest available file descriptor.
- fprintf(stderr, "freopen() changed fd from %d to %d\n", i, new_fd);
- return EX_OSERR;
- }
-
- if (unlikely(!is_controlling_tty(new_fd))) {
- perror("tcgetpgrp");
- return EX_OSERR;
- }
- }
-
- return EX_OK;
-}
-
-static Buffer *init_std_buffer(EditorState *e, int fds[2])
-{
- const char *name = NULL;
- Buffer *buffer = NULL;
-
- if (fds[STDIN_FILENO] >= 3) {
- Encoding enc = encoding_from_type(UTF8);
- buffer = buffer_new(&e->buffers, &e->options, &enc);
- if (read_blocks(buffer, fds[STDIN_FILENO], false)) {
- name = "(stdin)";
- buffer->temporary = true;
- } else {
- error_msg("Unable to read redirected stdin");
- remove_and_free_buffer(&e->buffers, buffer);
- buffer = NULL;
- }
- }
-
- if (fds[STDOUT_FILENO] >= 3) {
- if (!buffer) {
- buffer = open_empty_buffer(&e->buffers, &e->options);
- name = "(stdout)";
- } else {
- name = "(stdin|stdout)";
- }
- buffer->stdout_buffer = true;
- buffer->temporary = true;
- }
-
- BUG_ON(!buffer != !name);
- if (name) {
- set_display_filename(buffer, xstrdup(name));
- }
-
- return buffer;
-}
-
-static ExitCode init_logging(const char *filename, const char *req_level_str)
-{
- if (!filename || filename[0] == '\0') {
- return EX_OK;
- }
-
- LogLevel req_level = log_level_from_str(req_level_str);
- if (req_level == LOG_LEVEL_NONE) {
- return EX_OK;
- }
- if (req_level == LOG_LEVEL_INVALID) {
- fprintf(stderr, "Invalid $DTE_LOG_LEVEL value: '%s'\n", req_level_str);
- return EX_USAGE;
- }
-
- // https://no-color.org/
- const char *no_color = xgetenv("NO_COLOR");
-
- LogLevel got_level = log_open(filename, req_level, !no_color);
- if (got_level == LOG_LEVEL_NONE) {
- const char *err = strerror(errno);
- fprintf(stderr, "Failed to open $DTE_LOG (%s): %s\n", filename, err);
- return EX_IOERR;
- }
-
- const char *got_level_str = log_level_to_str(got_level);
- if (got_level != req_level) {
- const char *r = req_level_str;
- const char *g = got_level_str;
- LOG_WARNING("log level '%s' unavailable; falling back to '%s'", r, g);
- }
-
- LOG_INFO("logging to '%s' (level: %s)", filename, got_level_str);
-
- if (no_color) {
- LOG_INFO("log colors disabled ($NO_COLOR)");
- }
-
- struct utsname u;
- if (likely(uname(&u) >= 0)) {
- LOG_INFO("system: %s/%s %s", u.sysname, u.machine, u.release);
- } else {
- LOG_ERRNO("uname");
- }
- return EX_OK;
-}
-
-static void log_config_counts(const EditorState *e)
-{
- if (!log_level_enabled(LOG_LEVEL_INFO)) {
- return;
- }
-
- size_t nbinds = 0;
- for (size_t i = 0; i < ARRAYLEN(e->modes); i++) {
- nbinds += e->modes[i].key_bindings.count;
- }
-
- size_t nerrorfmts = 0;
- for (HashMapIter it = hashmap_iter(&e->compilers); hashmap_next(&it); ) {
- const Compiler *compiler = it.entry->value;
- nerrorfmts += compiler->error_formats.count;
- }
-
- LOG_INFO (
- "binds=%zu aliases=%zu hi=%zu ft=%zu option=%zu errorfmt=%zu(%zu)",
- nbinds,
- e->aliases.count,
- e->colors.other.count + NR_BC,
- e->filetypes.count,
- e->file_options.count,
- e->compilers.count,
- nerrorfmts
- );
-}
-
-static const char copyright[] =
- "dte " VERSION "\n"
- "(C) 2013-2023 Craig Barnes\n"
- "(C) 2010-2015 Timo Hirvonen\n"
- "This program is free software; you can redistribute and/or modify\n"
- "it under the terms of the GNU General Public License version 2\n"
- "<https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>.\n"
- "There is NO WARRANTY, to the extent permitted by law.\n";
-
-static const char usage[] =
- "Usage: %s [OPTIONS] [[+LINE] FILE]...\n\n"
- "Options:\n"
- " -c COMMAND Run COMMAND after editor starts\n"
- " -t CTAG Jump to source location of CTAG\n"
- " -r RCFILE Read user config from RCFILE instead of ~/.dte/rc\n"
- " -s FILE Validate dte-syntax commands in FILE and exit\n"
- " -b NAME Print built-in config matching NAME and exit\n"
- " -B Print list of built-in config names and exit\n"
- " -H Don't load or save history files\n"
- " -R Don't read user config file\n"
- " -K Start editor in \"showkey\" mode\n"
- " -h Display help summary and exit\n"
- " -V Display version number and exit\n"
- "\n";
-
-int main(int argc, char *argv[])
-{
- static const char optstring[] = "hBHKRVb:c:t:r:s:";
- const char *tag = NULL;
- const char *rc = NULL;
- const char *commands[8];
- size_t nr_commands = 0;
- bool read_rc = true;
- bool use_showkey = false;
- bool load_and_save_history = true;
- set_print_errors_to_stderr(true);
-
- for (int ch; (ch = getopt(argc, argv, optstring)) != -1; ) {
- switch (ch) {
- case 'c':
- if (unlikely(nr_commands >= ARRAYLEN(commands))) {
- fputs("Error: too many -c options used\n", stderr);
- return EX_USAGE;
- }
- commands[nr_commands++] = optarg;
- break;
- case 't':
- tag = optarg;
- break;
- case 'r':
- rc = optarg;
- break;
- case 's':
- return lint_syntax(optarg);
- case 'R':
- read_rc = false;
- break;
- case 'b':
- return dump_builtin_config(optarg);
- case 'B':
- return list_builtin_configs();
- case 'H':
- load_and_save_history = false;
- break;
- case 'K':
- use_showkey = true;
- goto loop_break;
- case 'V':
- return write_stdout(copyright, sizeof(copyright));
- case 'h':
- printf(usage, (argv[0] && argv[0][0]) ? argv[0] : "dte");
- return EX_OK;
- default:
- return EX_USAGE;
- }
- }
-
-loop_break:;
-
- const char *term_name = xgetenv("TERM");
- if (!term_name) {
- fputs("Error: $TERM not set\n", stderr);
- // This is considered a "usage" error, because the program
- // must be started from a properly configured terminal
- return EX_USAGE;
- }
-
- // This must be done before calling init_logging(), otherwise an
- // invocation like e.g. `DTE_LOG=/dev/pts/2 dte 0<&-` could
- // cause the logging fd to be opened as STDIN_FILENO
- int std_fds[2] = {-1, -1};
- ExitCode r = init_std_fds(std_fds);
- if (unlikely(r != EX_OK)) {
- return r;
- }
-
- r = init_logging(getenv("DTE_LOG"), getenv("DTE_LOG_LEVEL"));
- if (unlikely(r != EX_OK)) {
- return r;
- }
-
- if (!term_mode_init()) {
- perror("tcgetattr");
- return EX_IOERR;
- }
-
- const char *colorterm = getenv("COLORTERM");
- if (use_showkey) {
- return showkey_loop(term_name, colorterm);
- }
-
- EditorState *e = init_editor_state();
- Terminal *term = &e->terminal;
- term_init(term, term_name, colorterm);
-
- Buffer *std_buffer = init_std_buffer(e, std_fds);
- bool have_stdout_buffer = std_buffer && std_buffer->stdout_buffer;
-
- // Create this early (needed if "lock-files" is true)
- const char *cfgdir = e->user_config_dir;
- BUG_ON(!cfgdir);
- if (mkdir(cfgdir, 0755) != 0 && errno != EEXIST) {
- error_msg("Error creating %s: %s", cfgdir, strerror(errno));
- load_and_save_history = false;
- e->options.lock_files = false;
- }
-
- term_save_title(term);
- exec_builtin_rc(e);
-
- if (read_rc) {
- ConfigFlags flags = CFG_NOFLAGS;
- char buf[4096];
- if (rc) {
- flags |= CFG_MUST_EXIST;
- } else {
- xsnprintf(buf, sizeof buf, "%s/%s", cfgdir, "rc");
- rc = buf;
- }
- LOG_INFO("loading configuration from %s", rc);
- read_normal_config(e, rc, flags);
- }
-
- log_config_counts(e);
- update_all_syntax_colors(&e->syntaxes, &e->colors);
-
- Window *window = new_window(e);
- e->window = window;
- e->root_frame = new_root_frame(window);
-
- set_signal_handlers();
- set_fatal_error_cleanup_handler(cleanup_handler, e);
-
- if (load_and_save_history) {
- file_history_load(&e->file_history, path_join(cfgdir, "file-history"));
- history_load(&e->command_history, path_join(cfgdir, "command-history"));
- history_load(&e->search_history, path_join(cfgdir, "search-history"));
- if (e->search_history.last) {
- search_set_regexp(&e->search, e->search_history.last->text);
- }
- }
-
- set_print_errors_to_stderr(false);
-
- // Initialize terminal but don't update screen yet. Also display
- // "Press any key to continue" prompt if there were any errors
- // during reading configuration files.
- if (!term_raw()) {
- perror("tcsetattr");
- return EX_IOERR;
- }
- if (get_nr_errors()) {
- any_key(term, e->options.esc_timeout);
- clear_error();
- }
-
- e->status = EDITOR_RUNNING;
-
- for (size_t i = optind, line = 0, col = 0; i < argc; i++) {
- const char *str = argv[i];
- if (line == 0 && *str == '+' && str_to_filepos(str + 1, &line, &col)) {
- continue;
- }
- View *view = window_open_buffer(window, str, false, NULL);
- if (line == 0) {
- continue;
- }
- set_view(view);
- move_to_filepos(view, line, col);
- line = 0;
- }
-
- if (std_buffer) {
- window_add_buffer(window, std_buffer);
- }
-
- View *dview = NULL;
- if (window->views.count == 0) {
- // Open a default buffer, if none were opened for arguments
- dview = window_open_empty_buffer(window);
- BUG_ON(!dview);
- BUG_ON(window->views.count != 1);
- BUG_ON(dview != window->views.ptrs[0]);
- }
-
- set_view(window->views.ptrs[0]);
- ui_start(e);
-
- for (size_t i = 0; i < nr_commands; i++) {
- handle_normal_command(e, commands[i], false);
- }
-
- if (tag) {
- StringView tag_sv = strview_from_cstring(tag);
- if (tag_lookup(&e->tagfile, &tag_sv, NULL, &e->messages)) {
- activate_current_message(e);
- if (dview && nr_commands == 0 && window->views.count > 1) {
- // Close default/empty buffer, if `-t` jumped to a tag
- // and no commands were executed via `-c`
- remove_view(dview);
- dview = NULL;
- }
- }
- }
-
- if (nr_commands > 0 || tag) {
- normal_update(e);
- }
-
- int exit_code = main_loop(e);
-
- term_restore_title(term);
- ui_end(e);
- term_output_flush(&term->obuf);
- set_print_errors_to_stderr(true);
-
- // Unlock files and add to file history
- remove_frame(e, e->root_frame);
-
- if (load_and_save_history) {
- history_save(&e->command_history);
- history_save(&e->search_history);
- file_history_save(&e->file_history);
- }
-
- if (have_stdout_buffer) {
- int fd = std_fds[STDOUT_FILENO];
- Block *blk;
- block_for_each(blk, &std_buffer->blocks) {
- if (xwrite_all(fd, blk->data, blk->size) < 0) {
- error_msg_errno("failed to write (stdout) buffer");
- if (exit_code == EDITOR_EXIT_OK) {
- exit_code = EX_IOERR;
- }
- break;
- }
- }
- free_blocks(std_buffer);
- free(std_buffer);
- }
-
- free_editor_state(e);
- LOG_INFO("exiting with status %d", exit_code);
- log_close();
- return exit_code;
-}