#ifdef RCS
static char rcsid[]="$Id: parse.c,v 1.1.1.1 2000/11/13 02:42:45 holsta Exp $";
#endif
/******************************************************************************
* Internetting Cooperating Programmers
* ----------------------------------------------------------------------------
*
* ____ PROJECT
* | _ \ __ _ _ __ ___ ___ _ __
* | | | |/ _` | '_ \ / __/ _ \ '__|
* | |_| | (_| | | | | (_| __/ |
* |____/ \__,_|_| |_|\___\___|_| the IRC bot
*
* All files in this archive are subject to the GNU General Public License.
*
* $Source: /cvsroot/dancer/dancer/src/parse.c,v $
* $Revision: 1.1.1.1 $
* $Date: 2000/11/13 02:42:45 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
/******************************************************************************
* Thoughts and theories...
Hi!
Here are some of my confused and mixed first plans and thougts around
first the aliases we've been discussing before, but then also a little
about how the future "extensions" of the bot could look like. Overkill
for a bot? naah... ;)
alias COMMAND REPLACEMENT - sets an alias
REPLACEMENT can be any sequence of
- plain text like in
alias commandlist cmdslist
- arguments from the entered sequence, specified with $<number> when making
the alias, like in:
alias fastban ban $1 10
Writing it like $2- means all arguments from the 2nd to the end of the
line, like in
alias msg do privmsg $1 : $2-
Writing it like $2? means number two if there is any number 2! Can also
be used together with - like in:
alias kick DO kick $channel $1 :$2?-
- generic 'variables' and inline information, specified with $<name> for
the info you'd like added at the place, like in:
alias topic do topic $channel :$1-
- $-letters for inclusion in the aliased command must be written with two
of them in a row, like in:
alias dollars say please send me some $$ now!!!
- built-in functionality for random picks, like:
alias killme $( ban $nick $| say no way, you're my friend $)
or more sofisticated:
alias saysome $( kick $nick $| $(say hi $| say no $) $)
lots of alternatives:
alias tryme $(ban $nick $| say one $| say two $| say three $)
- alias-controlled "new command" separator ($;), like in:
alias killme say my pleasure! $; ban $nick
Variables/info that might be interesting to offer:
$channel - Channel name of the bot
$nick - Nickname of the user
$bot - Nickname of the bot
$time - Bot time
$year
$date
We should here consider adding $-variables like:
* outputed text of a command (the slightly unix inspired) $'command'
* more "low level" variables
In an even more advanced level, I suggest full FPL support to enable
(at least) the bot maintainer to fix things like
alias -fpl mykick mykick(\" $1 \");
which builds an FPL program line that is run, the mykick() in the above
example line has of course already been defined in a file like:
export void mykick(char *nick)
{
if(Joined(nick))
Printf("%s: BOO!!", nick);
else
Printf("%s: you missed him! ;(", _nick); // built-in variable
}
#
# Advanced setup file for new commands/context control for Dancer.
#
# Syntax of this file is: 'FLAGS STRING(S) [ACTION]'
#
# FLAGS:
# C - straight command alias (used as normal commands)
# P - publically available (for commands)
# D - DCC chat required (for commands)
# * - password required (for commands)
# !0-9 - (in a sequence like !50 or !25) level required to trigger
# +0-9 - highest command level allowed to trigger (below level to trigger)
# S - string match (anyware on the line)
# E - regex match (anyware on the line)
# F - string match (first on the line)
# = - the rest of the line is treated as a help-text for the command/
# alias written on the previous line
# & - the rest of the line is treated as a syntax-description for the
# command/alias
#
# STRING(S)
# one or more strings according to the flags/actions wanted.
# white spaces can only be used if the string is placed within quotes as
# in "white space". Several strings are written like one,two,three.
#
# ACTION
# A complete Dancer command line. With $-style replacements as documented
# elsewhere.
#
C!20 topic do topic $channel :$1-
= Change topic on the currrent channel.
& <topic>
C!50 kick do kick $channel: $1 $2?-
= Kick the specified user from the channel. Optionally with a given kick message.
& <user> [message]
F!0 brb say Hurry back $user!
F!0 bbl say I'll be waiting for you, $user!
******************************************************************************/
#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "parse.h"
#include "list.h"
#include "function.h"
#include "transfer.h"
#include "command.h"
#define ALIAS_MAX_ARGS 16 /* Allocation steps */
#define ARG_TYPEMASK 0x0f
#define ARG_RESTOFLINE 0x10 /* from this parameter to the rest of line */
#define ARG_IFPOSSIBLE 0x20 /* if this parameter was specified */
typedef enum {
ARG_NONE,
ARG_PAR, /* parameter from the line when alias command is invoked */
ARG_VAR, /* program variable */
ARG_WORD, /* straight word */
ARG_PICKLIST, /* a list with a selection of different picks (pick) */
ARG_ENDOFCMD, /* push away the left as one separate command, another one
follows! ;) */
} aliasArguments;
typedef struct Alias {
struct Alias *next; /* in a "linked case" (pick) */
long amount; /* number of links in a "linked case" (pick) */
long reqargs; /* required amount of arguments required for the alias to run */
long args; /* args in the data list */
long *flags;
void **data;
long entries; /* number of array entries allocated */
} itemalias;
typedef struct FuncStruct {
struct Header h;
char *name; /* name of the new func-alias */
itemalias *alias;
long lowlevel; /* lowest required status */
long highlevel; /* highest required status */
int percent; /* likeliness 0 - 100 % */
long flags; /* mode stuff */
char *help; /* possible help text */
char *syntax; /* possible syntax description */
int id; /* unique id for the func-alias */
} itemfunc;
extern char *errfrom;
itemfunc *funcHead = NULL;
static int funcid = 0;
static char *PutAlias(itemalias *a, char *name, char *line, char *buffer, size_t buffer_size)
{
static char *argv[ALIAS_MAX_ARGS];
static int argc = 0;
int i, j;
snapshot;
buffer[0] = (char)0;
if (name && line) {
/* Split into arguments */
argc = 0;
argv[argc++] = name;
do {
while (iswhite(*line))
line++;
if ((char)0 == *line)
break;
if ('\"' == *line) {
argv[argc++] = ++line;
while (*line && ('\"' != *line))
line++;
}
else {
argv[argc++] = line;
while (isgraph(*line))
line++;
}
if ((argc < ALIAS_MAX_ARGS) && *line)
*line++ = (char)0;
} while (argc < ALIAS_MAX_ARGS);
}
if (argc <= a->reqargs)
return "not enough arguments";
for (i = 0; i < a->args; i++) {
switch (a->flags[i] & ARG_TYPEMASK) {
case ARG_PAR:
if (argc <= (int)a->data[i]) /* ARG_IFPOSSIBLE */
continue; /* for */
StrAppendMax(buffer, buffer_size, argv[(int)a->data[i]]);
if (a->flags[i] & ARG_RESTOFLINE) {
for (j = (int)a->data[i] + 1; j < argc; j++)
StrFormatAppendMax(buffer, buffer_size, " %s", argv[j]);
}
break;
case ARG_WORD:
StrAppendMax(buffer, buffer_size, (char *)a->data[i]);
break;
case ARG_ENDOFCMD:
StrAppendMax(buffer, buffer_size, "\n");
break;
case ARG_PICKLIST:
{
itemalias *alias = (itemalias *)a->data[i];
char *errptr;
int rnd;
if (alias->amount) {
rnd = (int)(Rnd() * (alias->amount + 1));
while (rnd-- && alias->next)
alias = alias->next;
}
j = StrLength(buffer);
if (errptr = PutAlias(alias, NULL, NULL, &buffer[j], buffer_size - j))
return errptr;
}
break;
case ARG_VAR:
{
char *str;
str = VarInfo((lineVariables)a->data[i]);
if (str)
StrAppendMax(buffer, buffer_size, str);
else
return "unknown variable";
}
break;
default:
break;
}
}
return NULL;
}
/* --- AliasAddArgument ------------------------------------------- */
char *AliasAddArgument(itemalias *a, void *data, aliasArguments flags)
{
snapshot;
if (a) {
if (a->entries <= a->args) {
void *newdata, *newflags;
newdata = realloc((void *)a->data, (a->entries + ALIAS_MAX_ARGS) * sizeof(void *));
if (newdata) {
newflags = realloc((void *)a->flags, (a->entries + ALIAS_MAX_ARGS) * sizeof(long));
if (newflags) {
a->data = (void **)newdata;
a->flags = (long *)newflags;
a->entries += ALIAS_MAX_ARGS;
}
else {
free(newdata);
}
}
if (a->entries <= a->args)
return "out of memory";
}
switch (flags) {
case ARG_WORD:
data = (void *)StrDuplicate((char *)data);
if (NULL == data)
return "out of memory";
break;
default:
break;
}
a->data[a->args] = data;
a->flags[a->args] = (long)flags;
a->args++;
}
return NULL; /* OK, or at least we assume so */
}
/* --- FreeAlias -------------------------------------------------- */
void FreeAlias(void *v)
{
int i;
itemalias *a, *next;
snapshot;
for (a = (itemalias *)v; a; a = next) {
next = a->next;
for (i = 0; i < a->args; i++) {
switch (a->flags[i] & ARG_TYPEMASK) {
case ARG_WORD:
StrFree((char *)a->data[i]);
break;
case ARG_PICKLIST:
FreeAlias(a->data[i]);
break;
default:
break;
}
}
if (a->data)
free((void *)a->data);
if (a->flags)
free((void *)a->flags);
free(a);
}
}
char *AliasAddFailed(itemalias *a, char *errptr)
{
snapshot;
if (a) {
FreeAlias(a);
}
return errptr;
}
char *AliasAdd(itemalias **alias, char **string)
{
char *ptr, *nextptr, *errptr;
itemalias *newone;
snapshot;
*alias = NULL;
newone = NewEntry(itemalias);
if (NULL == newone)
return "out of memory";
ptr = *string;
while (nextptr = StrIndex(ptr, '$')) {
if (nextptr == ptr) {
nextptr++;
switch (*nextptr) {
case '$':
if (errptr = AliasAddArgument(newone, "$", ARG_WORD))
return AliasAddFailed(newone, errptr);
ptr = nextptr + 1;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
long argnum;
aliasArguments arg = 0;
argnum = StrToLong(nextptr, &ptr, 0);
if ('?' == *ptr) {
arg |= ARG_IFPOSSIBLE;
ptr++;
}
else if (newone->reqargs < argnum) {
newone->reqargs = argnum;
}
if ('-' == *ptr) {
arg |= ARG_RESTOFLINE;
ptr++;
}
if (errptr = AliasAddArgument(newone, (void *)argnum, (arg | ARG_PAR)))
return AliasAddFailed(newone, errptr);
}
break;
case '(':
{
/* Random-pick list coming up, gee, big fun */
itemalias *alter;
itemalias *prev;
itemalias *first;
char c;
nextptr++;
if (errptr = AliasAdd(&first, &nextptr))
return AliasAddFailed(newone, errptr);
if (errptr = AliasAddArgument(newone, first, ARG_PICKLIST)) {
FreeAlias(first);
return AliasAddFailed(newone, errptr);
}
prev = first;
for (c = *nextptr++; c != ')'; c = *nextptr++) {
if ('|' == c) {
if (errptr = AliasAdd(&alter, &nextptr))
return AliasAddFailed(newone, errptr);
prev->next = alter;
prev = alter;
first->amount++;
}
else
return AliasAddFailed(newone, "illegal random-pick sequence");
}
ptr = nextptr;
}
break;
case '|':
case ')':
*alias = newone;
*string = nextptr;
return NULL;
case ';':
if (errptr = AliasAddArgument(newone, NULL, ARG_ENDOFCMD))
return AliasAddFailed(newone, errptr);
ptr = nextptr + 1;
while (iswhite(*ptr))
ptr++;
break;
default:
{
char name[MINIBUFFER];
lineVariables var = 0;
/* Variable name coming up! */
if (1 == StrScan(nextptr, "%"MINIBUFFERTXT"[a-zA-Z0-9]", name)) {
if (StrEqual(name, "CHANNEL"))
var = VAR_CHANNEL;
else if (StrEqual(name, "NICK"))
var = VAR_NICKNAME;
else if (StrEqual(name, "BOT"))
var = VAR_BOTNAME;
}
if (0 == var)
return AliasAddFailed(newone, "unknown variable");
if (errptr = AliasAddArgument(newone, (void *)var, ARG_VAR))
return AliasAddFailed(newone, errptr);
ptr = nextptr + StrLength(name);
break;
}
}
}
else {
ptr = StrDuplicateMax(ptr, nextptr - ptr + 1);
if (ptr) {
if (errptr = AliasAddArgument(newone, ptr, ARG_WORD))
return AliasAddFailed(newone, errptr);
StrFree(ptr);
}
ptr = nextptr;
}
}
if (*ptr) {
if (errptr = AliasAddArgument(newone, ptr, ARG_WORD))
return AliasAddFailed(newone, errptr);
}
*alias = newone;
return NULL;
}
/* --- FuncRun ---------------------------------------------------- */
char *FuncRun(char *from, /* from who it came */
char *cmd, /* actual command name */
char *param, /* parameters */
long status, /* status of the user using this */
long mode, /* flags about this */
char oktorun) /* no level checks */
{
static char buffer[KILOBUFFER];
char *errptr;
itemfunc *f;
snapshot;
for (f = First(funcHead); f; f = Next(f)) {
if ((oktorun || (f->lowlevel <= status)) && StrEqual(f->name, cmd)) {
if (!oktorun) {
if ((0 == (f->flags & FUNC_PUBLIC)) && (mode & FUNC_PUBLIC)) {
Send(errfrom, GetText(msg_cant_use_in_public));
break;
}
if ((f->flags & FUNC_DCC) && (0 == (mode & FUNC_DCC))) {
Send(errfrom, GetText(msg_use_chat));
break;
}
if ((f->flags & FUNC_PWD) && (0 == (mode & FUNC_PWD))) {
Send(errfrom, GetText(msg_use_pass_first));
break;
}
}
errptr = PutAlias(f->alias, cmd, param, buffer, sizeof(buffer));
if (errptr) {
Send(errfrom, errptr);
return NULL;
}
return buffer; /* This will be run as a standard command line */
}
}
return NULL;
}
/* --- FuncMatch -------------------------------------------------- */
char *FuncMatch(char *from, /* from who it came */
char *line, /* text line */
long status, /* status of the user using this */
long mode) /* flags about this */
{
static char buffer[KILOBUFFER];
char *errptr;
itemfunc *f;
snapshot;
for (f = First(funcHead); f; f = Next(f)) {
if (f->flags & (FUNC_MATCH | FUNC_REGEX | FUNC_STRINGFIRST)) {
if ((f->lowlevel <= status) && (status <= f->highlevel)) {
if (f->flags & FUNC_MATCH) {
if (!StrMatch(line, f->name))
continue;
}
else if (f->flags & FUNC_REGEX) {
if (!PatternExist(line, f->name))
continue;
}
else { /* (f->flags & FUNC_STRINGFIRST) */
if (!StrEqualMax(line, StrLength(f->name), f->name))
continue;
}
if (f->percent < 100) {
/* Not to be done always */
if (f->percent <= (int)(Rnd() * 100))
continue;
}
errptr = PutAlias(f->alias, f->name, line, buffer, sizeof(buffer));
if (errptr) {
Send(errfrom, errptr);
return NULL;
}
return buffer; /* This will be run as a standard command line */
}
}
}
return NULL;
}
/* --- FuncAddHelp ------------------------------------------------ */
void FuncAddHelp(itemfunc *f, char *line)
{
char help[BIGBUFFER];
snapshot;
if (1 == StrScan(line, "%"BIGBUFFERTXT"[^\n]", help)) {
if (f->help)
StrFree(f->help);
f->help = StrDuplicate(help);
}
}
/* --- FuncAddSyntax ---------------------------------------------- */
void FuncAddSyntax(itemfunc *f, char *line)
{
char syntax[BIGBUFFER];
snapshot;
if (1 == StrScan(line, "%"BIGBUFFERTXT"[^\n]", syntax)) {
if (f->syntax)
StrFree(f->syntax);
f->syntax = StrDuplicate(syntax);
}
}
/* --- FuncAddItem ------------------------------------------------ */
itemfunc *FuncAddItem(char *line)
{
char flagbuffer[MINIBUFFER], namebuffer[MIDBUFFER], aliasbuffer[KILOBUFFER];
char *pointer, *errptr;
long flags = 0;
long low = 0;
long high = 999999999;
int percent = 100;
itemalias *a;
itemfunc *f;
snapshot;
if ((3 == StrScan(line, "%"MINIBUFFERTXT"s \"%"MIDBUFFERTXT"[^\"]\" %"KILOBUFFERTXT"[^\n]",
flagbuffer, namebuffer, aliasbuffer)) ||
(3 == StrScan(line, "%"MINIBUFFERTXT"s %"MIDBUFFERTXT"s %"KILOBUFFERTXT"[^\n]",
flagbuffer, namebuffer, aliasbuffer))) {
pointer = flagbuffer;
while (*pointer) {
switch (toupper(*pointer++)) {
case 'C':
flags |= FUNC_REGULAR;
break;
case 'P':
flags |= FUNC_PUBLIC;
break;
case 'D':
flags |= FUNC_DCC;
break;
case '*':
flags |= FUNC_PWD;
break;
case '!':
flags |= FUNC_LOWLEVEL;
low = StrToLong(pointer, &pointer, 0);
break;
case '+':
flags |= FUNC_HIGHLEVEL;
high = StrToLong(pointer, &pointer, 0);
break;
case '%':
percent = StrToLong(pointer, &pointer, 0);
break;
case 'S':
flags |= FUNC_MATCH;
break;
case 'E':
flags |= FUNC_REGEX;
break;
case 'F':
flags |= FUNC_STRINGFIRST;
break;
default:
break;
}
}
pointer = aliasbuffer;
errptr = AliasAdd(&a, &pointer);
if (errptr) {
StrScan(line, "%"BIGBUFFERTXT"[^\n]", aliasbuffer);
Logf(LOGINIT, "Func AliasAdd() error: %s\nLINE: %s", errptr, aliasbuffer);
return NULL;
}
f = NewEntry(itemfunc);
if (f) {
f->name = StrDuplicate(namebuffer);
if (f->name) {
InsertLast(funcHead, f);
f->alias = a;
f->lowlevel = low;
f->highlevel = high;
f->percent = percent;
f->flags = flags;
f->id = ++funcid;
return f;
}
free(f);
}
FreeAlias(a);
}
return NULL;
}
/* --- FuncLoad --------------------------------------------------- */
bool FuncLoad(char *filename)
{
char line[2048];
itemfunc *f = NULL;
FILE *file;
snapshot;
if ((NULL == funcHead) || (NULL == filename) || ((char)0 == filename[0]))
return FALSE;
file = fopen(filename, "r");
if (file) {
while (fgets(line, sizeof(line), file)) {
switch (line[0]) {
case '#':
case '\n':
break;
case '=':
if (f)
FuncAddHelp(f, line + 1);
break;
case '&':
if (f)
FuncAddSyntax(f, line + 1);
break;
default:
f = FuncAddItem(line);
break;
}
}
fclose(file);
return TRUE;
}
return FALSE;
}
/* --- FuncInit --------------------------------------------------- */
void FuncInit(void)
{
extern char funcfile[];
snapshot;
funcHead = NewList(itemfunc);
FuncLoad(funcfile);
}
void FreeFunc(void *v)
{
itemfunc *f;
snapshot;
f = (itemfunc *)v;
if (f) {
if (f->name)
StrFree(f->name);
if (f->help)
StrFree(f->help);
if (f->syntax)
StrFree(f->syntax);
if (f->alias)
FreeAlias(f->alias);
}
}
/* --- FuncCleanup ------------------------------------------------ */
void FuncCleanup(void)
{
snapshot;
DeleteList(funcHead, FreeFunc);
}
/* --- FuncReload ------------------------------------------------- */
void FuncReload(char *filename)
{
snapshot;
FlushList(funcHead, FreeFunc);
FuncLoad(filename);
}
#ifdef FUNCS_HACK
/* --- FuncAdd ---------------------------------------------------- */
void FuncAdd(char *from, char *line)
{
itemfunc *f;
snapshot;
f = FuncAddItem(line);
if (f) {
Sendf(from, "Added new func \"%s\"", f->name);
}
else
CmdSyntax(errfrom, "FUNCADD");
}
/* --- FuncDel ---------------------------------------------------- */
void FuncDel(char *from, char *line)
{
char *ptr;
bool deleted = FALSE;
int number;
itemfunc *f, *next;
snapshot;
ptr = NextQuoteWord(line);
if (ptr && *ptr) {
number = atoi(ptr);
for (f = First(funcHead); f; f = next) {
next = Next(f);
if ((number && (f->id == number)) || StrMatch(f->name, ptr)) {
Sendf(from, "%d. \"%s\" removed", f->id, f->name);
DeleteEntry(funcHead, f, FreeFunc);
deleted = TRUE;
}
}
if (!deleted)
Sendf(from, "Func \"%s\" not found for deletion", ptr);
}
else
CmdSyntax(errfrom, "FUNCDEL");
}
/* --- FuncList --------------------------------------------------- */
void FuncList(char *from, char *line)
{
itemfunc *f;
snapshot;
if (First(funcHead)) {
for (f = First(funcHead); f; f = Next(f))
Sendf(from, "%d. \"%s\"", f->id, f->name);
}
else {
Send(from, "No funcs to list");
}
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1