/*
* ratExp.c --
*
* This file handles the expressions.
*
* TkRat software and its included text is Copyright 1996-2002 by
* Martin Forssén
*
* The full text of my legal notices is contained in the file called
* COPYRIGHT, included with this distribution.
*/
#include "ratFolder.h"
/*
* Private types
*/
typedef enum {TT_Field, TT_Operator, TT_Boolean,TT_Grouping,TT_Spec} TokenType;
typedef enum {T_To, T_From, T_Subject, T_Sender, T_Cc, T_Reply_To, T_Size,
T_Has, T_Is, T_Gt, T_Lt,
T_And, T_Or, T_Not,
T_Lparen, T_Rparen} Token;
typedef struct {
Token token;
TokenType type;
char *string;
RatFolderInfoType info;
} TokenList;
typedef struct Expression {
int negate;
Token op;
union {
struct Expression *expPtr;
RatFolderInfoType info;
} arg1;
union {
struct Expression *expPtr;
char *string;
} arg2;
} Expression;
typedef struct ExpList {
int id;
Expression *expPtr;
struct ExpList *next;
} ExpList;
/*
* Static data
*/
static int expCounter = 0;
static ExpList *expListPtr = NULL;
static TokenList tokenList[] = {
{T_To, TT_Field, "to", RAT_FOLDER_TO},
{T_From, TT_Field, "from", RAT_FOLDER_FROM},
{T_Subject, TT_Field, "subject", RAT_FOLDER_SUBJECT},
{T_Sender, TT_Field, "sender", RAT_FOLDER_SENDER},
{T_Cc, TT_Field, "cc", RAT_FOLDER_CC},
{T_Reply_To,TT_Field, "reply-to", RAT_FOLDER_REPLY_TO},
{T_Size, TT_Field, "size", RAT_FOLDER_SIZE},
{T_Has, TT_Operator, "has", 0},
{T_Is, TT_Operator, "is", 0},
{T_Gt, TT_Operator, ">", 0},
{T_Lt, TT_Operator, "<", 0},
{T_And, TT_Boolean, "and", 0},
{T_Or, TT_Boolean, "or", 0},
{T_Not, TT_Spec, "not", 0},
{T_Lparen, TT_Grouping, "(", 0},
{T_Rparen, TT_Grouping, ")", 0},
{0, 0, NULL, 0}
};
/*
* Local functions
*/
static TokenList *GetToken(char **sPtr);
static char *GetString(char **sPtr);
static void FreeExp(Expression *expPtr);
static Expression *ParseExpression(char **sPtr, char **errPtr, int inParen);
static void GetExpression(Tcl_Interp *interp, Tcl_Obj *ePtr,
Expression *expPtr);
static int RatExpMatchDo(Tcl_Interp *interp, Expression *expPtr,
RatInfoProc *infoProc, ClientData clientData, int index);
/*
*----------------------------------------------------------------------
*
* GetToken --
*
* Extract the next token from the string.
* to the interpreter.
*
* Results:
* A pointer to a TokenList entity or nULL if no valid token is
* found
*
* Side effects:
* *sPtr will most probably be modified.
*
*
*----------------------------------------------------------------------
*/
static TokenList*
GetToken(char **sPtr)
{
char *cPtr = *sPtr;
int i;
while (isspace((unsigned char)*cPtr)) {
cPtr++;
}
*sPtr = cPtr;
if (!*cPtr) {
return NULL;
}
for (i=0; tokenList[i].string; i++) {
if (!strncasecmp(cPtr, tokenList[i].string,
strlen(tokenList[i].string))) {
cPtr += strlen(tokenList[i].string);
*sPtr = cPtr;
return &tokenList[i];
}
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* GetString --
*
* Extract the next string.
*
* Results:
* A pointer to a copy of the found string. It is the callers
* resposibility to eventually free this area.
*
* Side effects:
* *sPtr will most probably be modified.
*
*
*----------------------------------------------------------------------
*/
static char*
GetString(char **sPtr)
{
char quote = '\0', *cPtr = *sPtr, *result;
int i;
while (isspace((unsigned char)*cPtr)) {
cPtr++;
}
if ('\'' == *cPtr || '"' == *cPtr || '{' == *cPtr) {
quote = *cPtr++;
}
*sPtr = cPtr;
if ('{' == quote) {
quote = '}';
}
result = (char*)ckalloc(strlen(cPtr)+1);
i=0;
while (*cPtr && !(quote == *cPtr
|| (!quote && isspace((unsigned char)*cPtr)))) {
if ('\\' == *cPtr && cPtr[1]) {
cPtr++;
}
if (isupper((unsigned char)*cPtr)) {
result[i] = tolower((unsigned char)*cPtr);
} else {
result[i] = *cPtr;
}
i++;
cPtr++;
}
result[i] = '\0';
if (quote && quote == *cPtr) {
cPtr++;
}
*sPtr = cPtr;
return result;
}
/*
*----------------------------------------------------------------------
*
* FreeExp --
*
* Free an expression
*
* Results:
* None
*
* Side effects:
* The given expression will be free'ed.
*
*
*----------------------------------------------------------------------
*/
static void
FreeExp(Expression *expPtr)
{
if (!expPtr) {
return;
}
if (expPtr->op == T_And || expPtr->op == T_Or) {
FreeExp(expPtr->arg1.expPtr);
FreeExp(expPtr->arg2.expPtr);
} else {
ckfree(expPtr->arg2.string);
}
ckfree(expPtr);
}
/*
*----------------------------------------------------------------------
*
* ParseExpression --
*
* Parse a given search expression
*
* Results:
* A pointer to an expression, or NULL if an error was encountered.
*
* Side effects:
* *sPtr will most probably be modified.
*
*
*----------------------------------------------------------------------
*/
static Expression*
ParseExpression(char **sPtr, char **errPtr, int inParen)
{
TokenList *tokPtr;
Expression *expPtr = NULL, *exp2Ptr;
char *newString;
int negated, l;
while (**sPtr) {
negated = 0;
while (tokPtr = GetToken(sPtr), (tokPtr && tokPtr->token == T_Not)) {
negated = negated ? 0 : 1;
}
if (!tokPtr) {
if (**sPtr) {
*errPtr = "Unparseable text";
}
return expPtr;
}
switch (tokPtr->type) {
case TT_Field:
if (expPtr && expPtr->op != T_And && expPtr->op != T_Or) {
*errPtr = "Expected boolean or ')'";
return expPtr;
}
exp2Ptr = (Expression*)ckalloc(sizeof(Expression));
exp2Ptr->negate = negated;
exp2Ptr->arg1.info = tokPtr->info;
tokPtr = GetToken(sPtr);
if (!tokPtr || tokPtr->type != TT_Operator) {
*errPtr = "Expected operator";
ckfree(exp2Ptr);
return expPtr;
}
exp2Ptr->op = tokPtr->token;
exp2Ptr->arg2.string = GetString(sPtr);
if (!exp2Ptr->arg2.string) {
*errPtr = "String expected";
ckfree(exp2Ptr);
return expPtr;
} else if (T_Is == exp2Ptr->op) {
exp2Ptr->op = T_Has;
l = strlen(exp2Ptr->arg2.string)+3;
newString = (char*)ckalloc(l);
strlcpy(newString, "^", l);
strlcat(newString, exp2Ptr->arg2.string, l);
strlcat(newString, "$", l);
ckfree(exp2Ptr->arg2.string);
exp2Ptr->arg2.string = newString;
}
if (expPtr) {
expPtr->arg2.expPtr = exp2Ptr;
} else {
expPtr = exp2Ptr;
}
break;
case TT_Boolean:
if (!expPtr) {
*errPtr = "Must have a valid expression before a boolean";
return expPtr;
}
exp2Ptr = (Expression*)ckalloc(sizeof(Expression));
exp2Ptr->negate = negated;
exp2Ptr->op = tokPtr->token;
exp2Ptr->arg1.expPtr = expPtr;
exp2Ptr->arg2.expPtr = NULL;
expPtr = exp2Ptr;
break;
case TT_Grouping:
if (T_Lparen == tokPtr->token) {
if (expPtr && expPtr->op != T_And && expPtr->op != T_Or) {
*errPtr = "Expected boolean, field or ')'";
return expPtr;
}
exp2Ptr = ParseExpression(sPtr, errPtr, 1);
if (expPtr) {
expPtr->arg2.expPtr = exp2Ptr;
} else {
expPtr = exp2Ptr;
}
if (*errPtr) {
return expPtr;
}
} else {
if (!inParen) {
*errPtr = "Unexpected ')'.";
return expPtr;
}
return expPtr;
}
break;
case TT_Operator: /* fallthrough */
case TT_Spec:
*errPtr = "Expected field or (";
return expPtr;
}
}
if (!expPtr) {
*errPtr = "Empty expression";
return NULL;
}
return expPtr;
}
/*
*----------------------------------------------------------------------
*
* RatParseExp --
*
* See ../doc/interface
*
* Results:
* A standard tcl result.
*
* Side effects:
* Will probably add an expression to the local list.
*
*
*----------------------------------------------------------------------
*/
int
RatParseExpCmd(ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[])
{
char *error = NULL, *cPtr, *exp;
Expression *expPtr;
ExpList *elemPtr;
if (objc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetString(objv[0]), " expression\"",
(char *) NULL);
return TCL_ERROR;
}
exp = cPtr = Tcl_GetString(objv[1]);
expPtr = ParseExpression(&cPtr, &error, 0);
if (error) {
char buf[32];
FreeExp(expPtr);
sprintf(buf, "%d", cPtr-exp);
Tcl_AppendElement(interp, buf);
Tcl_AppendElement(interp, error);
return TCL_ERROR;
}
elemPtr = (ExpList*)ckalloc(sizeof(ExpList));
elemPtr->id = expCounter;
elemPtr->expPtr = expPtr;
elemPtr->next = expListPtr;
expListPtr = elemPtr;
Tcl_SetObjResult(interp, Tcl_NewIntObj(expCounter++));
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GetExp --
*
* Print one expression into the given DString.
*
* Results:
* The given DString will be modified.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static void
GetExpression(Tcl_Interp *interp, Tcl_Obj *ePtr, Expression *expPtr)
{
int opIndex, fIndex;
Tcl_Obj *oPtr;
for (opIndex=0; tokenList[opIndex].token != expPtr->op; opIndex++);
if (expPtr->negate) {
Tcl_ListObjAppendElement(interp, ePtr, Tcl_NewStringObj("not", 3));
}
if (tokenList[opIndex].type == TT_Boolean) {
oPtr = Tcl_NewObj();
GetExpression(interp, oPtr, expPtr->arg1.expPtr);
Tcl_ListObjAppendElement(interp, ePtr, oPtr);
Tcl_ListObjAppendElement(interp, ePtr,
Tcl_NewStringObj(tokenList[opIndex].string, -1));
oPtr = Tcl_NewObj();
GetExpression(interp, oPtr, expPtr->arg2.expPtr);
Tcl_ListObjAppendElement(interp, ePtr, oPtr);
} else {
for (fIndex=0; tokenList[fIndex].info != expPtr->arg1.info; fIndex++);
Tcl_ListObjAppendElement(interp, ePtr,
Tcl_NewStringObj(tokenList[fIndex].string, -1));
Tcl_ListObjAppendElement(interp, ePtr,
Tcl_NewStringObj(tokenList[opIndex].string, -1));
Tcl_ListObjAppendElement(interp, ePtr,
Tcl_NewStringObj(expPtr->arg2.string, -1));
}
}
/*
*----------------------------------------------------------------------
*
* RatGetExp --
*
* See ../doc/interface
*
* Results:
* The identified expression is returned as a string in the result area.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
int
RatGetExpCmd(ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[])
{
ExpList *elemPtr;
Tcl_Obj *rPtr;
int id;
if (objc < 2
|| TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &id)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetString(objv[0]), " id\"", (char *) NULL);
return TCL_ERROR;
}
for (elemPtr = expListPtr; elemPtr; elemPtr = elemPtr->next) {
if (elemPtr->id == id) {
rPtr = Tcl_NewObj();
GetExpression(interp, rPtr, elemPtr->expPtr);
Tcl_SetObjResult(interp, rPtr);
return TCL_OK;
}
}
Tcl_AppendResult(interp, "No expression with id \"",
Tcl_GetString(objv[1]), "\"", (char *) NULL);
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* RatFreeExp --
*
* See ../doc/interface
*
* Results:
* A standard tcl result.
*
* Side effects:
* Will probably remove an expression from the local list.
*
*
*----------------------------------------------------------------------
*/
int
RatFreeExpCmd(ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[])
{
ExpList **elemPtrPtr, *elemPtr;
int id;
if (objc < 2
|| TCL_OK != Tcl_GetIntFromObj(interp, objv[1], &id)) {
Tcl_AppendResult(interp, "Illegal usage: should be \"",
Tcl_GetString(objv[0]), " id\"", (char *) NULL);
return TCL_ERROR;
}
for (elemPtrPtr = &expListPtr; *elemPtrPtr;
elemPtrPtr=&(*elemPtrPtr)->next){
if ((*elemPtrPtr)->id == id) {
elemPtr = *elemPtrPtr;
FreeExp(elemPtr->expPtr);
*elemPtrPtr = elemPtr->next;
ckfree(elemPtr);
return TCL_OK;
}
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* RatExpMatch --
*
* Checks if a given expression matches to given message.
*
* Results:
* True if it did match.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
int
RatExpMatch(Tcl_Interp *interp, int expId, RatInfoProc *infoProc,
ClientData clientData, int index)
{
ExpList *elemPtr;
for (elemPtr = expListPtr; elemPtr && elemPtr->id != expId;
elemPtr = elemPtr->next);
if (!elemPtr) {
return 0;
}
return RatExpMatchDo(interp, elemPtr->expPtr, infoProc, clientData, index);
}
/*
*----------------------------------------------------------------------
*
* RatExpMatchDo --
*
* Checks if a given expression matches to given message.
* This routine actually does the checking and may call itself
* recursively.
*
* Results:
* True if it did match.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
int
RatExpMatchDo(Tcl_Interp *interp, Expression *expPtr, RatInfoProc *infoProc,
ClientData clientData, int index)
{
char *sLower, *cPtr;
int opIndex, val;
static Tcl_Obj *sPtr = NULL;
Tcl_Obj *oPtr;
for (opIndex=0; tokenList[opIndex].token != expPtr->op; opIndex++);
if (TT_Boolean == tokenList[opIndex].type) {
val = RatExpMatchDo(interp, expPtr->arg1.expPtr, infoProc, clientData,
index);
if (!((T_Or == tokenList[opIndex].token && val) ||
(T_And == tokenList[opIndex].token && !val))) {
val = RatExpMatchDo(interp, expPtr->arg2.expPtr, infoProc,
clientData, index);
}
if (expPtr->negate) {
val = val ? 0 : 1;
}
return val;
} else {
oPtr = (*infoProc)(interp, clientData, expPtr->arg1.info, index);
if (!oPtr) {
if (!sPtr) {
sPtr = Tcl_NewObj();
Tcl_IncrRefCount(sPtr);
}
oPtr = sPtr;
}
if (T_Has == tokenList[opIndex].token
|| T_Is == tokenList[opIndex].token) {
sLower = cpystr(Tcl_GetString(oPtr));
for (cPtr = sLower; *cPtr; cPtr++) {
if (isupper((unsigned char)*cPtr)) {
*cPtr = tolower((unsigned char)*cPtr);
}
}
val = Tcl_RegExpMatch(interp, sLower, expPtr->arg2.string);
ckfree(sLower);
return val;
} else if (expPtr->arg1.info == RAT_FOLDER_SIZE) {
Tcl_GetIntFromObj(interp, oPtr, &val);
if (T_Gt == tokenList[opIndex].token) {
return val > atoi(expPtr->arg2.string);
} else {
return val < atoi(expPtr->arg2.string);
}
}
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1