/* * Copyright (C) 2012-2013 Andy Spencer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _GNU_SOURCE #define _XOPEN_SOURCE #define _XOPEN_SOURCE_EXTENDED #define _POSIX_C_SOURCE 200100 #include #include #include #include #include #include #include #include "view.h" #include "util.h" #pragma weak view_exit #pragma weak view_debug /* Static data */ static int epoll = 0; static FILE *debug_fd = NULL; static int running = 0; /* View debugging */ extern void view_debug(const char *fmt, va_list ap); /* Helper functions */ static void message(FILE *output_fd, const char *prefix, const char *fmt, va_list ap) { va_list tmp; struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); unsigned int sec = ts.tv_sec; unsigned int msec = ts.tv_nsec / 1000000; /* Log to standard out */ if (output_fd) { va_copy(tmp, ap); fprintf(output_fd, "%u.%03u: ", sec, msec); fprintf(output_fd, "%s: ", prefix); vfprintf(output_fd, fmt, tmp); fprintf(output_fd, "\n"); fflush(output_fd); } /* Log to debug file */ if (debug_fd) { va_copy(tmp, ap); fprintf(debug_fd, "%u.%03u: ", sec, msec); fprintf(debug_fd, "%s: ", prefix); vfprintf(debug_fd, fmt, tmp); fprintf(debug_fd, "\n"); fflush(debug_fd); } /* Log to status bar */ if (&view_debug) { va_copy(tmp, ap); view_debug(fmt, tmp); } } /* Initialize */ void util_init(void) { epoll = epoll_create(1); debug_fd = fopen("/tmp/lamechat.log", "w+"); running = 1; } /* String functions */ void strsub(char *str, char find, char repl) { for (char *cur = str; *cur; cur++) if (*cur == find) *cur = repl; } char *strcopy(const char *str) { if (str == NULL) return NULL; return strdup(str); } void strset(char **old, const char *str) { if (*old) free(*old); if (str) *old = strdup(str); else *old = NULL; } int match(const char *a, const char *b) { if (a == b) return 1; if (!a || !b) return 0; return !strcmp(a, b); } int prefix(const char *str, const char *prefix, const char **suffix) { while (*str && *prefix) { if (*str != *prefix) return 0; str++; prefix++; } if (*prefix) return 0; if (*str && *str != ' ') return 0; while (*str && *str == ' ') str++; if (suffix) *suffix = *str ? str : NULL; return 1; } /* Memory functions */ void *alloc0(int size) { void *data = calloc(1, size); if (!data) error("memory allocation failed"); return data; } void append(buf_t *buf, const char *data, int len) { if (buf->len + len + 1 > buf->max) { buf->max += (((buf->len+len)/4096)+1)*4096; buf->data = realloc(buf->data, buf->max); if (!buf->data) error("buffer reallocation allocation failed"); } if (data) memcpy(buf->data + buf->len, data, len); buf->len += len; ((char*)buf->data)[buf->len] = '\0'; } void release(buf_t *buf) { free(buf->data); buf->data = 0; buf->len = 0; buf->max = 0; } const char *reset(buf_t *buf) { if (!buf->len) return NULL; buf->len = 0; return buf->data; } /* Data functions */ int base64(const void *_in, int ilen, void *_out, int olen) { static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; const unsigned char *in = _in; unsigned char *out = _out; int val = 0; int len = ((ilen-1)/3+1)*4; if (olen < len) return 0; for (int i=0; i>6*3)&077]; *(out++) = table[(val>>6*2)&077]; *(out++) = table[(val>>6*1)&077]; *(out++) = table[(val>>6*0)&077]; } switch (ilen%3) { case 2: val = *(in++)<<020; val |= *(in++)<<010; *out++ = table[(val>>6*3)&077]; *out++ = table[(val>>6*2)&077]; *out++ = table[(val>>6*1)&077]; *out++ = '='; break; case 1: val = *(in++)<<020; *out++ = table[(val>>6*3)&077]; *out++ = table[(val>>6*2)&077]; *out++ = '='; *out++ = '='; break; } return len; } /* File functions */ char *read_file(const char *path, int *len) { /* we could use stat, but we'll try to be portable */ FILE *fd = fopen(path, "rt+"); if (!fd) return NULL; int block = 512; // read size int size = 512; // buffer size int slen = 0; // string length char *buf = malloc(size); if (!buf) goto err; while (!feof(fd)) { if (slen + block + 1 > size) { size *= 2; buf = realloc(buf, size); if (!buf) goto err; } slen += fread(&buf[slen], 1, block, fd); buf[slen] = '\0'; } err: if (len) *len = slen; fclose(fd); return buf; } /* Polling functions */ int poll_add(poll_t *poll, int fd, cb_t cb, void *data) { struct epoll_event ctl = { .events = EPOLLIN | EPOLLOUT | EPOLLERR, .data.ptr = poll, }; poll->fd = fd; poll->cb = cb; poll->data = data; return epoll_ctl(epoll, EPOLL_CTL_ADD, fd, &ctl); } int poll_ctl(poll_t *poll, int in, int out, int err) { struct epoll_event ctl = { .events = (in ? EPOLLIN : 0) | (out ? EPOLLOUT : 0) | (err ? EPOLLERR : 0), .data.ptr = poll, }; return epoll_ctl(epoll, EPOLL_CTL_MOD, poll->fd, &ctl); } int poll_del(poll_t *poll) { return epoll_ctl(epoll, EPOLL_CTL_DEL, poll->fd, NULL); } int poll_run(int timeout) { poll_t *poll; int count; struct epoll_event event; while (running) { errno = 0; count = epoll_wait(epoll, &event, 1, timeout); if (errno == EINTR) continue; if (count < 0) continue; if (count == 0) continue; if (!(poll = event.data.ptr)) continue; if (!(poll->cb)) continue; //debug("poll fd=%d count=%d,%s%s%s%s%s%s%s%s%s%s%s%s%s%s", // poll->fd, count, // event.events & EPOLLIN ? " in" : "", // event.events & EPOLLPRI ? " pri" : "", // event.events & EPOLLOUT ? " out" : "", // event.events & EPOLLRDNORM ? " rdnorm" : "", // event.events & EPOLLRDBAND ? " rdband" : "", // event.events & EPOLLWRNORM ? " wrnorm" : "", // event.events & EPOLLWRBAND ? " wrband" : "", // event.events & EPOLLMSG ? " msg" : "", // event.events & EPOLLERR ? " err" : "", // event.events & EPOLLHUP ? " hup" : "", // event.events & EPOLLRDHUP ? " rdhup" : "", // event.events & EPOLLWAKEUP ? " wakeup" : "", // event.events & EPOLLONESHOT ? " oneshot" : "", // event.events & EPOLLET ? " et" : ""); poll->cb(poll->data); return running; } return running; } void poll_quit(void) { running = 0; } /* Debugging functions */ void debug(char *fmt, ...) { va_list ap; va_start(ap, fmt); message(NULL, "debug", fmt, ap); va_end(ap); } void error(char *fmt, ...) { va_list ap; va_start(ap, fmt); fflush(stdout); fflush(stderr); message(stderr, "error", fmt, ap); va_end(ap); if (view_exit) view_exit(); exit(-1); }