Initial commit
This commit is contained in:
commit
165a19da21
17
Makefile
Normal file
17
Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
SHELL = /bin/sh
|
||||
CC = clang
|
||||
CFLAGS = -ggdb -Wall -Wextra -std=c99
|
||||
LDFLAGS = `pkg-config --libs libssl`
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
targets = zyklonb
|
||||
|
||||
all: $(targets)
|
||||
|
||||
clean:
|
||||
rm -f $(targets)
|
||||
|
||||
zyklonb: src/zyklonb.c src/siphash.c
|
||||
$(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS)
|
||||
|
21
README
Normal file
21
README
Normal file
@ -0,0 +1,21 @@
|
||||
ZyklonB
|
||||
=======
|
||||
|
||||
`ZyklonB' is an experimental IRC bot, building upon the concept of my other
|
||||
VitaminA IRC bot. The main characteristic of these two bots is that they run
|
||||
plugins as coprocesses, which allows for enhanced reliability and programming
|
||||
language freedom.
|
||||
|
||||
While originally intended to be a simple C99 rewrite of the original bot, which
|
||||
was written in the GNU dialect of AWK, it fairly quickly became a playground
|
||||
where I added everything that seemed nice.
|
||||
|
||||
License
|
||||
-------
|
||||
`ZyklonB' is written by Přemysl Janouch <p.janouch@gmail.com>.
|
||||
|
||||
You may use the software under the terms of the ISC license, the text of which
|
||||
is included within the package, or, at your option, you may relicense the work
|
||||
under the MIT or the Modified BSD License, as listed at the following site:
|
||||
|
||||
http://www.gnu.org/licenses/license-list.html
|
312
plugins/eval
Executable file
312
plugins/eval
Executable file
@ -0,0 +1,312 @@
|
||||
#!/usr/bin/awk -f
|
||||
#
|
||||
# ZyklonB eval plugin, LISP-like expression evaluator
|
||||
#
|
||||
# Copyright 2013, 2014 Přemysl Janouch. All rights reserved.
|
||||
# See the file LICENSE for licensing information.
|
||||
#
|
||||
|
||||
BEGIN \
|
||||
{
|
||||
RS = "\r"
|
||||
ORS = "\r\n"
|
||||
IGNORECASE = 1
|
||||
srand()
|
||||
|
||||
prefix = get_config("prefix")
|
||||
|
||||
print "ZYKLONB register"
|
||||
fflush("")
|
||||
|
||||
# All functions have to be in this particular array
|
||||
min_args["int"] = 1
|
||||
min_args["+"] = 1
|
||||
min_args["-"] = 1
|
||||
min_args["*"] = 1
|
||||
min_args["/"] = 1
|
||||
min_args["%"] = 1
|
||||
min_args["^"] = 1
|
||||
min_args["**"] = 1
|
||||
min_args["exp"] = 1
|
||||
min_args["sin"] = 1
|
||||
min_args["cos"] = 1
|
||||
min_args["atan2"] = 2
|
||||
min_args["log"] = 1
|
||||
min_args["rand"] = 0
|
||||
min_args["sqrt"] = 1
|
||||
|
||||
min_args["pi"] = 0
|
||||
min_args["e"] = 0
|
||||
|
||||
min_args["min"] = 1
|
||||
min_args["max"] = 1
|
||||
|
||||
# Whereas here their presence is only optional
|
||||
max_args["int"] = 1
|
||||
max_args["sin"] = 1
|
||||
max_args["cos"] = 1
|
||||
max_args["atan2"] = 2
|
||||
max_args["log"] = 1
|
||||
max_args["rand"] = 0
|
||||
max_args["sqrt"] = 1
|
||||
|
||||
max_args["pi"] = 0
|
||||
max_args["e"] = 0
|
||||
}
|
||||
|
||||
{
|
||||
parse($0)
|
||||
}
|
||||
|
||||
msg_command == "PRIVMSG" \
|
||||
{
|
||||
# Context = either channel or user nickname
|
||||
match(msg_prefix, /^[^!]+/)
|
||||
ctx = substr(msg_prefix, RSTART, RLENGTH)
|
||||
if (msg_param[0] ~ /^[#&!+]/)
|
||||
{
|
||||
ctxquote = ctx ": "
|
||||
ctx = msg_param[0]
|
||||
}
|
||||
else
|
||||
ctxquote = ""
|
||||
|
||||
|
||||
if (substr(msg_param[1], 1, length(prefix)) == prefix)
|
||||
{
|
||||
keyword = "eval"
|
||||
text = substr(msg_param[1], 1 + length(prefix))
|
||||
if (match(text, "^" keyword "([^A-Za-z0-9].*|$)"))
|
||||
process_request(substr(text, 1 + length(keyword)))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
fflush("")
|
||||
}
|
||||
|
||||
function pmrespond (text)
|
||||
{
|
||||
print "PRIVMSG " ctx " :" ctxquote text
|
||||
}
|
||||
|
||||
function process_request (input, res, x)
|
||||
{
|
||||
delete funs
|
||||
delete accumulator
|
||||
delete n_args
|
||||
|
||||
res = ""
|
||||
fun_top = 0
|
||||
funs[0] = ""
|
||||
accumulator[0] = 0
|
||||
n_args[0] = 0
|
||||
|
||||
if (match(input, "^[ \t]*"))
|
||||
input = substr(input, RLENGTH + 1)
|
||||
if (input == "")
|
||||
res = "expression missing"
|
||||
|
||||
while (res == "" && input != "") {
|
||||
if (match(input, "^-?[0-9]+\\.?[0-9]*")) {
|
||||
x = substr(input, RSTART, RLENGTH)
|
||||
input = substr(input, RLENGTH + 1)
|
||||
|
||||
match(input, "^ *")
|
||||
input = substr(input, RLENGTH + 1)
|
||||
|
||||
res = process_argument(x)
|
||||
} else if (match(input, "^[(]([^ ()]+)")) {
|
||||
x = substr(input, RSTART + 1, RLENGTH - 1)
|
||||
input = substr(input, RLENGTH + 1)
|
||||
|
||||
match(input, "^ *")
|
||||
input = substr(input, RLENGTH + 1)
|
||||
|
||||
if (!(x in min_args)) {
|
||||
res = "undefined function '" x "'"
|
||||
} else {
|
||||
fun_top++
|
||||
funs[fun_top] = x
|
||||
accumulator[fun_top] = 636363
|
||||
n_args[fun_top] = 0
|
||||
}
|
||||
} else if (match(input, "^[)] *")) {
|
||||
input = substr(input, RLENGTH + 1)
|
||||
res = process_end()
|
||||
} else
|
||||
res = "invalid input at '" substr(input, 1, 10) "...'"
|
||||
}
|
||||
|
||||
if (res == "") {
|
||||
if (fun_top != 0)
|
||||
res = "unclosed '" funs[fun_top] "'"
|
||||
else if (n_args[0] != 1)
|
||||
res = "internal error, expected one result" \
|
||||
", got " n_args[0] " instead"
|
||||
}
|
||||
|
||||
if (res == "")
|
||||
pmrespond(accumulator[0])
|
||||
else
|
||||
pmrespond(res)
|
||||
}
|
||||
|
||||
function process_argument (arg)
|
||||
{
|
||||
if (fun_top == 0) {
|
||||
if (n_args[0]++ != 0)
|
||||
return "too many results, I only expect one"
|
||||
|
||||
accumulator[0] = arg
|
||||
return ""
|
||||
}
|
||||
|
||||
fun = funs[fun_top]
|
||||
if (fun in max_args && max_args[fun] <= n_args[fun_top])
|
||||
return "too many operands for " fun
|
||||
|
||||
if (fun == "int") {
|
||||
accumulator[fun_top] = int(arg)
|
||||
} else if (fun == "+") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else
|
||||
accumulator[fun_top] += arg
|
||||
} else if (fun == "-") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else
|
||||
accumulator[fun_top] -= arg
|
||||
} else if (fun == "*") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else
|
||||
accumulator[fun_top] *= arg
|
||||
} else if (fun == "/") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else if (arg == 0)
|
||||
return "division by zero"
|
||||
else
|
||||
accumulator[fun_top] /= arg
|
||||
} else if (fun == "%") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else if (arg == 0)
|
||||
return "division by zero"
|
||||
else
|
||||
accumulator[fun_top] %= arg
|
||||
} else if (fun == "^" || fun == "**" || fun == "exp") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else
|
||||
accumulator[fun_top] ^= arg
|
||||
} else if (fun == "sin") {
|
||||
accumulator[fun_top] = sin(arg)
|
||||
} else if (fun == "cos") {
|
||||
accumulator[fun_top] = cos(arg)
|
||||
} else if (fun == "atan2") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else
|
||||
accumulator[fun_top] = atan2(accumulator[fun_top], arg)
|
||||
} else if (fun == "log") {
|
||||
accumulator[fun_top] = log(arg)
|
||||
} else if (fun == "rand") {
|
||||
# Just for completeness, execution never gets here
|
||||
} else if (fun == "sqrt") {
|
||||
accumulator[fun_top] = sqrt(arg)
|
||||
} else if (fun == "min") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else if (accumulator[fun_top] > arg)
|
||||
accumulator[fun_top] = arg
|
||||
} else if (fun == "max") {
|
||||
if (n_args[fun_top] == 0)
|
||||
accumulator[fun_top] = arg
|
||||
else if (accumulator[fun_top] < arg)
|
||||
accumulator[fun_top] = arg
|
||||
} else
|
||||
return "internal error, unhandled operands for " fun
|
||||
|
||||
n_args[fun_top]++
|
||||
return ""
|
||||
}
|
||||
|
||||
function process_end ()
|
||||
{
|
||||
if (fun_top <= 0)
|
||||
return "extraneous ')'"
|
||||
|
||||
fun = funs[fun_top]
|
||||
if (!(fun in min_args))
|
||||
return "internal error, unhandled ')' for '" fun "'"
|
||||
if (min_args[fun] > n_args[fun_top])
|
||||
return "not enough operands for '" fun "'"
|
||||
|
||||
# There's no 'init' function to do it in
|
||||
if (fun == "rand")
|
||||
accumulator[fun_top] = rand()
|
||||
else if (fun == "pi")
|
||||
accumulator[fun_top] = 3.141592653589793
|
||||
else if (fun == "e")
|
||||
accumulator[fun_top] = 2.718281828459045
|
||||
|
||||
return process_argument(accumulator[fun_top--])
|
||||
}
|
||||
|
||||
function get_config (key)
|
||||
{
|
||||
print "ZYKLONB get_config :" key
|
||||
fflush("")
|
||||
|
||||
getline
|
||||
parse($0)
|
||||
return msg_param[0]
|
||||
}
|
||||
|
||||
function parse (line, s, n, id, token)
|
||||
{
|
||||
s = 1
|
||||
id = 0
|
||||
|
||||
# NAWK only uses the first character of RS
|
||||
if (line ~ /^\n/)
|
||||
line = substr(line, 2)
|
||||
|
||||
msg_prefix = ""
|
||||
msg_command = ""
|
||||
delete msg_param
|
||||
|
||||
n = match(substr(line, s), / |$/)
|
||||
while (n)
|
||||
{
|
||||
token = substr(line, s, n - 1)
|
||||
if (token ~ /^:/)
|
||||
{
|
||||
if (s == 1)
|
||||
msg_prefix = substr(token, 2)
|
||||
else
|
||||
{
|
||||
msg_param[id] = substr(line, s + 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
else if (!msg_command)
|
||||
msg_command = toupper(token)
|
||||
else
|
||||
msg_param[id++] = token
|
||||
|
||||
s = s + n
|
||||
n = index(substr(line, s), " ")
|
||||
|
||||
if (!n)
|
||||
{
|
||||
n = length(substr(line, s)) + 1
|
||||
if (n == 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
86
src/siphash.c
Normal file
86
src/siphash.c
Normal file
@ -0,0 +1,86 @@
|
||||
// Code taken from https://github.com/floodyberry/siphash with some edits.
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
||||
// and related and neighboring rights to this software to the public domain
|
||||
// worldwide. This software is distributed without any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
||||
// with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
#include "siphash.h"
|
||||
|
||||
static uint64_t inline
|
||||
u8to64_le (const unsigned char *p)
|
||||
{
|
||||
return
|
||||
(uint64_t) p[0] |
|
||||
(uint64_t) p[1] << 8 |
|
||||
(uint64_t) p[2] << 16 |
|
||||
(uint64_t) p[3] << 24 |
|
||||
(uint64_t) p[4] << 32 |
|
||||
(uint64_t) p[5] << 40 |
|
||||
(uint64_t) p[6] << 48 |
|
||||
(uint64_t) p[7] << 56 ;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
siphash (const unsigned char key[16], const unsigned char *m, size_t len)
|
||||
{
|
||||
uint64_t v0, v1, v2, v3;
|
||||
uint64_t mi, k0, k1;
|
||||
uint64_t last7;
|
||||
size_t i, blocks;
|
||||
|
||||
k0 = u8to64_le (key + 0);
|
||||
k1 = u8to64_le (key + 8);
|
||||
v0 = k0 ^ 0x736f6d6570736575ull;
|
||||
v1 = k1 ^ 0x646f72616e646f6dull;
|
||||
v2 = k0 ^ 0x6c7967656e657261ull;
|
||||
v3 = k1 ^ 0x7465646279746573ull;
|
||||
|
||||
last7 = (uint64_t) (len & 0xff) << 56;
|
||||
|
||||
#define ROTL64(a,b) (((a)<<(b))|((a)>>(64-b)))
|
||||
|
||||
#define COMPRESS \
|
||||
v0 += v1; v2 += v3; \
|
||||
v1 = ROTL64 (v1,13); v3 = ROTL64 (v3,16); \
|
||||
v1 ^= v0; v3 ^= v2; \
|
||||
v0 = ROTL64 (v0,32); \
|
||||
v2 += v1; v0 += v3; \
|
||||
v1 = ROTL64 (v1,17); v3 = ROTL64 (v3,21); \
|
||||
v1 ^= v2; v3 ^= v0; \
|
||||
v2 = ROTL64(v2,32);
|
||||
|
||||
for (i = 0, blocks = (len & ~7); i < blocks; i += 8) {
|
||||
mi = u8to64_le (m + i);
|
||||
v3 ^= mi;
|
||||
COMPRESS
|
||||
COMPRESS
|
||||
v0 ^= mi;
|
||||
}
|
||||
|
||||
switch (len - blocks)
|
||||
{
|
||||
case 7: last7 |= (uint64_t) m[i + 6] << 48;
|
||||
case 6: last7 |= (uint64_t) m[i + 5] << 40;
|
||||
case 5: last7 |= (uint64_t) m[i + 4] << 32;
|
||||
case 4: last7 |= (uint64_t) m[i + 3] << 24;
|
||||
case 3: last7 |= (uint64_t) m[i + 2] << 16;
|
||||
case 2: last7 |= (uint64_t) m[i + 1] << 8;
|
||||
case 1: last7 |= (uint64_t) m[i + 0] ;
|
||||
default:;
|
||||
};
|
||||
v3 ^= last7;
|
||||
COMPRESS
|
||||
COMPRESS
|
||||
v0 ^= last7;
|
||||
v2 ^= 0xff;
|
||||
COMPRESS
|
||||
COMPRESS
|
||||
COMPRESS
|
||||
COMPRESS
|
||||
|
||||
return v0 ^ v1 ^ v2 ^ v3;
|
||||
}
|
||||
|
10
src/siphash.h
Normal file
10
src/siphash.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef SIPHASH_H
|
||||
#define SIPHASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
uint64_t siphash (const unsigned char key[16],
|
||||
const unsigned char *m, size_t len);
|
||||
|
||||
#endif // SIPHASH_H
|
3320
src/zyklonb.c
Normal file
3320
src/zyklonb.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user