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