#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <regex.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <avl.h>
#include <postgresql/libpq-fe.h>
#include "f00f.h"
struct zoekkarma {
real karma;
byte *name;
byte *key;
};
void f00f_LOADAVG(presence_t p, byte *arg) {
double la[3];
getloadavg(la, 3);
say(p, _("Load average: %.2f, %.2f, %.2f."), la[0], la[1], la[2]);
}
void f00f_MEMINFO(presence_t p, byte *arg) {
int ps, fd;
unsigned int size, resident, share, trs, drs, lrs, dt;
char s[1024], e[64] = {0}; /* Fix me. */
ps = getpagesize();
fd = open("/proc/self/statm", O_RDONLY);
if(fd < 0)
return;
if(read(fd, s, 1024) < 0)
return;
close(fd);
sscanf(s, "%u %u %u %u %u %u %u", &size, &resident, &share, &trs, &drs,
&lrs, &dt);
say(p,
_("Size: %u%sB; Resident: %u%sB; Shared: %u pages (%u%sB); "
"Text: %u%sB; Stack: %u%sB; Library: %u%sB; Dirty: %u pages (%u%sB)\n"),
strunit(ps * size, 1024, e), e,
strunit(ps * resident, 1024, e+2), e+2, share,
strunit(ps * share, 1024, e+4), e+4,
strunit(ps * trs, 1024, e+6), e+6,
strunit(ps * drs, 1024, e+8), e+8,
strunit(ps * lrs, 1024, e+10), e+10, dt,
strunit(dt * ps, 1024, e+12), e+12);
}
static void f00f_DEBUG(presence_t p, byte *arg) {
if(arg)
f00f_debug = strtoul(arg, 0, 0);
say(p, _("debug is nu %u"), f00f_debug);
}
static void f00f_OPZOUTEN(presence_t p, byte *arg) {
if(p.chan)
part(p.chan);
}
static void opjes(presence_t p, byte *arg, byte *mode) {
if(!p.chan)
return;
if(p.nick->cred < AUTH_CERTAIN)
return;
if(arg)
p.nick = getnick(arg);
if(!p.nick)
return;
nickmode(p, mode);
}
static void f00f_OP(presence_t p, byte *arg) {
opjes(p, arg, "+o");
}
static void f00f_DEOP(presence_t p, byte *arg) {
opjes(p, arg, "-o");
}
static void f00f_VOICE(presence_t p, byte *arg) {
opjes(p, arg, "+v");
}
static void f00f_DEVOICE(presence_t p, byte *arg) {
opjes(p, arg, "-v");
}
static void f00f_HOP(presence_t p, byte *arg) {
opjes(p, arg, "+h");
}
static void f00f_DEHOP(presence_t p, byte *arg) {
opjes(p, arg, "-h");
}
void f00f_WIJS(presence_t p, byte *arg) {
ventriloact("Chii", p, _("wijst naar %s. Hideki!"), arg ? fixnick(arg) : p.nick->name);
}
static unsigned int bitcount(unsigned long i) {
i = ((i & 0xAAAAAAAA) >> 1) + (i & 0x55555555);
i = ((i & 0xCCCCCCCC) >> 2) + (i & 0x33333333);
i = ((i & 0xF0F0F0F0) >> 4) + (i & 0x0F0F0F0F);
i = ((i & 0xFF00FF00) >> 8) + (i & 0x00FF00FF);
return (i & 0xFFFF) + (i >> 16);
}
static unsigned long bitmask(unsigned int n) {
return n?~((1<<(32-n))-1):0;
}
static unsigned int ipclass(unsigned int n) {
/* These values need to be verified */
if(n < 0x80000000)
return 8;
else if(n < 0x12000000)
return 16;
else if(n < 0xE0000000)
return 24;
else
return 4;
}
static void f00f_CIDR(presence_t p, byte *arg) {
byte *s, *m;
bool cidr = FALSE;
unsigned long ip, mask = ~0;
splitbuf_t *q;
if(!arg) {
ip = (unsigned long)vorige.echt;
} else {
ip = strtocidr(arg, &m);
if(*m == '/') {
mask = strtocidr(++m, &s);
if(s-m < 1)
return;
if(mask <= 32 && s-m < 4) {
cidr = TRUE;
mask = bitmask(mask);
}
} else if(!*m) {
cidr = FALSE;
mask = bitmask(ipclass(ip));
} else {
return;
}
}
q = qnew();
if(ip)
qprintf(q, "%lu.%lu.%lu.%lu",
ip>>24, (ip>>16)&255, (ip>>8)&255, ip&255);
if(cidr || mask != bitmask(bitcount(mask)))
qprintf(q, "/%lu.%lu.%lu.%lu",
mask>>24, (mask>>16)&255, (mask>>8)&255, mask&255);
else
qprintf(q, "/%u", bitcount(mask));
if(ip && mask != 0 && mask != ~0) {
ip &= mask;
qprintf(q, " %lu.%lu.%lu.%lu",
ip>>24, (ip>>16)&255, (ip>>8)&255, ip&255);
ip |= ~mask;
qprintf(q, " %lu.%lu.%lu.%lu",
ip>>24, (ip>>16)&255, (ip>>8)&255, ip&255);
}
sayq(p, q);
qfree(q);
}
static void f00f_CALC(presence_t p, byte *arg) {
real r;
if(!arg)
return;
r = calc(arg);
if(isnan(r) && p.chan)
lart(p);
else
say(p, "%.15Lg 0x%Lx 0%Lo", r, (s64)r, (s64)r);
}
static void f00f_BEREKEN(presence_t p, byte *arg) {
echt_t r;
splitbuf_t *q;
if(!arg)
return;
r = bereken(arg);
if(isfout(r) && p.chan)
lart(p);
else {
q = qnew();
spreek_echt(q, r);
qstrcat(q, " (");
echtprint(q, r);
qstrcat(q, ")");
sayq(p, q);
qfree(q);
}
}
static void f00f_TIME(presence_t p, byte *arg) {
char *s, *t;
struct timeval tv;
struct tm tm, tw;
time_t tt;
s64 now;
splitbuf_t *q;
if(!arg) {
gettimeofday(&tv, NULL);
now = tv.tv_sec;
} else {
now = strtoll(arg, &s, 10);
tv.tv_sec = now;
if(s && (*s == '.' || *s == ',')) {
tv.tv_usec = strtoul(++s, &t, 10);
if(t - s > 6) {
s[6] = '\0';
tv.tv_usec = strtoul(s, &t, 10);
}
t = s + 6 - (t - s);
while(t-- > s)
tv.tv_usec *= 10;
if(now < 0)
tv.tv_usec = 1000000 - tv.tv_usec;
} else {
tv.tv_usec = 0;
}
}
tt = (time_t)now;
localtime_r(&tt, &tm);
if(!tm.tm_wday)
tm.tm_wday = 7;
tt -= (tm.tm_wday-4)*86400;
localtime_r(&tt, &tw);
q = qnew();
qprintf(q, "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
if(tv.tv_usec)
qprintf(q, ".%06li", tv.tv_usec);
qprintf(q, " %02d-%d %03d",
tw.tm_yday/7+1, tm.tm_wday, (int)((now+3600)%86400*10/864));
sayq(p, q);
}
void f00f_DPKG(presence_t p, byte *arg) {
struct stat st;
byte *m = NULL, *n, *s, *e;
int plen;
int fd;
byte *name = NULL, *desc = NULL;
if(!arg)
return;
plen = strlen(arg);
fd = open("/var/lib/dpkg/available", O_RDONLY);
if(fd == -1) {
perror("open(/var/lib/dpkg/available)");
} else {
if(fstat(fd, &st) == -1) {
perror("fstat(/var/lib/dpkg/available)");
} else if(!st.st_size) {
perror("/var/lib/dpkg/available is empty\n");
} else {
m = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if(m == MAP_FAILED) {
perror("mmap(/var/lib/dpkg/available)");
m = NULL;
}
}
if(close(fd))
perror("close(/var/lib/dpkg/available)");
}
if(m) {
if(m[st.st_size-1] != '\n') {
perror("last line of /var/lib/dpkg/available isn't terminated\n");
return;
}
madvise(m, st.st_size, MADV_SEQUENTIAL);
e = (s = m) + st.st_size;
while(s < e) {
n = memchr(s, '\n', e - s);
if(*s == '\n') {
desc = NULL;
} else if(!strncasecmp(s, "Package:", 8)) {
s += 8;
while(isspace(*s))
s++;
if(!strncasecmp(s, arg, plen) && isspace(s[plen])) {
name = s;
name[plen] = '\0';
}
} else if(!strncasecmp(s, "Description:", 12)) {
s += 12;
while(isspace(*s))
s++;
desc = s;
desc[n - s] = '\0';
}
if(name && desc) {
say(p, "%s: %s", name, desc);
break;
}
s = n + 1;
}
munmap(m, st.st_size);
}
if(name && desc)
return;
say(p, _("Hrm. Ik kan \xe2\x80\x98%s\xe2\x80\x99 niet vinden."), arg);
}
void f00f_APT(presence_t p, byte *arg) {
struct stat st;
byte *m = NULL, *n, *s, *e;
int nlen = 0, llen = 0, fd, r, matches = 0;
bool match = FALSE;
byte *name = NULL;
regex_t re;
if(!arg)
return;
r = regcomp(&re, arg, REG_EXTENDED|REG_ICASE|REG_NOSUB);
if(r) {
nlen = regerror(r, &re, NULL, 0);
m = alloca(nlen);
regerror(r, &re, m, nlen);
say(p, "%s", m);
regfree(&re);
return;
}
fd = open("/var/lib/dpkg/available", O_RDONLY);
if(fd == -1) {
perror("open(/var/lib/dpkg/available)");
} else {
if(fstat(fd, &st) == -1) {
perror("fstat(/var/lib/dpkg/available)");
} else if(!st.st_size) {
perror("/var/lib/dpkg/available is empty\n");
} else {
m = pmalloc(st.st_size + 1);
if(!saferead(fd, m, st.st_size, 60000000LL)) {
pfree(m);
m = NULL;
perror("read(/var/lib/dpkg/available)");
}
}
if(close(fd))
perror("close(/var/lib/dpkg/available)");
}
if(m) {
splitbuf_t *q = qnew();
m[st.st_size++] = '\n';
// madvise(m, st.st_size, MADV_SEQUENTIAL|MADV_WILLNEED);
e = (s = m) + st.st_size;
while(s < e) {
n = memchr(s, '\n', e - s);
*n = '\0';
if(!*s) {
match = FALSE;
} else if(isspace(*s)) {
if(!match)
while(isspace(*++s));
} else if(!strncasecmp(s, "Package:", 8)) {
s += 8;
while(isspace(*s))
s++;
name = s;
nlen = n - s;
} else if(!match && !strncasecmp(s, "Description:", 12)) {
s += 12;
while(isspace(*s))
s++;
} else {
s = NULL;
}
if(s && !match) {
r = regexec(&re, s, 0, NULL, 0);
if(r == REG_ESPACE)
r++;
if(!r)
match = TRUE;
}
if(name && match) {
if(matches) {
if(llen < 400) {
llen += 2 + nlen;
qprintf(q, ", ");
if(llen < 400)
qwrite(q, name, nlen);
else
qprintf(q, "\002...\002");
}
} else {
llen = nlen + strlen(arg);
qprintf(q, "/\002%s\002/: ", arg);
qwrite(q, name, nlen);
}
matches++;
name = NULL;
}
s = n + 1;
}
if(!matches)
qprintf(q, _("Hrm. Ik kon niets vinden dat op /\002%s\002/ lijkt."), arg);
else
qprintf(q, _(" (%d matches)"), matches);
sayq(p, q);
qfree(q);
pfree(m);
}
regfree(&re);
}
void f00f_GOOGLE(presence_t p, byte *arg) {
byte *url = NULL;
splitbuf_t *q;
if(!arg)
arg = p.nick->name;
q = qnew();
qprintf(q, "http://www.google.com/search?btnI=submit&ie=UTF-8&oe=UTF-8&q=");
url_encode(q, arg);
splitbuf_finish(q);
if(splitline(q))
url = url_redir(splitline(q)->buf);
if(url)
say(p, "\00310G\0034o\0038o\00310g\0039l\0034e\017 %s", url);
else
say(p, _("\00310G\0034o\0038o\00310g\0039l\0034e\017 kon “%s” ni vinden."), arg);
}
static s64 gtoi(char *s, char **e) {
s64 r = 0;
for(;;) {
if(*s >= '0' && *s <= '9')
r = (r * 10) + *s - '0';
else if(*s != ',')
break;
s++;
}
if(e)
*e = s;
return r;
}
static bool numberofresults(splitline_t *l) {
byte *s;
s64 n;
if(l->len < 2000)
return TRUE;
s = strstr(l->buf, "<td bgcolor=#3366cc align=right nowrap><font size=-1 color=#ffffff>Results <b>");
if(!s)
return TRUE;
s += 78;
while(isdigit(*s))
s++;
if(strncmp(s, "</b> - <b>", 10))
return TRUE;
s += 10;
while(isdigit(*s))
s++;
if(strncmp(s, "</b> of about <b>", 17))
return TRUE;
s += 17;
n = gtoi(s, &s);
if(strncmp(s, "</b>", 4))
return TRUE;
*(s64 *)((splitbuf_t *)l->node.item)->userdata = n;
return TRUE;
}
static int googlecount(byte *format, ...) {
splitbuf_t b, *q;
byte *s;
va_list ap;
int x;
s64 number = 0;
q = qnew();
qprintf(q, "http://www.google.com/search?hl=en&q=");
va_start(ap, format);
x = vasprintf(&s, format, ap);
va_end(ap);
if(x == -1) {
qfree(q);
return -1;
}
url_encode(q, s);
free(s);
splitbuf_finish(q);
splitbuf_init(&b);
b.keepempty = FALSE;
b.keepdelim = FALSE;
b.hook = numberofresults;
b.userdata = &number;
url_fetch(&b, splitline(q)->buf);
splitbuf_clean(&b);
return number;
}
static void f00f_SUCKSRULESOMETER(presence_t p, byte *arg) {
int rules = 0, sucks = 0;
splitbuf_t *q;
if(!arg)
arg = p.nick->name;
rules += googlecount("\"%s rule\"", arg);
rules += googlecount("\"%s rules\"", arg);
rules += googlecount("\"%s rulez\"", arg);
sucks += googlecount("\"%s suck\"", arg);
sucks += googlecount("\"%s sucks\"", arg);
q = qnew();
qprintf(q, _("%s roeleert %d en zuigt %d keer"), arg, rules, sucks);
if(!rules && !sucks)
qprintf(q, _(" (rule-o-suck ratio onbepaald)."));
else if(!sucks)
qprintf(q, _(" (rule-o-suck ratio oneindig)."));
else
qprintf(q, _(" (rule-o-suck ratio %Lg)."), (real)rules/(real)sucks);
sayq(p, q);
qfree(q);
/* appreciaten? */
}
static void f00f_GOOGLEFIGHT(presence_t p, byte *arg) {
int a, b;
splitbuf_t *q;
int argc;
char **argv;
if(!arg)
return;
matchsplit(arg, &argc, &argv);
if(argc != 2)
return;
a = googlecount("\"%s\"", argv[0]);
b = googlecount("\"%s\"", argv[1]);
q = qnew();
qprintf(q, _("%s (%d) vs. %s (%d): "), argv[0], a, argv[1], b);
if(!a && !b)
qprintf(q, _("kansloos."));
else if(!a || !b)
qprintf(q, _("%s si hte suQ!"), argv[!b]);
else if(a == b)
qprintf(q, _("gelijkspel!"));
else if(b/a > 1000 || a/b > 1000)
qprintf(q, _("%s SI TEH WIN!!!!!11"), argv[a < b]);
else
qprintf(q, _("%s si hte win!"), argv[a < b]);
sayq(p, q);
qfree(q);
}
struct dubyastate {
splitbuf_t *quote;
bool finished;
};
static bool dubyaspeak(splitline_t *l) {
byte *s, *b, *e;
struct dubyastate *d;
d = ((splitbuf_t *)l->node.item)->userdata;
if(d->finished)
return TRUE;
s = l->buf;
if(!d->quote) {
b = strstr(s, "<dt>");
if(b) {
d->quote = qnew();
b += 4;
}
} else {
b = s;
}
if(d->quote) {
e = strstr(b, "</dt>");
if(e) {
splitbuf(d->quote, b, e-b);
d->finished = TRUE;
} else {
qstrcat(d->quote, b);
qputc(d->quote, ' ');
}
}
return TRUE;
}
static void f00f_DUBYASPEAK(presence_t p, byte *arg) {
splitbuf_t b;
struct dubyastate d;
d.quote = NULL;
d.finished = FALSE;
splitbuf_init(&b);
b.keepempty = FALSE;
b.keepdelim = FALSE;
b.hook = dubyaspeak;
b.userdata = &d;
url_fetch(&b, "http://www.aoshingo.com/dubya/randomdubya.shtml");
splitbuf_clean(&b);
if(d.quote && d.finished)
ventriloquateq("Bush", p, d.quote);
}
static bool karma(splitline_t *l) {
byte *p;
byte *karma, *name, *email, *at;
struct zoekkarma *z;
z = ((splitbuf_t *)l->node.item)->userdata;
p = l->buf;
while(*p == ' ') p++;
karma = p;
p = index(p, ' ');
if(!p)
return TRUE;
p++[0] = '\0';
name = p;
p = index(p, '(');
if(!p)
return TRUE;
p++[-1] = '\0';
email = p;
/* Zorg dat Cris' email adres goed geparst wordt */
if(*p == '"') {
p = index(p + 1, '"');
if(!p)
return TRUE;
}
p = index(p, ' ');
if(!p)
return TRUE;
if(strncmp(p, " at ", 4))
return TRUE;
*p = '\0';
p += 4;
at = p;
p = index(p, ')');
if(!p)
return TRUE;
*p = '\0';
/* W00t! Nu hebben we alles wat we nodig hebben. */
if(!strcasecmp(z->key, name) || !strcmp(z->key, email))
goto gevind;
memmove(at - 3, at, p - at + 1);
at[-4] = '@';
if(!strcmp(z->key, email))
goto gevind;
return TRUE;
gevind:
if(!z->name)
z->name = xstrdup(name);
z->karma += atof(karma);
return TRUE;
}
static void f00f_KARMA(presence_t p, byte *arg) {
splitbuf_t b;
struct zoekkarma z;
if(!arg)
arg = p.nick->name;
z.karma = 0.0;
z.name = NULL;
z.key = arg;
splitbuf_init(&b);
b.keepempty = FALSE;
b.keepdelim = FALSE;
b.hook = karma;
b.userdata = &z;
url_fetch(&b, "http://master.debian.org/~edd/karma.txt");
splitbuf_clean(&b);
if(!z.name)
say(p, _("%s heeft geen karma."), arg);
else {
say(p, _("%s heeft een karma van %Lg."), z.name, z.karma);
free(z.name);
}
}
static void f00f_POOTJE(presence_t p, byte *arg) {
if(!arg)
arg = p.nick->name;
else if(!frandom(0,3)) {
act(p, _("vindt %s maar een flikker."), arg);
return;
}
act(p, _("geeft %s een pootje."), arg);
}
static void f00f_JUMP(presence_t p, byte *arg) {
if(arg)
act(p, _("bespringt %s."), arg);
else
say(p, _("whee!"));
}
static void f00f_BESPRING(presence_t p, byte *arg) {
act(p, _("bespringt %s."), arg ?: p.nick->name);
}
void f00f_PGINFO(presence_t p, byte *arg) {
byte *b;
b = pg_info();
if(b) {
say(p, "PostgreSQL: %s", b);
free(b);
}
}
static const byte * const days[] = {
"Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle",
"Setting Orange"
};
static const byte * const seasons[] = {
"Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
};
static void f00f_DTIME(presence_t p, byte *arg) {
struct tm grego;
time_t now;
struct disc_time dt;
time(&now);
localtime_r(&now, &grego);
makeday(grego.tm_mon + 1, grego.tm_mday, grego.tm_year+1900, &dt);
say(p, "Today is %s, the %d%s day of %s in the YOLD %d.",
days[dt.yday % 5], dt.day+1, ending(dt.day+1),
seasons[dt.season], dt.year);
}
static void f00f_KOP(presence_t p, byte *arg) {
appreciate(p, arg, "kop");
}
static void f00f_MUNT(presence_t p, byte *arg) {
appreciate(p, arg, "munt");
}
static void f00f_KIES(presence_t p, byte *arg) {
int argc;
char **argv;
if(!arg)
return (void)appreciate(p, arg, "fles");
matchsplit(arg, &argc, &argv);
act(p, _("kiest %s."), argv[frandom(0, argc)]);
}
static void f00f_MOO(presence_t p, byte *arg) {
say(p, "%s", (p.nick->user && !strcmp(p.nick->user->name, "zarq"))
?
" / / / /\n"
" /_/__/__/_\n"
" / / < oO><^^ > \\\n"
" /_/____|_| |_| |\n"
" <· ·> // // |.\n"
" |_|_ ___ | /\\___/\n"
" |||| ||||| ||/"
:
" (__)\n"
" (oo)\n"
" /------\\/\n"
" / | ||\n"
" * /\\---/\\\n"
" ~~ ~~\n"
"....\"Have you mooed today?\"...");
}
void grep_FLUXI(presence_t p, int argc, byte **argv) {
static struct timeval prev = {0, 0};
struct timeval now;
gettimeofday(&now, NULL);
if(now.tv_sec - prev.tv_sec < 600)
return;
ventriloquate("Fluxi", p, "hu tie\002\002ten ???");
prev = now;
}
void grep_ARR(presence_t p, int argc, byte **argv) {
static struct timeval prev = {0, 0};
struct timeval now;
gettimeofday(&now, NULL);
if(now.tv_sec - prev.tv_sec < 60)
return;
say(p, "ar\002\002rhar\n");
prev = now;
}
void grep_OESP(presence_t p, int argc, byte **argv) {
static struct timeval prev = {0, 0};
struct timeval now;
gettimeofday(&now, NULL);
if(now.tv_sec - prev.tv_sec < 60)
return;
say(p, "kan\002\002sloos!\n");
prev = now;
}
void grep_SIGHTING(presence_t p, int argc, byte **argv) {
static struct timeval next = {0, 0};
struct timeval now;
if(argc < 2)
return;
gettimeofday(&now, NULL);
if(now.tv_sec > next.tv_sec && !frandom(0, 20)) {
say(p, "^^^ %s sighting", argv[1]);
/* Volgende keer over 10 minuten. */
next.tv_sec = now.tv_sec + 600;
}
}
void grep_WHITESPACE(presence_t p, byte *arg) {
if(!strncasecmp(p.nick->name, "LarstiQ", 7))
say(p, _("%s: whitespace before punctuation mark"), p.nick->name);
}
void grep_BUG(presence_t p, byte *arg) {
static struct timeval next = {0, 0};
struct timeval now;
gettimeofday(&now, NULL);
if(now.tv_sec > next.tv_sec && !frandom(0, 20)) {
appreciate(p, arg, "bug");
/* Volgende keer over 10 minuten. */
next.tv_sec = now.tv_sec + 600;
}
}
void grep_URL(presence_t p, int argc, byte **argv) {
pg_trans_t *pg = NULL;
PGresult *res = NULL;
if(!argc || !p.chan)
return;
pg = pg_begin();
if(!pg)
return;
res = pg_query(pg, "INSERT INTO urls (url, nick, channel) VALUES ('%?', '%?', channelregister('%?', '%?'))",
argv[0], p.nick->name, p.chan->name, f00finstance);
if(res)
return (void)pg_commit(pg);
pg_rollback(pg);
}
static void f00f_POM(presence_t p, byte *arg) {
echt_t duur;
if(arg) {
duur = bereken(arg);
if(isfout(duur) || !zelfdedimensie(duur, echtseconde) || duur.echt <= 0 || !finite(duur.echt))
return (void)lart(p);
sayq(p, calc_pom(qnew(), duur.echt));
} else {
sayq(p, calc_pom(qnew(), 0));
}
}
static bool iso8601date(byte *w, time_t *now) {
byte *s, *when = w;
struct tm tm = {0};
if(!when || !*when)
return FALSE;
if(strlen(when) != 10)
return FALSE;
if(when[4] != '-')
return FALSE;
if(when[7] != '-')
return FALSE;
when[4] = when[7] = '\0';
tm.tm_year = strtoul(when, &s, 10) - 1900;
if(s != when+4)
return FALSE;
tm.tm_mon = strtoul(when+5, &s, 10);
if(s != when+7)
return FALSE;
tm.tm_mday = strtoul(when+8, &s, 10);
if(s != when+10)
return FALSE;
tm.tm_hour = 12;
*now = mktime(&tm);
if(*now == -1)
return FALSE;
return TRUE;
}
static void f00f_POS(presence_t p, byte *arg) {
time_t now = time(NULL);
echt_t duur;
if(arg) {
duur = bereken(arg);
if(!iso8601date(arg, &now)) {
if(isfout(duur) || !zelfdedimensie(duur, echtseconde) || duur.echt <= 0 || !finite(duur.echt))
return (void)lart(p);
now = (time_t)duur.echt;
}
}
sayq(p, calc_pos(qnew(), now));
}
extern void f00f_huhhuh(presence_t p, byte *arg);
static void f00f_RFC(presence_t p, byte *arg) {
byte s[8];
int n;
if(!arg || (n = atoi(arg)) <= 0 || n > 9999) {
lart(p);
} else {
snprintf(s, sizeof s, "RFC%04d", n);
f00f_huhhuh(p, s);
}
}
static void f00f_TOPIC(presence_t p, byte *arg) {
if(!p.chan)
return;
if(!arg) {
if(p.chan->topic)
say(p, _("Het onderwerp van dit kanaal is: %s"), p.chan->topic);
else
say(p, _("Er is geen onderwerp voor dit kanaal ingesteld."));
} else {
if(strcmp(arg, "-"))
topic(p, "%s", arg);
else
topic(p, "%s", "");
}
}
static void f00f_ADDTOPIC(presence_t p, byte *arg) {
if(!arg || !p.chan)
return;
if(p.chan->topic)
topic(p, "%s || %s", strdupa(p.chan->topic), arg);
else
topic(p, "%s", arg);
}
static void f00f_DELTOPIC(presence_t p, byte *arg) {
byte *s, *e, *f, *t;
if(!arg || !p.chan->topic)
return;
s = t = strdupa(p.chan->topic);
for(;;) {
e = strstr(s, " || ");
f = strstr(s, arg);
if(!f)
break;
if(!e) {
if(s == t) {
topic(p, "%s", "");
} else {
s[-4] = '\0';
topic(p, "%s", t);
}
break;
}
if(f < e) {
memmove(s, e + 4, strlen(e) - 3);
topic(p, "%s", t);
break;
}
s = e + 4;
}
}
static void f00f_CHANGETOPIC(presence_t p, byte *arg) {
byte *s, *e, *f, *t, *n;
if(!arg || !p.chan->topic)
return;
n = match(arg);
if(!n)
return;
s = t = strdupa(p.chan->topic);
for(;;) {
e = strstr(s, " || ") ?: (t + strlen(t));
f = strstr(s, arg);
if(!f)
break;
if(f < e) {
*s = '\0';
topic(p, "%s%s%s", t, n, e);
break;
}
s = e + 4;
}
}
static void f00f_APPENDTOPIC(presence_t p, byte *arg) {
byte *s, *e, *f, *t, *n, x;
if(!arg || !p.chan)
return;
n = match(arg);
if(!n)
return;
s = t = strdupa(p.chan->topic);
for(;;) {
e = strstr(s, " || ") ?: (t + strlen(t));
f = strstr(s, arg);
if(!f)
break;
if(f < e) {
x = *(e - 1);
*(e - 1) = '\0';
topic(p, "%s%c %s%s", t, x, n, e);
break;
}
s = e + 4;
}
}
real unix_to_jd(int sec, int usec) {
real jd;
jd = usec;
jd /= 1000000.0;
jd += sec;
jd /= 86400.0;
jd += 2440587.5;
return jd;
}
real cal_to_jd(presence_t p, int year, int month, int day, int hour, int min, int sec) {
real jy, ja, jm;
real gregcal, dayfrac, frac, jd0, jd;
long intgr;
if(year == 0) {
say(p, _("Jaar 0 bestaat niet!"));
return FP_NAN;
}
if(year == 1582 && month == 10 && day > 4 && day < 15) {
say(p, _("5 t/m 14 oktober 1582 bestaan niet in dit systeem!"));
return FP_NAN;
}
if(month > 2) {
jy = year;
jm = month + 1;
} else {
jy = year - 1;
jm = month + 13;
}
intgr = floor(floor(365.25 * jy) + floor(30.6001 * jm) + day + 1720995);
/* check for switch to Gregorian calendar */
gregcal = 15 + 31 * (10 + 12 * 1582);
if(day + 31 * (month + 12 * year) >= gregcal) {
ja = floor(0.01 * jy);
intgr += 2 - ja + floor(0.25 * ja);
}
/* correct for half-day offset */
dayfrac = hour / 24.0 - 0.5;
if(dayfrac < 0.0) {
dayfrac += 1.0;
intgr--;
}
/* now set the fraction of a day */
frac = dayfrac + (min + sec / 60.0) / 1440.0;
/* round to nearest second */
jd0 = (intgr + frac)*100000;
jd = floor(jd0);
if(jd0 - jd > 0.5)
jd++;
return jd / 100000.0;
}
static void f00f_JDATE(presence_t p, byte *arg) {
real jdate;
struct timeval tv;
gettimeofday(&tv, NULL);
jdate = unix_to_jd(tv.tv_sec, tv.tv_usec);
if(jdate == FP_NAN)
lart(p);
else
say(p, "%Lg", jdate);
}
void grep_OI(presence_t p, byte *arg) {
pg_trans_t *pg;
if(!p.chan || !arg)
return;
pg = pg_begin();
if(!pg)
return;
if(pg_query(pg, "SELECT setsimple('oi', '%?', '%? at %?')",
p.chan->name, p.nick->name, arg))
pg_commit(pg);
else
pg_rollback(pg);
}
static void f00f_REOI(presence_t p, byte *arg) {
pg_trans_t *pg;
PGresult *res = NULL;
byte *s;
if(!p.chan)
return;
pg = pg_begin();
if(!pg)
return;
res = pg_query(pg, "SELECT getsimple('oi', '%?')", p.chan->name);
if(!res)
goto rollback;
s = pg_getstr(res, 0, 0);
if(!s)
goto rollback;
say(p, "Responding to broadcast-\002oi\002 from %s", s);
pg_commit(pg);
return;
rollback:
pg_rollback(pg);
}
static void f00f_LANGUAGE(presence_t p, byte *arg) {
if(!p.chan) {
if(arg) {
if(!checklanguage(arg)) {
lart(p);
return;
}
if(unc_readtowritelock(&p)) {
*planguage() = xreplace(&p.nick->language, arg);
say(p, _("Je taal is veranderd in %s, %s!"), p.nick->language, p.nick->name);
} unc_writetoreadlock(NULL);
} else {
if(p.nick->language)
say(p, _("Je taal is %s, %s."), p.nick->language, p.nick->name);
else
say(p, _("Je hebt geen taal ingesteld, %s."), p.nick->name);
}
} else {
if(arg) {
if(!checklanguage(arg)) {
lart(p);
return;
}
if(unc_readtowritelock(&p)) {
*planguage() = xreplace(&p.chan->language, arg);
say(p, _("De taal van het kanaal is veranderd in %s."), p.chan->language);
} unc_writetoreadlock(NULL);
} else {
if(p.chan->language)
say(p, _("De taal van het kanaal is %s."), p.chan->language);
else
say(p, _("Er is geen taal van het kanaal ingesteld."));
}
}
}
static void f00f_TRANSLATE(presence_t p, byte *arg) {
const byte *from, *to, *phrase, *trans;
int argc;
char **argv;
matchsplit(arg, &argc, &argv);
if(argc < 3 || argc > 4) {
lart(p);
return;
}
from = argv[0];
to = argv[1];
phrase = argv[2];
if(argc == 3) {
trans = translate(cformattostring(phrase), from, to);
if(!trans)
say(p, _("Ik ken geen vertaling van '%s' van %s naar %s."), phrase, from, to);
else
say(p, _("De vertaling van '%s' van %s naar %s is '%s'."), phrase, from, to, stringtocformat(trans));
} else {
p.chan = NULL;
trans = argv[3];
if(addtranslation(cformattostring(phrase), cformattostring(trans), from, to))
say(p, _("Vertaling van '%s' van %s naar %s is toegevoegd als '%s'."), phrase, from, to, trans);
else
say(p, _("Toevoegen van vertaling van '%s' van %s naar %s is mislukt."), phrase, from, to);
}
}
static void f00f_UNTRANSLATED(presence_t p, byte *arg) {
pg_trans_t *pg = NULL;
PGresult *res = NULL;
const byte *from, *to, *phrase;
unsigned int n;
int argc;
char **argv;
matchsplit(arg, &argc, &argv);
if(argc != 2) {
lart(p);
return;
}
from = argv[0];
to = argv[1];
if(!checklanguage(from) || !checklanguage(to)) {
lart(p);
return;
}
pg = pg_begin();
if(!pg)
return;
res = pg_query(pg, "SELECT COUNT(phrase) FROM translations tfrom WHERE"
" language = (SELECT num from languages WHERE language = lower('%?')) AND"
" (SELECT COUNT(phrase) FROM translations tto WHERE"
" language = (SELECT num FROM languages WHERE language = lower('%?')) AND"
" tfrom.phrase = tto.phrase) = 0", from, to);
if(!res || !PQntuples(res))
goto rollback;
n = pg_getint(res, 0, 0);
if(!n) {
say(p, _("Alles in %s is al vertaald naar %s!"), from, to);
} else {
n = frandom(0, n);
res = pg_query(pg, "SELECT tfrom.translation FROM translations tfrom WHERE"
" language = (SELECT num FROM languages WHERE language = lower('%?')) AND"
" (SELECT COUNT(phrase) FROM translations tto WHERE"
" language = (SELECT num FROM languages WHERE language = lower('%?')) AND"
" tfrom.phrase = tto.phrase) = 0 OFFSET %u LIMIT 1", from, to, n);
if(!res || !PQntuples(res))
goto rollback;
phrase = pg_getstr(res, 0, 0);
say(p, _("Nog niet vertaald van %s naar %s: '%s'"), from, to, stringtocformat(phrase));
}
pg_commit(pg);
return;
rollback:
pg_rollback(pg);
return;
}
static void f00f_BALANS(presence_t p, byte *arg) {
s64 balans;
pg_trans_t *pg;
PGresult *res = NULL;
pg = pg_begin();
if(!pg)
return;
res = pg_query(pg, "SELECT getsimple('kosmische balans')");
if(!res)
goto rollback;
balans = pg_getint(res, 0, 0);
if(balans < -10)
say(p, _("Het kosmische evenwicht is uit balans, er moet meer gebonkt worden!"));
else if(balans > 10)
say(p, _("Het kosmische evenwicht is uit balans, er moet meer geoift worden!"));
else if(balans > 0)
say(p, _("Er is een licht bonk-overschot, maar er dreigt geen direct gevaar voor de volksgezondheid."));
else if(balans < 0)
say(p, _("Er is een licht oif-overschot, maar er dreigt geen direct gevaar voor de volksgezondheid."));
else
say(p, _("Het kosmische evenwicht is perfect in balans!"));
pg_commit(pg);
return;
rollback:
pg_rollback(pg);
}
static void f00f_BALANCEER(presence_t p, byte *arg) {
s64 i, balans;
unsigned int n, r;
pg_trans_t *pg;
PGresult *res = NULL;
irc_nick_t *f, *k, *s;
pg = pg_begin();
if(!pg)
return;
res = pg_query(pg, "SELECT getsimple('kosmische balans')");
if(!res)
goto rollback;
balans = pg_getint(res, 0, 0);
pg_commit(pg);
if(balans < 0) {
for(i = 0; i > balans; i--) {
if(p.chan) {
f = getnick(f00fnick);
s = getnick(arg);
n = avl_count(&p.chan->nicks);
if(n) {
r = 0;
if(f && avl_search(&p.chan->nicks, f))
r++;
if(s && s != f && avl_search(&p.chan->nicks, s))
r++;
k = avl_at(&p.chan->nicks, frandom(0, n - r))->item;
if(r >= 1 && (k == f || k == s))
k = p.chan->nicks.tail->item;
if(r == 2 && (k == f || k == s))
k = p.chan->nicks.tail->prev->item;
arg = pstrdup(k->nick.item);
}
}
appreciate(p, arg, "bonk");
}
kosmische_balans(-balans);
} else if(balans > 0) {
for(i = 0; i < balans; i++)
say(p, "oif.");
kosmische_balans(-balans);
} else {
appreciate(p, arg, "balanceer");
}
pg = pg_begin();
if(!pg)
return;
res = pg_query(pg, "SELECT getsimple('kosmische balans')");
if(!res)
goto rollback;
balans = pg_getint(res, 0, 0);
pg_commit(pg);
if(balans) {
say(p, _("De balans is ondanks mijn verwoede pogingen nog niet hersteld!"));
act(p, _("zegt zijn lidmaatschap van het druidengilde op."));
}
return;
rollback:
pg_rollback(pg);
}
static void f00f_RAW(presence_t p, byte *arg) {
if(!arg)
return;
if(checkperms(p, "raw"))
iputs(arg);
}
static void f00f_GETCONFIG(presence_t p, byte *arg) {
config_t c = {arg, NULL, 0};
if(!arg)
return;
get_config(&c);
if(c.value) {
say(p, "%s = %s", c.option, c.value);
free(c.value);
} else {
say(p, _("%s is niet gezet."), c.option);
}
}
static void f00f_SETCONFIG(presence_t p, byte *arg) {
config_t c = {NULL, NULL, 0};
int argc;
char **argv;
matchsplit(arg, &argc, &argv);
if(argc != 2)
return;
c.option = argv[0];
c.value = argv[1];
set_config(&c);
if(c.lastupdate) {
say(p, "%s = %s", c.option, c.value);
} else {
say(p, _("%s kon niet gezet worden."), c.option);
}
}
commandtab_t const cmd_tricks[] = {
{"AAPJE", f00f_DUBYASPEAK},
{"ADDTOPIC", f00f_ADDTOPIC},
{"APPENDTOPIC", f00f_APPENDTOPIC},
{"APT", f00f_APT},
{"BALANCEER", f00f_BALANCEER},
{"BALANS", f00f_BALANS},
{"BEREKEN", f00f_BEREKEN},
{"BESPRING", f00f_BESPRING},
{"BUSH", f00f_DUBYASPEAK},
{"USA", f00f_DUBYASPEAK},
{"CALC", f00f_CALC},
{"CALCULATE", f00f_CALC},
{"CHANGETOPIC", f00f_CHANGETOPIC},
{"CHTOPIC", f00f_CHANGETOPIC},
{"CIDR", f00f_CIDR},
{"DATE", f00f_TIME},
{"DATUM", f00f_TIME},
{"DEBUG", f00f_DEBUG},
{"DEHOP", f00f_DEHOP},
{"DELTOPIC", f00f_DELTOPIC},
{"DEOP", f00f_DEOP},
{"DEVOICE", f00f_DEVOICE},
{"DPKG", f00f_DPKG},
{"DTIME", f00f_DTIME},
{"DUBYASPEAK", f00f_DUBYASPEAK},
{"FOAD", f00f_OPZOUTEN},
{"GETCONF", f00f_GETCONFIG},
{"GETCONFIG", f00f_GETCONFIG},
{"GOOGLE", f00f_GOOGLE},
{"GOOGLEFIGHT", f00f_GOOGLEFIGHT},
{"HOP", f00f_HOP},
{"JDATE", f00f_JDATE},
{"JUMP", f00f_JUMP},
{"KARMA", f00f_KARMA},
{"KIES", f00f_KIES},
{"KOP", f00f_KOP},
{"LANGUAGE", f00f_LANGUAGE},
{"LANGUE", f00f_LANGUAGE},
{"LENGUA", f00f_LANGUAGE},
{"LINGUA", f00f_LANGUAGE},
{"LOADAVG", f00f_LOADAVG},
{"MEMINFO", f00f_MEMINFO},
{"MOO", f00f_MOO},
{"MUNT", f00f_MUNT},
{"NEER", f00f_DEOP},
{"ONTKEVER", f00f_DEBUG},
{"ONTSTEM", f00f_DEVOICE},
{"ONVERTAALD", f00f_UNTRANSLATED},
{"OP", f00f_OP},
{"OPZOUTEN", f00f_OPZOUTEN},
{"PGINFO", f00f_PGINFO},
{"POM", f00f_POM},
{"POOT", f00f_POOTJE},
{"POOTJE", f00f_POOTJE},
{"POS", f00f_POS},
{"RAW", f00f_RAW},
{"REKEN", f00f_BEREKEN},
{"REOI", f00f_REOI},
{"RFC", f00f_RFC},
{"SPRING", f00f_JUMP},
{"SROM", f00f_SUCKSRULESOMETER},
{"STFW", f00f_GOOGLE},
{"SETCONF", f00f_SETCONFIG},
{"SETCONFIG", f00f_SETCONFIG},
{"SPRACHE", f00f_LANGUAGE},
{"SUCKSRULESOMETER", f00f_SUCKSRULESOMETER},
{"TAAL", f00f_LANGUAGE},
{"TIJD", f00f_TIME},
{"TIME", f00f_TIME},
{"TOPIC", f00f_TOPIC},
{"UNTRANSLATED", f00f_UNTRANSLATED},
{"VERTAAL", f00f_TRANSLATE},
{"VOICE", f00f_VOICE},
{"WIJS", f00f_WIJS},
{"言語", f00f_LANGUAGE},
};
const int num_tricks = sizeof cmd_tricks / sizeof *cmd_tricks;