summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Kaivo <jkk@ung.org>2019-03-13 21:22:32 -0400
committerJakob Kaivo <jkk@ung.org>2019-03-13 21:22:32 -0400
commit949f13f09d2fe8df841ab05dff271f46f9a55bf9 (patch)
treec9bf65495bc14ddbf816038bdd99981d7bb8f670
migrate to gitlab
-rw-r--r--Makefile24
-rw-r--r--commands.c212
-rw-r--r--ed.c76
-rw-r--r--ed.h103
-rw-r--r--env.c85
-rw-r--r--input.c59
-rw-r--r--io.c163
-rw-r--r--re.c28
-rw-r--r--signals.c55
-rw-r--r--stubs.c35
10 files changed, 840 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2311579
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+.POSIX:
+
+.SUFFIXES: .cat .msg
+
+default: all
+
+CFLAGS=-g -Wall -Wextra -Wpedantic -Werror -Wno-unused-parameter
+UTILITY=ed
+SOURCES=ed.c commands.c stubs.c env.c io.c signals.c input.c re.c
+HEADERS=ed.h
+OBJECTS=ed.o commands.o stubs.o env.o io.o signals.o input.o re.o
+L10N=
+all: $(UTILITY) $(L10N)
+
+$(UTILITY): $(OBJECTS) $(HEADERS)
+
+.msg.cat:
+ gencat $@ $<
+
+.c.cat:
+ sed -ne '/^\/\*\*cat/,/cat\*\*\//p;' $< | grep -v ^/ | grep -v ^\* | gencat $@ -
+
+clean:
+ rm -f *.o $(L10N) $(UTILITY)
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..9970f7e
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,212 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+
+#define D "." /* [D]ot */
+#define S "1" /* [S]tart */
+#define E "$" /* [E]nd */
+#define P ".+1" /* [P]lus one */
+
+struct ed_command ed_commands[] = {
+ ['a'] = { 1, D, 0, ed_append, ARG_NONE },
+ ['c'] = { 2, D, D, ed_change, ARG_NONE },
+ ['d'] = { 2, D, D, ed_delete, ARG_NONE },
+ ['e'] = { 0, 0, 0, ed_edit, ARG_FILE },
+ ['E'] = { 0, 0, 0, ed_Edit, ARG_FILE },
+ ['f'] = { 0, 0, 0, ed_filename, ARG_FILE },
+ ['g'] = { 2, S, E, ed_global, ARG_RE },
+ ['G'] = { 2, S, E, ed_Global, ARG_RE },
+ ['h'] = { 0, 0, 0, ed_help, ARG_NONE },
+ ['H'] = { 0, 0, 0, ed_help_mode, ARG_NONE },
+ ['i'] = { 1, D, 0, ed_insert, ARG_NONE },
+ ['j'] = { 2, D, P, ed_join, ARG_NONE },
+ ['k'] = { 1, D, 0, ed_mark, ARG_MARK },
+ ['l'] = { 2, D, D, ed_list, ARG_NONE },
+ ['m'] = { 2, D, D, ed_move, ARG_ADDRESS },
+ ['n'] = { 2, D, D, ed_number, ARG_NONE },
+ ['p'] = { 2, D, D, ed_print, ARG_NONE },
+ ['P'] = { 0, 0, 0, ed_prompt, ARG_NONE },
+ ['q'] = { 0, 0, 0, ed_quit, ARG_NONE },
+ ['Q'] = { 0, 0, 0, ed_Quit, ARG_NONE },
+ ['r'] = { 1, E, 0, ed_read, ARG_FILE },
+ ['s'] = { 2, D, D, ed_substitute, ARG_RE },
+ ['t'] = { 2, D, D, ed_copy, ARG_ADDRESS },
+ ['u'] = { 0, 0, 0, ed_undo, ARG_NONE },
+ ['v'] = { 2, S, E, ed_global_non_matched, ARG_RE },
+ ['V'] = { 2, S, E, ed_Global_non_matched, ARG_RE },
+ ['w'] = { 2, S, E, ed_write, ARG_FILE },
+ ['='] = { 1, E, 0, ed_line_number, ARG_NONE },
+ ['!'] = { 0, 0, 0, ed_shell_escape, ARG_COMMAND },
+ ['\0'] = { 1, P, 0, ed_null, ARG_NONE },
+};
+static int ncommands = sizeof(ed_commands) / sizeof(ed_commands[0]);
+
+static intmax_t ed_range(char *cmdline, char **next)
+{
+ intmax_t line = 0;
+ int advance = 0;
+ if (cmdline == 0) {
+ if (next) {
+ *next = NULL;
+ }
+ return 0;
+ }
+
+ switch (cmdline[0]) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ line = strtoimax(cmdline, next, 10);
+ break;
+
+ case '.':
+ advance = 1;
+ line = ed_state.line;
+ break;
+
+ case '$':
+ advance = 1;
+ line = ed_state.nlines;
+ break;
+
+ case '\'':
+ if (!islower(cmdline[1])) {
+ ed_error("'%c' is not a valid mark name", cmdline[1]);
+ return -1;
+ }
+
+ if (ed_state.marks['a' - cmdline[1]] == 0) {
+ ed_error("Mark '%c' is not set", cmdline[1]);
+ return -1;
+ }
+
+ /* TODO: marked line is not currently present in the edit buffer */
+
+ advance = 2;
+ line = ed_state.marks['a' - cmdline[1]];
+ break;
+
+ case '/':
+ /* TODO: RE starting from line 1 */
+ advance = 1;
+ line = 1;
+ break;
+
+ case '?':
+ /* TODO: RE starting from end */
+ advance = 1;
+ line = ed_state.nlines;
+ break;
+
+ case '+':
+ case '-':
+ if (!isdigit(cmdline[1])) {
+ advance = 1;
+ line = 1;
+ break;
+ }
+ line = ed_state.line + strtoimax(cmdline, next, 10);
+ break;
+
+ default:
+ *next = cmdline;
+ break;
+ }
+
+ /* TODO: Check for + or - */
+
+ if (next) {
+ *next = cmdline + advance;
+ }
+ return line;
+}
+
+void ed_command(char *cmdline)
+{
+ char *next = NULL;
+ cmdline[strlen(cmdline)-1] = '\0';
+ while (isblank(*cmdline)) {
+ cmdline++;
+ }
+
+ intmax_t begin = ed_range(cmdline, &next);
+ if (begin == -1) {
+ return;
+ }
+
+ while (isblank(*next)) {
+ next++;
+ }
+
+ char sep = *next;
+
+ if (sep == ',' || sep == ';') {
+ next++;
+ }
+
+ while (isblank(*next)) {
+ next++;
+ }
+
+ intmax_t end = ed_range(next, &next);
+
+ if (begin == 0 && sep == ',' && end == 0) {
+ begin = 1;
+ end = ed_state.nlines;
+ } else if (begin == 0 && sep == ',') {
+ begin = 1;
+ } else if (sep == ',' && end == 0) {
+ end = begin;
+ } else if (begin == 0 && sep == ';' && end == 0) {
+ begin = ed_state.line;
+ end = ed_state.nlines;
+ } else if (begin == 0 && sep == ';') {
+ begin = ed_state.line;
+ } else if (sep == ';' && end == 0) {
+ end = begin;
+ }
+
+ while (isblank(*next)) {
+ next++;
+ }
+
+ int cmd = next[0];
+ if (cmd > ncommands || ed_commands[cmd].fn == 0) {
+ ed_error("%c: no such command", cmd);
+ return;
+ }
+
+ if (sep != ',' && sep != ';') {
+ if (begin == 0) {
+ begin = ed_range(ed_commands[cmd].default_range1, NULL);
+ }
+ if (end == 0) {
+ end = ed_range(ed_commands[cmd].default_range2, NULL);
+ }
+ }
+
+ /* TODO: validate parameters */
+ /* TODO: suffix l, n, or p */
+
+ ed_commands[cmd].fn(begin, end, next + 1);
+ ed_state.lastcmd = cmd;
+}
diff --git a/ed.c b/ed.c
new file mode 100644
index 0000000..4ce7bdd
--- /dev/null
+++ b/ed.c
@@ -0,0 +1,76 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int ed(const char *path)
+{
+ if (path) {
+ ed_edit(0, 0, path);
+ }
+
+ for (;;) {
+ char *line = NULL;
+ size_t n = 0;
+
+ if (ed_state.prompt_mode) {
+ printf("%s", ed_state.prompt);
+ fflush(stdout);
+ }
+ getline(&line, &n, stdin);
+ ed_command(line);
+ free(line);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ while ((c = getopt(argc, argv, "p:s")) != -1) {
+ switch (c) {
+ case 'p': /** set prompt to [prompt] **/
+ ed_state.prompt_mode = true;
+ ed_state.prompt = optarg;
+ break;
+
+ case 's': /** suppress byte counts **/
+ ed_state.suppress = true;
+ break;
+
+ default:
+ return 1;
+ }
+ }
+
+ setlocale(LC_ALL, "");
+ signal(SIGINT, ed_sigint);
+ signal(SIGHUP, ed_sighup);
+ signal(SIGQUIT, SIG_IGN);
+
+ return ed(argv[optind]);
+}
+
+struct ed_state ed_state;
diff --git a/ed.h b/ed.h
new file mode 100644
index 0000000..a9c2e00
--- /dev/null
+++ b/ed.h
@@ -0,0 +1,103 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#define _XOPEN_SOURCE 700
+#include <inttypes.h>
+#include <stdbool.h>
+
+struct ed_line {
+ struct ed_line *next;
+ struct ed_line *prev;
+ char *text;
+};
+
+struct ed_state {
+ bool suppress;
+ bool dirty;
+ char lastcmd;
+
+ bool prompt_mode;
+ char *prompt;
+
+ bool help_mode;
+ char *help;
+
+ char *filename;
+
+ intmax_t nlines;
+ intmax_t line;
+ intmax_t marks[26];
+ struct ed_line *head;
+ struct ed_line *tail;
+ struct ed_line *current;
+};
+extern struct ed_state ed_state;
+
+struct ed_command {
+ int range_params;
+ char *default_range1;
+ char *default_range2;
+ void (*fn)(intmax_t start, intmax_t end, const char *arg);
+ enum {
+ ARG_NONE = 0,
+ ARG_FILE,
+ ARG_RE,
+ ARG_ADDRESS,
+ ARG_COMMAND,
+ ARG_MARK,
+ } argtype;
+};
+extern struct ed_command ed_commands[];
+
+void ed_command(char *cmd);
+void ed_error(const char *fmt, ...);
+
+void ed_sigint(int sig);
+void ed_sighup(int sig);
+
+void ed_append(intmax_t start, intmax_t end, const char *arg);
+void ed_change(intmax_t start, intmax_t end, const char *arg);
+void ed_delete(intmax_t start, intmax_t end, const char *arg);
+void ed_edit(intmax_t start, intmax_t end, const char *arg);
+void ed_Edit(intmax_t start, intmax_t end, const char *arg);
+void ed_filename(intmax_t start, intmax_t end, const char *arg);
+void ed_global(intmax_t start, intmax_t end, const char *arg);
+void ed_Global(intmax_t start, intmax_t end, const char *arg);
+void ed_help(intmax_t start, intmax_t end, const char *arg);
+void ed_help_mode(intmax_t start, intmax_t end, const char *arg);
+void ed_insert(intmax_t start, intmax_t end, const char *arg);
+void ed_join(intmax_t start, intmax_t end, const char *arg);
+void ed_mark(intmax_t start, intmax_t end, const char *arg);
+void ed_list(intmax_t start, intmax_t end, const char *arg);
+void ed_move(intmax_t start, intmax_t end, const char *arg);
+void ed_number(intmax_t start, intmax_t end, const char *arg);
+void ed_print(intmax_t start, intmax_t end, const char *arg);
+void ed_prompt(intmax_t start, intmax_t end, const char *arg);
+void ed_quit(intmax_t start, intmax_t end, const char *arg);
+void ed_Quit(intmax_t start, intmax_t end, const char *arg);
+void ed_read(intmax_t start, intmax_t end, const char *arg);
+void ed_substitute(intmax_t start, intmax_t end, const char *arg);
+void ed_copy(intmax_t start, intmax_t end, const char *arg);
+void ed_undo(intmax_t start, intmax_t end, const char *arg);
+void ed_global_non_matched(intmax_t start, intmax_t end, const char *arg);
+void ed_Global_non_matched(intmax_t start, intmax_t end, const char *arg);
+void ed_write(intmax_t start, intmax_t end, const char *arg);
+void ed_line_number(intmax_t start, intmax_t end, const char *arg);
+void ed_shell_escape(intmax_t start, intmax_t end, const char *arg);
+void ed_null(intmax_t start, intmax_t end, const char *arg);
diff --git a/env.c b/env.c
new file mode 100644
index 0000000..a90b8ea
--- /dev/null
+++ b/env.c
@@ -0,0 +1,85 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void ed_error(const char *fmt, ...)
+{
+ if (ed_state.help) {
+ free(ed_state.help);
+ }
+
+ va_list ap, ap2;
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
+ size_t n = vsnprintf(NULL, 0, fmt, ap);
+
+ ed_state.help = calloc(n + 2, 1);
+ if (ed_state.help == NULL) {
+ fprintf(stderr, "ed: Out of memory\n");
+ exit(1);
+ }
+
+ vsnprintf(ed_state.help, n + 1, fmt, ap2);
+ va_end(ap);
+ va_end(ap2);
+
+ puts("?");
+
+ if (ed_state.help_mode) {
+ puts(ed_state.help);
+ }
+}
+
+void ed_filename(intmax_t start, intmax_t end, const char *arg)
+{
+ if (ed_state.filename) {
+ free(ed_state.filename);
+ }
+
+ ed_state.filename = strdup(arg);
+}
+
+void ed_help(intmax_t start, intmax_t end, const char *arg)
+{
+ if (ed_state.help) {
+ puts(ed_state.help);
+ }
+}
+
+void ed_help_mode(intmax_t start, intmax_t end, const char *arg)
+{
+ ed_state.help_mode = !ed_state.help_mode;
+ if (ed_state.help_mode) {
+ ed_help(start, end, arg);
+ }
+}
+
+void ed_prompt(intmax_t start, intmax_t end, const char *arg)
+{
+ if (ed_state.prompt == NULL) {
+ ed_state.prompt = "*";
+ }
+
+ ed_state.prompt_mode = !ed_state.prompt_mode;
+}
diff --git a/input.c b/input.c
new file mode 100644
index 0000000..99b27d4
--- /dev/null
+++ b/input.c
@@ -0,0 +1,59 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+
+static struct ed_line *ed_input(void)
+{
+ struct ed_line *head = NULL;
+ struct ed_line *tail = NULL;
+
+ for (;;) {
+ struct ed_line *line = calloc(1, sizeof(*line));
+ size_t n;
+
+ getline(&(line->text), &n, stdin);
+ if (!strcmp(line->text, ".\n")) {
+ free(line->text);
+ free(line);
+ break;
+ }
+
+ insque(line, tail);
+ if (head == NULL) {
+ head = line;
+ }
+ tail = line;
+ }
+
+ return head;
+}
+
+void ed_append(intmax_t start, intmax_t end, const char *arg) { }
+void ed_change(intmax_t start, intmax_t end, const char *arg) { }
+
+void ed_insert(intmax_t start, intmax_t end, const char *arg)
+{
+ struct ed_line *input = ed_input();
+ insque(input, ed_state.current);
+}
diff --git a/io.c b/io.c
new file mode 100644
index 0000000..8573a92
--- /dev/null
+++ b/io.c
@@ -0,0 +1,163 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+
+void ed_edit(intmax_t start, intmax_t end, const char *arg)
+{
+ if (ed_state.dirty && ed_state.lastcmd != 'e') {
+ ed_error("The current file has not been saved");
+ return;
+ }
+
+ ed_Edit(start, end, arg);
+}
+
+void ed_Edit(intmax_t start, intmax_t end, const char *arg)
+{
+ struct ed_line *cursor = ed_state.head;
+ while (cursor) {
+ cursor = cursor->next;
+ remque(cursor->prev);
+ }
+ ed_state.nlines = 0;
+ ed_state.dirty = false;
+
+ if (strlen(arg) > 0) {
+ if (ed_state.filename) {
+ free(ed_state.filename);
+ }
+ ed_state.filename = strdup(arg);
+ }
+
+ ed_state.current = NULL;
+ ed_state.tail = NULL;
+ ed_state.head = NULL;
+
+ ed_read(start, end, arg);
+
+ ed_state.current = ed_state.tail;
+ ed_state.line = ed_state.nlines;
+}
+
+void ed_print(intmax_t start, intmax_t end, const char *arg)
+{
+ fputs(ed_state.current->text, stdout);
+}
+
+void ed_quit(intmax_t start, intmax_t end, const char *arg)
+{
+ if (ed_state.dirty && ed_state.lastcmd != 'q') {
+ ed_error("The current file has not been saved");
+ return;
+ }
+ ed_Quit(start, end, arg);
+}
+
+void ed_Quit(intmax_t start, intmax_t end, const char *arg)
+{
+ exit(0);
+}
+
+void ed_read(intmax_t start, intmax_t end, const char *arg)
+{
+ intmax_t total = 0;
+ FILE *f = NULL;
+
+ char *file = ed_state.filename;
+ if (strlen(arg) > 0) {
+ file = (char*)arg;
+ }
+
+ if (file[0] == '!') {
+ f = popen(file + 1, "r");
+ } else {
+ f = fopen(file, "r");
+ }
+
+ if (f == NULL) {
+ ed_error("Couldn't open %s: %s", ed_state.filename, strerror(errno));
+ return;
+ }
+
+ while (!feof(f)) {
+ struct ed_line *line = calloc(1, sizeof(*line));
+ if (line == NULL) {
+ ed_error("Out of memory");
+ return;
+ }
+
+ size_t n = 0;
+ total += getline(&(line->text), &n, f);
+
+ ed_state.nlines++;
+ insque(line, ed_state.current);
+ ed_state.current = line;
+
+ if (line->prev == NULL) {
+ ed_state.head = line;
+ }
+ }
+
+ if (file[0] == '!') {
+ pclose(f);
+ } else {
+ fclose(f);
+ }
+
+ if (!ed_state.suppress) {
+ printf("%"PRIdMAX"\n", total);
+ }
+}
+
+void ed_write(intmax_t start, intmax_t end, const char *arg)
+{
+ intmax_t total = 0;
+ FILE *f = NULL;
+
+ char *file = ed_state.filename;
+ if (strlen(arg) > 0) {
+ file = (char*)arg;
+ }
+
+ if (file[0] == '!') {
+ f = popen(file + 1, "w");
+ } else {
+ f = fopen(file, "w");
+ }
+
+ if (f == NULL) {
+ ed_error("Couldn't open %s: %s", ed_state.filename, strerror(errno));
+ return;
+ }
+
+ for (struct ed_line *cursor = ed_state.head; cursor; cursor = cursor->next) {
+ fputs(cursor->text, f);
+ total += strlen(cursor->text);
+ }
+
+ if (!ed_state.suppress) {
+ printf("%"PRIdMAX"\n", total);
+ }
+}
diff --git a/re.c b/re.c
new file mode 100644
index 0000000..2ec335b
--- /dev/null
+++ b/re.c
@@ -0,0 +1,28 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+
+void ed_global(intmax_t start, intmax_t end, const char *arg) { }
+void ed_Global(intmax_t start, intmax_t end, const char *arg) { }
+
+void ed_substitute(intmax_t start, intmax_t end, const char *arg) { }
+
+void ed_global_non_matched(intmax_t start, intmax_t end, const char *arg) { }
+void ed_Global_non_matched(intmax_t start, intmax_t end, const char *arg) { }
diff --git a/signals.c b/signals.c
new file mode 100644
index 0000000..91d8a15
--- /dev/null
+++ b/signals.c
@@ -0,0 +1,55 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void ed_sigint(int sig)
+{
+}
+
+void ed_sighup(int sig)
+{
+ const char *filename = "ed.hup";
+
+ if (!ed_state.dirty) {
+ exit(sig);
+ }
+
+ if (access(filename, W_OK) == 0) {
+ ed_write(1, ed_state.nlines, filename);
+ exit(sig);
+ }
+
+ char *home = getenv("HOME");
+ if (home == NULL) {
+ exit(sig);
+ }
+
+ char path[strlen(home) + strlen(filename) + 2];
+ sprintf(path, "%s/%s", home, filename);
+ if (access(path, W_OK) == 0) {
+ ed_write(1, ed_state.nlines, path);
+ }
+
+ exit(sig);
+}
diff --git a/stubs.c b/stubs.c
new file mode 100644
index 0000000..bf6e626
--- /dev/null
+++ b/stubs.c
@@ -0,0 +1,35 @@
+/*
+ * UNG's Not GNU
+ *
+ * Copyright (c) 2011-2017, Jakob Kaivo <jkk@ung.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "ed.h"
+
+void ed_delete(intmax_t start, intmax_t end, const char *arg) { }
+
+void ed_join(intmax_t start, intmax_t end, const char *arg) { }
+void ed_mark(intmax_t start, intmax_t end, const char *arg) { }
+void ed_list(intmax_t start, intmax_t end, const char *arg) { }
+void ed_move(intmax_t start, intmax_t end, const char *arg) { }
+void ed_number(intmax_t start, intmax_t end, const char *arg) { }
+
+void ed_copy(intmax_t start, intmax_t end, const char *arg) { }
+void ed_undo(intmax_t start, intmax_t end, const char *arg) { }
+
+void ed_line_number(intmax_t start, intmax_t end, const char *arg) { }
+void ed_shell_escape(intmax_t start, intmax_t end, const char *arg) { }
+void ed_null(intmax_t start, intmax_t end, const char *arg) { }