]> Pileus Git - ~andy/lamechat/blob - util.c
XMPP MUC
[~andy/lamechat] / util.c
1 /*
2  * Copyright (C) 2012-2013 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #define _GNU_SOURCE
19 #define _XOPEN_SOURCE
20 #define _XOPEN_SOURCE_EXTENDED
21 #define _POSIX_C_SOURCE 200100
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <time.h>
29
30 #include <sys/epoll.h>
31
32 #include "view.h"
33 #include "util.h"
34
35 #pragma weak view_exit
36 #pragma weak view_debug
37
38 /* Static data */
39 static int   epoll    = 0;
40 static FILE *debug_fd = NULL;
41 static int   running  = 0;
42
43 /* View debugging */
44 extern void view_debug(const char *fmt, va_list ap);
45
46 /* Helper functions */
47 static void message(FILE *output_fd, const char *prefix, const char *fmt, va_list ap)
48 {
49         va_list tmp;
50         struct timespec ts;
51
52         clock_gettime(CLOCK_REALTIME, &ts);
53         unsigned int sec = ts.tv_sec;
54         unsigned int msec = ts.tv_nsec / 1000000;
55
56         /* Log to standard out */
57         if (output_fd) {
58                 va_copy(tmp, ap);
59                 fprintf(output_fd, "%u.%03u: ", sec, msec);
60                 fprintf(output_fd, "%s: ", prefix);
61                 vfprintf(output_fd, fmt, tmp);
62                 fprintf(output_fd, "\n");
63                 fflush(output_fd);
64         }
65
66         /* Log to debug file */
67         if (debug_fd) {
68                 va_copy(tmp, ap);
69                 fprintf(debug_fd, "%u.%03u: ", sec, msec);
70                 fprintf(debug_fd, "%s: ", prefix);
71                 vfprintf(debug_fd, fmt, tmp);
72                 fprintf(debug_fd, "\n");
73                 fflush(debug_fd);
74         }
75
76         /* Log to status bar */
77         if (&view_debug) {
78                 va_copy(tmp, ap);
79                 view_debug(fmt, tmp);
80         }
81 }
82
83 /* Initialize */
84 void util_init(void)
85 {
86         epoll    = epoll_create(1);
87         debug_fd = fopen("/tmp/lamechat.log", "w+");
88         running  = 1;
89 }
90
91 /* String functions */
92 void strsub(char *str, char find, char repl)
93 {
94         for (char *cur = str; *cur; cur++)
95                 if (*cur == find)
96                         *cur = repl;
97 }
98
99 char *strcopy(const char *str)
100 {
101         if (str == NULL)
102                 return NULL;
103         return strdup(str);
104 }
105
106 int match(const char *a, const char *b)
107 {
108         if (a == b)
109                 return 1;
110         if (!a || !b)
111                 return 0;
112         return !strcmp(a, b);
113 }
114
115 int prefix(const char *str, const char *prefix, const char **suffix)
116 {
117         while (*str && *prefix) {
118                 if (*str != *prefix)
119                         return 0;
120                 str++;
121                 prefix++;
122         }
123         if (*prefix)
124                 return 0;
125         if (*str && *str != ' ')
126                 return 0;
127         while (*str && *str == ' ')
128                 str++;
129         if (suffix)
130                 *suffix = *str ? str : NULL;
131         return 1;
132 }
133
134 /* Memory functions */
135 void *alloc0(int size)
136 {
137         void *data = calloc(1, size);
138         if (!data)
139                 error("memory allocation failed");
140         return data;
141 }
142
143 void append(buf_t *buf, const char *data, int len)
144 {
145         if (buf->len + len + 1 > buf->max) {
146                 buf->max += (((buf->len+len)/4096)+1)*4096;
147                 buf->data = realloc(buf->data, buf->max);
148                 if (!buf->data)
149                         error("buffer reallocation allocation failed");
150         }
151         if (data)
152                 memcpy(buf->data + buf->len, data, len);
153         buf->len += len;
154         ((char*)buf->data)[buf->len] = '\0';
155 }
156
157 void release(buf_t *buf)
158 {
159         free(buf->data);
160         buf->data = 0;
161         buf->len  = 0;
162         buf->max  = 0;
163 }
164
165 /* Data functions */
166 int base64(const void *_in, int ilen, void *_out, int olen)
167 {
168         static const char table[] =
169                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
170                 "abcdefghijklmnopqrstuvwxyz"
171                 "0123456789+/";
172
173         const unsigned char *in  = _in;
174         unsigned char       *out = _out;
175
176         int val = 0;
177         int len = ((ilen-1)/3+1)*4;
178
179         if (olen < len)
180                 return 0;
181
182         for (int i=0; i<ilen/3; i++) {
183                 val      = *(in++) << 020;
184                 val     |= *(in++) << 010;
185                 val     |= *(in++) << 000;
186                 *(out++) = table[(val>>6*3)&077];
187                 *(out++) = table[(val>>6*2)&077];
188                 *(out++) = table[(val>>6*1)&077];
189                 *(out++) = table[(val>>6*0)&077];
190         }
191
192         switch (ilen%3) {
193                 case 2:
194                         val    = *(in++)<<020;
195                         val   |= *(in++)<<010;
196                         *out++ = table[(val>>6*3)&077];
197                         *out++ = table[(val>>6*2)&077];
198                         *out++ = table[(val>>6*1)&077];
199                         *out++ = '=';
200                         break;
201                 case 1:
202                         val    = *(in++)<<020;
203                         *out++ = table[(val>>6*3)&077];
204                         *out++ = table[(val>>6*2)&077];
205                         *out++ = '=';
206                         *out++ = '=';
207                         break;
208         }
209
210         return len;
211 }
212
213 /* File functions */
214 char *read_file(const char *path, int *len)
215 {
216         /* we could use stat, but we'll try to be portable */
217         FILE *fd = fopen(path, "rt+");
218         if (!fd)
219                 return NULL;
220
221         int   block = 512; // read size
222         int   size  = 512; // buffer size
223         int   slen  = 0;   // string length
224         char *buf   = malloc(size);
225         if (!buf)
226                 goto err;
227
228         while (!feof(fd)) {
229                 if (slen + block + 1 > size) {
230                         size *= 2;
231                         buf   = realloc(buf, size);
232                         if (!buf)
233                                 goto err;
234                 }
235                 slen += fread(&buf[slen], 1, block, fd);
236                 buf[slen] = '\0';
237         }
238
239 err:
240         if (len)
241                 *len = slen;
242         fclose(fd);
243         return buf;
244 }
245
246 /* Polling functions */
247 int poll_add(poll_t *poll, int fd, cb_t cb, void *data)
248 {
249         struct epoll_event ctl = {
250                 .events = EPOLLIN | EPOLLOUT | EPOLLERR,
251                 .data.ptr = poll,
252         };
253         poll->fd   = fd;
254         poll->cb   = cb;
255         poll->data = data;
256         return epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &ctl);
257 }
258
259 int poll_ctl(poll_t *poll, int in, int out, int err)
260 {
261         struct epoll_event ctl = {
262                 .events = (in  ? EPOLLIN  : 0)
263                         | (out ? EPOLLOUT : 0)
264                         | (err ? EPOLLERR : 0),
265                 .data.ptr = poll,
266         };
267         return epoll_ctl(epoll, EPOLL_CTL_MOD, poll->fd, &ctl);
268 }
269
270 int poll_del(poll_t *poll)
271 {
272         return epoll_ctl(epoll, EPOLL_CTL_DEL, poll->fd, NULL);
273 }
274
275 int poll_run(int timeout)
276 {
277         poll_t *poll;
278         int count;
279         struct epoll_event event;
280
281         while (running) {
282                 errno = 0;
283                 count = epoll_wait(epoll, &event, 1, timeout);
284                 if (errno == EINTR)
285                         continue;
286                 if (count < 0)
287                         continue;
288                 if (count == 0)
289                         continue;
290                 if (!(poll = event.data.ptr))
291                         continue;
292                 if (!(poll->cb))
293                         continue;
294                 //debug("poll fd=%d count=%d,%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
295                 //        poll->fd, count,
296                 //        event.events & EPOLLIN      ? " in"      : "",
297                 //        event.events & EPOLLPRI     ? " pri"     : "",
298                 //        event.events & EPOLLOUT     ? " out"     : "",
299                 //        event.events & EPOLLRDNORM  ? " rdnorm"  : "",
300                 //        event.events & EPOLLRDBAND  ? " rdband"  : "",
301                 //        event.events & EPOLLWRNORM  ? " wrnorm"  : "",
302                 //        event.events & EPOLLWRBAND  ? " wrband"  : "",
303                 //        event.events & EPOLLMSG     ? " msg"     : "",
304                 //        event.events & EPOLLERR     ? " err"     : "",
305                 //        event.events & EPOLLHUP     ? " hup"     : "",
306                 //        event.events & EPOLLRDHUP   ? " rdhup"   : "",
307                 //        event.events & EPOLLWAKEUP  ? " wakeup"  : "",
308                 //        event.events & EPOLLONESHOT ? " oneshot" : "",
309                 //        event.events & EPOLLET      ? " et"      : "");
310                 poll->cb(poll->data);
311                 return running;
312         }
313         return running;
314 }
315
316 void poll_quit(void)
317 {
318         running = 0;
319 }
320
321 /* Debugging functions */
322 void debug(char *fmt, ...)
323 {
324         va_list ap;
325         va_start(ap, fmt);
326         message(NULL, "debug", fmt, ap);
327         va_end(ap);
328 }
329
330 void error(char *fmt, ...)
331 {
332         va_list ap;
333         va_start(ap, fmt);
334         fflush(stdout);
335         fflush(stderr);
336         message(stderr, "error", fmt, ap);
337         va_end(ap);
338         if (view_exit)
339                 view_exit();
340         exit(-1);
341 }