Add some subscription (a.k.a. "follow") functionality
[blerg.git] / common / stringbucket.c
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <sys/mman.h>
4 #include <sys/file.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stringbucket.h>
10
11 #define STRINGBUCKET_STRINGSIZE 64
12
13 const char * strnchr(const char *s, int c, int n) {
14         const char *ptr = s;
15         while (n > 0) {
16                 if (*ptr == c)
17                         return ptr;
18                 ptr++;
19                 n--;
20         }
21         return NULL;
22 }
23
24 struct stringbucket * stringbucket_open(const char *filename) {
25         struct stat st;
26         struct stringbucket *obj = malloc(sizeof(struct stringbucket));
27
28         if (obj == NULL) {
29                 perror("stringbucket allocate");
30                 return NULL;
31         }
32
33         obj->fd = open(filename, O_RDWR | O_APPEND | O_CREAT, 0600);
34         flock(obj->fd, LOCK_SH);
35         fstat(obj->fd, &st);
36         flock(obj->fd, LOCK_UN);
37         obj->size = st.st_size;
38         obj->list = mmap(NULL, obj->size, PROT_READ | PROT_WRITE, MAP_SHARED, obj->fd, 0);
39         if (obj->list == NULL) {
40                 perror("stringbucket mmap");
41                 close(obj->fd);
42                 free(obj);
43                 return 0;
44         }
45
46         return obj;
47 }
48
49 void stringbucket_close(struct stringbucket *sb) {
50         munmap(sb->list, sb->size);
51         close(sb->fd);
52         free(sb);
53 }
54
55 int stringbucket_find(struct stringbucket *sb, const char *string) {
56         char * end = sb->list + sb->size;
57         int string_len = strlen(string);
58
59         char * ptr = sb->list;
60         while (ptr < end) {
61                 char * next = (char *) strnchr(ptr, '\n', end - ptr);
62                 if (next == NULL)
63                         next = end;
64                 int len = next - ptr;
65                 if (len > STRINGBUCKET_STRINGSIZE)
66                         len = STRINGBUCKET_STRINGSIZE;
67                 if (memcmp(ptr, string, (len < string_len ? string_len : len)) == 0) {
68                         return (ptr - sb->list);
69                 }
70                 ptr = next + 1;
71         }
72
73         return -1;
74 }
75
76 int stringbucket_add(struct stringbucket *sb, const char *string) {
77         if (stringbucket_find(sb, string) != -1) return 0;
78         flock(sb->fd, LOCK_EX);
79         write(sb->fd, string, strlen(string));
80         write(sb->fd, "\n", 1);
81         flock(sb->fd, LOCK_UN);
82         return 1;
83 }
84
85 int stringbucket_delete(struct stringbucket *sb, const char *string) {
86         int pos = stringbucket_find(sb, string);
87         if (pos == -1) return 0;
88
89         /* We doin' it DOS style! */
90         sb->list[pos] = 0;
91 }
92
93 void stringbucket_iterate(struct stringbucket *sb, void (*iter)(char *, void *), void *stuff) {
94         char string[STRINGBUCKET_STRINGSIZE + 1];
95         char * ptr = sb->list;
96         char * end = sb->list + sb->size;
97
98         while (ptr < end) {
99                 char * next = (char *) strnchr(ptr, '\n', end - ptr);
100                 if (next == NULL)
101                         next = end;
102                 if (ptr[0] == 0) {
103                         ptr = next + 1;
104                         continue;
105                 }
106                 int len = next - ptr;
107                 memcpy(string, ptr, len);
108                 string[len] = 0;
109                 iter(string, stuff);
110                 ptr = next + 1;
111         }
112 }