Add tag listing functionality
[blerg.git] / tags.c
diff --git a/tags.c b/tags.c
index 96e7378..4705b87 100644 (file)
--- a/tags.c
+++ b/tags.c
@@ -6,6 +6,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/file.h>
+#include <sys/mman.h>
 #include "tags.h"
 #include "config.h"
 
@@ -21,18 +22,19 @@ int tag_scan(const char *author, const char *data, int len, uint64_t record) {
 
        for (i = 0; i < len; i++) {
 tag_scan_start:
-               if (data[i] == '#') {
+               if (data[i] == '#' || data[i] == '@') {
                        if (n_tags == MAX_TAGS) {
                                fprintf(stderr, "Too many tags in message\n");
                                break;
                        }
+                       int begin = i;
                        int start = ++i;
                        while (i < len && TAG_CHAR(data[i]) && (i - start < MAX_TAG_LENGTH)) {
                                i++;
                        }
                        if (start - i == 0) continue;
-                       char *tag = calloc(1, MAX_TAG_LENGTH + 1);
-                       memcpy(tag, &data[start], i - start);
+                       char *tag = calloc(1, MAX_TAG_LENGTH + 2);
+                       memcpy(tag, &data[begin], i - begin);
                        for (j = 0; j < n_tags; j++) {
                                if (!strncmp(tag, taglist[j], MAX_TAG_LENGTH)) {
                                        // We already have this tag.  Start over.
@@ -61,7 +63,18 @@ int tag_add(const char *author, const char *tag, uint64_t record) {
        strncpy(t.author, author, 32);
        t.record = record;
 
-       snprintf(filename, 512, "%s/%s", TAGS_PATH, tag);
+       switch(tag[0]) {
+       case '#':
+               snprintf(filename, 512, "%s/%s", HASH_TAGS_PATH, tag + 1);
+               break;
+       case '@':
+               snprintf(filename, 512, "%s/%s", AUTHOR_TAGS_PATH, tag + 1);
+               break;
+       default:
+               fprintf(stderr, "Invalid tag type: %s\n", tag);
+               return 0;
+       }
+
        int tag_fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
        flock(tag_fd, LOCK_EX);
        if (tag_fd == -1) {
@@ -79,5 +92,72 @@ int tag_add(const char *author, const char *tag, uint64_t record) {
        return 1;
 }
 
-int tag_list(const char *tag, int start, int count) {
+struct tag * tag_list(const char *tag, uint64_t offset, uint64_t *count, int direction) {
+       char filename[512];
+       struct stat st;
+       struct tag *taglist;
+       struct tag *retlist;
+       uint64_t n_tag_records;
+       
+       switch(tag[0]) {
+       case '#':
+               snprintf(filename, 512, "%s/%s", HASH_TAGS_PATH, tag + 1);
+               break;
+       case '@':
+               snprintf(filename, 512, "%s/%s", AUTHOR_TAGS_PATH, tag + 1);
+               break;
+       default:
+               fprintf(stderr, "Invalid tag type: %s\n", tag);
+               return 0;
+       }
+
+       int tag_fd = open(filename, O_RDONLY, 0600);
+       if (tag_fd == -1) {
+               perror("Could not open tag file");
+               *count = 0;
+               return NULL;
+       }
+
+       fstat(tag_fd, &st);
+       if (st.st_size == 0) {
+               close(tag_fd);
+               *count = 0;
+               return NULL;
+       }
+       n_tag_records = st.st_size / sizeof(struct tag);
+       if (*count > n_tag_records)
+               *count = n_tag_records;
+       if (offset > n_tag_records) {
+               fprintf(stderr, "Cannot access tag record beyond end\n");
+               return NULL;
+       }
+
+       taglist = (struct tag *) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, tag_fd, 0);
+       if (taglist == MAP_FAILED) {
+               perror("Could not mmap tag file");
+               goto tag_list_map_failed;
+       }
+       retlist = (struct tag *) malloc(sizeof(struct tag) * *count);
+       if (retlist == NULL) {
+               perror("Could not allocate memory for tag list");
+               goto tag_list_malloc_failed;
+       }
+       switch(direction) {
+       case 1:
+               memcpy(retlist, taglist + offset, sizeof(struct tag) * *count);
+               break;
+       case -1:
+               memcpy(retlist, taglist + (n_tag_records - *count - offset), sizeof(struct tag) * *count);
+               break;
+       }
+
+       munmap(taglist, st.st_size);
+       return retlist;
+
+tag_list_malloc_failed:
+       munmap(taglist, st.st_size);
+tag_list_map_failed:
+       close(tag_fd);
+tag_list_open_failed:
+       return NULL;
 }