From 42572f5c445497296ae70100d5e51370c8bba410 Mon Sep 17 00:00:00 2001
From: Leo Tenenbaum <pommicket@gmail.com>
Date: Fri, 19 Feb 2021 11:13:16 -0500
Subject: started tags

---
 config.c |  1 +
 main.c   |  3 +++
 tags.c   | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ted.cfg  |  2 ++
 ted.h    | 15 +++++++++++
 5 files changed, 107 insertions(+)
 create mode 100644 tags.c

diff --git a/config.c b/config.c
index 23fe5c9..fcdf7c0 100644
--- a/config.c
+++ b/config.c
@@ -202,6 +202,7 @@ void config_read(Ted *ted, char const *filename) {
 	};
 	OptionString const options_string[] = {
 		{"build-default-command", settings->build_default_command, sizeof settings->build_default_command},
+		{"tags-filename", settings->tags_filename, sizeof settings->tags_filename},
 	};
 
 	FILE *fp = fopen(filename, "rb");
diff --git a/main.c b/main.c
index bbcbeda..a1c3cc0 100644
--- a/main.c
+++ b/main.c
@@ -61,6 +61,7 @@ no_warn_end
 #include "syntax.c"
 #include "buffer.c"
 #include "ted.c"
+#include "tags.c"
 #include "ui.c"
 #include "find.c"
 #include "node.c"
@@ -389,6 +390,8 @@ int main(int argc, char **argv) {
 		ted->scroll_total_x = ted->scroll_total_y = 0;
 
 		//printf("%p\n",(void *)ted->drag_buffer);
+		
+		tags_read_if_changed(ted, &ted->tags);
 
 		ted_update_window_dimensions(ted);
 
diff --git a/tags.c b/tags.c
new file mode 100644
index 0000000..b1fac23
--- /dev/null
+++ b/tags.c
@@ -0,0 +1,86 @@
+void tags_free(TagsFile *f) {
+	free(f->file_data);
+	arr_free(f->tags);
+	memset(f, 0, sizeof *f);
+}
+
+void tags_read(Ted *ted, TagsFile *f) {
+	change_directory(ted->cwd);
+	Settings const *settings = &ted->settings;
+	char const *tags_filename = settings->tags_filename;
+	FILE *file = fopen(tags_filename, "rb");
+	tags_free(f);
+	if (file) {
+		fseek(file, 0, SEEK_END);
+		size_t file_size = (size_t)ftell(file);
+		fseek(file, 0, SEEK_SET);
+		char *file_data = ted_calloc(ted, file_size + 1, 1);
+		if (file_data) {
+			f->file_data = file_data;
+			fread(file_data, 1, file_size, file);
+			char *p = file_data;
+			while (1) {
+				// each line in the file is of the format:
+				// tag name\tfile name\taddress
+				// or
+				// tag name\tfile name\taddress;" additional information
+				
+				char *end = p + strcspn(p, "\n");
+				bool eof = *end == '\0';
+				char *name = p;
+				char *name_end = strchr(name, '\t');
+				if (name_end) {
+					*name_end = '\0';
+					char *filename = name_end + 1;
+					char *filename_end = strchr(filename, '\t');
+					if (filename_end) {
+						*filename_end = '\0';
+						char *address = filename_end + 1;
+						char *address_end = address;
+						int backslashes = 0;
+						while (1) {
+							bool is_end = false;
+							switch (*address_end) {
+							case '\n':
+							case '\r':
+								is_end = true;
+								break;
+							case '\\':
+								++backslashes;
+								break;
+							case '/':
+								if (address_end != address && backslashes % 2 == 0)
+									is_end = true;
+								break;
+							}
+							if (is_end) break;
+							if (*address_end != '\\') backslashes = 0;
+							++address_end;
+						}
+						*address_end = '\0';
+						if (address_end - address > 2 && address_end[-2] == ';' && address_end[-1] == '"') {
+							address_end[-2] = '\0';
+						}
+						Tag tag = {.name = name, .file = filename, .address = address};
+						arr_add(f->tags, tag);
+					}
+				}
+				if (eof) break; // end of file
+				p = end + 1;
+			}
+		}
+		f->last_modified = time_last_modified(tags_filename);
+		fclose(file);
+	} else {
+		ted_seterr(ted, "tags file does not exist.");
+	}
+}
+
+void tags_read_if_changed(Ted *ted, TagsFile *f) {
+	Settings const *settings = &ted->settings;
+	char const *tags_filename = settings->tags_filename;
+	struct timespec modify_time = time_last_modified(tags_filename);
+	if (!timespec_eq(modify_time, f->last_modified)) {
+		tags_read(ted, f);
+	}
+}
\ No newline at end of file
diff --git a/ted.cfg b/ted.cfg
index b65ba41..ed665e0 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -24,6 +24,8 @@ line-numbers = on
 # If set to "on", when a file is changed by another program, it will be reloaded by ted without asking you.
 auto-reload = off
 build-default-command = make
+# file name for ctags output
+tags-filename = tags
 
 [keyboard]
 # motion and selection
diff --git a/ted.h b/ted.h
index e5bc126..4676fb7 100644
--- a/ted.h
+++ b/ted.h
@@ -79,6 +79,7 @@ typedef struct {
 	u8 padding;
 	u8 scrolloff;
 	char build_default_command[256];
+	char tags_filename[128];
 	// [i] = comma-separated string of file extensions for language i, or NULL for none
 	char *language_extensions[LANG_COUNT];
 } Settings;
@@ -159,6 +160,18 @@ typedef struct {
 	BufferEdit *redo_history; // dynamic array of redo history
 } TextBuffer;
 
+typedef struct {
+	char const *name;
+	char const *file;
+	char const *address;
+} Tag;
+
+typedef struct {
+	Tag *tags; // dynamic array of tags
+	char *file_data;
+	struct timespec last_modified; // time when tags file was last modified
+} TagsFile;
+
 ENUM_U16 {
 	MENU_NONE,
 	MENU_OPEN,
@@ -254,6 +267,8 @@ typedef struct Ted {
 	bool build_shown; // are we showing the build output?
 	bool building; // is the build process running?
 	
+	TagsFile tags;
+	
 	BuildError *build_errors; // dynamic array of build errors
 	u32 build_error; // build error we are currently "on"
 
-- 
cgit v1.2.3