]> Pileus Git - ~andy/lamechat/blob - util.c
Escape XMPP html
[~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 <ctype.h>
28 #include <errno.h>
29 #include <time.h>
30
31 #include <sys/epoll.h>
32
33 #include "view.h"
34 #include "util.h"
35
36 #pragma weak view_exit
37 #pragma weak view_debug
38
39 /* Static data */
40 static int   epoll    = 0;
41 static FILE *debug_fd = NULL;
42 static int   running  = 0;
43
44 /* View debugging */
45 extern void view_debug(const char *fmt, va_list ap);
46
47 /* Helper functions */
48 static void message(FILE *output_fd, const char *prefix, const char *fmt, va_list ap)
49 {
50         va_list tmp;
51         struct timespec ts;
52
53         clock_gettime(CLOCK_REALTIME, &ts);
54         unsigned int sec = ts.tv_sec;
55         unsigned int msec = ts.tv_nsec / 1000000;
56
57         /* Log to standard out */
58         if (output_fd) {
59                 va_copy(tmp, ap);
60                 fprintf(output_fd, "%u.%03u: ", sec, msec);
61                 fprintf(output_fd, "%s: ", prefix);
62                 vfprintf(output_fd, fmt, tmp);
63                 fprintf(output_fd, "\n");
64                 fflush(output_fd);
65         }
66
67         /* Log to debug file */
68         if (debug_fd) {
69                 va_copy(tmp, ap);
70                 fprintf(debug_fd, "%u.%03u: ", sec, msec);
71                 fprintf(debug_fd, "%s: ", prefix);
72                 vfprintf(debug_fd, fmt, tmp);
73                 fprintf(debug_fd, "\n");
74                 fflush(debug_fd);
75         }
76
77         /* Log to status bar */
78         if (&view_debug) {
79                 va_copy(tmp, ap);
80                 view_debug(fmt, tmp);
81         }
82 }
83
84 /* Initialize */
85 void util_init(void)
86 {
87         epoll    = epoll_create(1);
88         debug_fd = fopen("/tmp/lamechat.log", "w+");
89         running  = 1;
90 }
91
92 /* String functions */
93 void strsub(char *str, char find, char repl)
94 {
95         for (char *cur = str; *cur; cur++)
96                 if (*cur == find)
97                         *cur = repl;
98 }
99
100 char *strcopy(const char *str)
101 {
102         if (str == NULL)
103                 return NULL;
104         return strdup(str);
105 }
106
107 void strset(char **old, const char *str)
108 {
109         if (*old)
110                 free(*old);
111         if (str)
112                 *old = strdup(str);
113         else
114                 *old = NULL;
115 }
116
117 int match(const char *a, const char *b)
118 {
119         if (a == b)
120                 return 1;
121         if (!a || !b)
122                 return 0;
123         return !strcmp(a, b);
124 }
125
126 int prefix(const char *str, const char *prefix, const char **suffix)
127 {
128         while (*str && *prefix) {
129                 if (*str != *prefix)
130                         return 0;
131                 str++;
132                 prefix++;
133         }
134         if (*prefix)
135                 return 0;
136         if (*str && *str != ' ')
137                 return 0;
138         while (*str && *str == ' ')
139                 str++;
140         if (suffix)
141                 *suffix = *str ? str : NULL;
142         return 1;
143 }
144
145 char *despace(char *str)
146 {
147         int chr, spaces = 1;
148         char *src = str;
149         char *dst = str;
150         while ((chr = *src++)) {
151                 if (isspace(chr))
152                         spaces++;
153                 else
154                         spaces=0;
155                 if (spaces == 0)
156                         *dst++ = chr;
157                 if (spaces == 1)
158                         *dst++ = ' ';
159         }
160         if (dst > str && spaces)
161                 dst--;
162         *dst = '\0';
163         return str;
164 }
165
166 void escape(char *dst, const char *src, int len)
167 {
168         int n, di = 0;
169         const char *repl;
170         for (int si=0; src[si]; si++) {
171                 switch (src[si]) {
172                         case '&': n = 5; repl = "&amp;";  break;
173                         case '<': n = 4; repl = "&lt;";   break;
174                         case '>': n = 4; repl = "&gt;";   break;
175                         default:  n = 1; repl = &src[si]; break;
176                 }
177                 if (di+n >= len)
178                         break;
179                 for (int ri = 0; ri < n; ri++)
180                         dst[di++] = repl[ri];
181         }
182         dst[di] = '\0';
183 }
184
185 /* Memory functions */
186 void *alloc0(int size)
187 {
188         void *data = calloc(1, size);
189         if (!data)
190                 error("memory allocation failed");
191         return data;
192 }
193
194 void append(buf_t *buf, const char *data, int len)
195 {
196         if (buf->len + len + 1 > buf->max) {
197                 buf->max += (((buf->len+len)/4096)+1)*4096;
198                 buf->data = realloc(buf->data, buf->max);
199                 if (!buf->data)
200                         error("buffer reallocation allocation failed");
201         }
202         if (data)
203                 memcpy(buf->data + buf->len, data, len);
204         buf->len += len;
205         ((char*)buf->data)[buf->len] = '\0';
206 }
207
208 void release(buf_t *buf)
209 {
210         free(buf->data);
211         buf->data = 0;
212         buf->len  = 0;
213         buf->max  = 0;
214 }
215
216 const char *reset(buf_t *buf)
217 {
218         if (!buf->len)
219                 return NULL;
220         buf->len = 0;
221         return buf->data;
222 }
223
224 /* Data functions */
225 int base64(const void *_in, int ilen, void *_out, int olen)
226 {
227         static const char table[] =
228                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
229                 "abcdefghijklmnopqrstuvwxyz"
230                 "0123456789+/";
231
232         const unsigned char *in  = _in;
233         unsigned char       *out = _out;
234
235         int val = 0;
236         int len = ((ilen-1)/3+1)*4;
237
238         if (olen < len)
239                 return 0;
240
241         for (int i=0; i<ilen/3; i++) {
242                 val      = *(in++) << 020;
243                 val     |= *(in++) << 010;
244                 val     |= *(in++) << 000;
245                 *(out++) = table[(val>>6*3)&077];
246                 *(out++) = table[(val>>6*2)&077];
247                 *(out++) = table[(val>>6*1)&077];
248                 *(out++) = table[(val>>6*0)&077];
249         }
250
251         switch (ilen%3) {
252                 case 2:
253                         val    = *(in++)<<020;
254                         val   |= *(in++)<<010;
255                         *out++ = table[(val>>6*3)&077];
256                         *out++ = table[(val>>6*2)&077];
257                         *out++ = table[(val>>6*1)&077];
258                         *out++ = '=';
259                         break;
260                 case 1:
261                         val    = *(in++)<<020;
262                         *out++ = table[(val>>6*3)&077];
263                         *out++ = table[(val>>6*2)&077];
264                         *out++ = '=';
265                         *out++ = '=';
266                         break;
267         }
268
269         return len;
270 }
271
272 /* File functions */
273 char *read_file(const char *path, int *len)
274 {
275         /* we could use stat, but we'll try to be portable */
276         FILE *fd = fopen(path, "rt+");
277         if (!fd)
278                 return NULL;
279
280         int   block = 512; // read size
281         int   size  = 512; // buffer size
282         int   slen  = 0;   // string length
283         char *buf   = malloc(size);
284         if (!buf)
285                 goto err;
286
287         while (!feof(fd)) {
288                 if (slen + block + 1 > size) {
289                         size *= 2;
290                         buf   = realloc(buf, size);
291                         if (!buf)
292                                 goto err;
293                 }
294                 slen += fread(&buf[slen], 1, block, fd);
295                 buf[slen] = '\0';
296         }
297
298 err:
299         if (len)
300                 *len = slen;
301         fclose(fd);
302         return buf;
303 }
304
305 /* Polling functions */
306 int poll_add(poll_t *poll, int fd, cb_t cb, void *data)
307 {
308         struct epoll_event ctl = {
309                 .events = EPOLLET | EPOLLIN | EPOLLOUT | EPOLLERR,
310                 .data.ptr = poll,
311         };
312         poll->fd   = fd;
313         poll->cb   = cb;
314         poll->data = data;
315         return epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &ctl);
316 }
317
318 int poll_ctl(poll_t *poll, int in, int out, int err)
319 {
320         struct epoll_event ctl = {
321                 .events = EPOLLET
322                         | (in  ? EPOLLIN  : 0)
323                         | (out ? EPOLLOUT : 0)
324                         | (err ? EPOLLERR : 0),
325                 .data.ptr = poll,
326         };
327         return epoll_ctl(epoll, EPOLL_CTL_MOD, poll->fd, &ctl);
328 }
329
330 int poll_del(poll_t *poll)
331 {
332         return epoll_ctl(epoll, EPOLL_CTL_DEL, poll->fd, NULL);
333 }
334
335 int poll_run(int timeout)
336 {
337         poll_t *poll;
338         int count;
339         struct epoll_event event;
340
341         while (running) {
342                 errno = 0;
343                 count = epoll_wait(epoll, &event, 1, timeout);
344                 if (errno == EINTR)
345                         continue;
346                 if (count < 0)
347                         continue;
348                 if (count == 0)
349                         return running;
350                 if (!(poll = event.data.ptr))
351                         continue;
352                 if (!(poll->cb))
353                         continue;
354                 //debug("poll fd=%d count=%d,%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
355                 //        poll->fd, count,
356                 //        event.events & EPOLLIN      ? " in"      : "",
357                 //        event.events & EPOLLPRI     ? " pri"     : "",
358                 //        event.events & EPOLLOUT     ? " out"     : "",
359                 //        event.events & EPOLLRDNORM  ? " rdnorm"  : "",
360                 //        event.events & EPOLLRDBAND  ? " rdband"  : "",
361                 //        event.events & EPOLLWRNORM  ? " wrnorm"  : "",
362                 //        event.events & EPOLLWRBAND  ? " wrband"  : "",
363                 //        event.events & EPOLLMSG     ? " msg"     : "",
364                 //        event.events & EPOLLERR     ? " err"     : "",
365                 //        event.events & EPOLLHUP     ? " hup"     : "",
366                 //        event.events & EPOLLRDHUP   ? " rdhup"   : "",
367                 //        event.events & EPOLLWAKEUP  ? " wakeup"  : "",
368                 //        event.events & EPOLLONESHOT ? " oneshot" : "",
369                 //        event.events & EPOLLET      ? " et"      : "");
370                 poll->cb(poll->data);
371                 return running;
372         }
373         return running;
374 }
375
376 void poll_quit(void)
377 {
378         running = 0;
379 }
380
381 /* Debugging functions */
382 void debug(char *fmt, ...)
383 {
384         va_list ap;
385         va_start(ap, fmt);
386         message(NULL, "debug", fmt, ap);
387         va_end(ap);
388 }
389
390 void error(char *fmt, ...)
391 {
392         va_list ap;
393         va_start(ap, fmt);
394         fflush(stdout);
395         fflush(stderr);
396         message(stderr, "error", fmt, ap);
397         va_end(ap);
398         if (view_exit)
399                 view_exit();
400         exit(-1);
401 }