#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "prot.h"
#include "lex.h"
#include "codes.h"
int lexer_state = LEXER_STATE_NORMAL;
#define ACAP_MAX_QSTR_LEN 4096
#define ERR() { \
lexer_state=LEXER_STATE_RECOVER; \
return SIEVE_FAIL; \
}
#define ERR_PUSHBACK() { \
prot_ungetc(ch, stream); \
ERR(); \
}
int token_lookup(char *str, int len)
{
if (strcmp(str,"ok")==0) return TOKEN_OK;
if (strcmp(str,"no")==0) return TOKEN_NO;
if (strcmp(str,"bye")==0) return TOKEN_BYE;
if (strcmp(str,"active")==0) return TOKEN_ACTIVE;
if (strcmp(str,"referral")==0) return TOKEN_REFERRAL;
if (strcmp(str,"sasl")==0) return TOKEN_SASL;
return -1;
}
int yylex(lexstate_t * lvalp, void * client)
{
int ch;
char buffer[ACAP_MAX_QSTR_LEN];
char *buff_ptr = buffer;
char *buff_end = buffer + ACAP_MAX_QSTR_LEN -1;
unsigned long count=0;
int result = SIEVE_OK;
int synchronizing;
struct protstream *stream=(struct protstream *) client;
while (1)
{
ch = prot_getc(stream);
if (ch == -1)
return SIEVE_FAIL;
switch (lexer_state)
{
case LEXER_STATE_RECOVER:
if (ch == '\r')
lexer_state=LEXER_STATE_RECOVER_CR;
break;
case LEXER_STATE_RECOVER_CR:
if (ch == '\n')
lexer_state=LEXER_STATE_NORMAL;
return EOL;
case LEXER_STATE_CR:
if (ch == '\n') {
lexer_state=LEXER_STATE_NORMAL;
return EOL;
}
ERR_PUSHBACK();
case LEXER_STATE_QSTR:
if (ch == '\"') {
lvalp->str = NULL;
result = string_allocate(buff_ptr - buffer, buffer, &lvalp->str);
if (result != SIEVE_OK)
ERR_PUSHBACK();
lexer_state=LEXER_STATE_NORMAL;
return STRING;
}
if (ch == '\0'
|| 0x7F < ((unsigned char)ch))
ERR_PUSHBACK();
if (buff_end <= buff_ptr)
ERR_PUSHBACK();
if (ch == '\\') {
ch=prot_getc(stream);
if (result != SIEVE_OK)
ERR();
if (ch != '\"' && ch != '\\')
ERR_PUSHBACK();
}
*buff_ptr++ = ch;
break;
case LEXER_STATE_LITERAL:
if (('0' <= ch) && (ch <= '9')) {
unsigned long newcount = count * 10 + (ch - '0');
if (newcount < count)
ERR_PUSHBACK();
count = newcount;
break;
}
synchronizing = FALSE;
if (ch != '}')
ERR_PUSHBACK();
ch=prot_getc(stream);
if (ch < 0)
ERR();
if (ch != '\r')
ERR_PUSHBACK();
ch=prot_getc(stream);
if (ch < 0)
ERR();
if (ch != '\n')
ERR_PUSHBACK();
lvalp->str = NULL;
result = string_allocate(count, NULL, &lvalp->str);
if (result != SIEVE_OK)
ERR_PUSHBACK();
{
char *it = string_DATAPTR(lvalp->str),
*end = it + count;
while (it < end) {
*it=prot_getc(stream);
it++;
}
*it = '\0';
}
lexer_state=LEXER_STATE_NORMAL;
return STRING;
case LEXER_STATE_NUMBER:
if (('0' <= ch) && (ch <= '9')) {
unsigned long newcount = count * 10 + (ch - '0');
if (newcount < count)
ERR_PUSHBACK();
count = newcount;
} else {
lvalp->number = count;
lexer_state=LEXER_STATE_NORMAL;
prot_ungetc(ch, stream);
return NUMBER;
}
break;
case LEXER_STATE_NORMAL:
if (isalpha((unsigned char) ch)) {
lexer_state=LEXER_STATE_ATOM;
*buff_ptr++ = tolower(ch);
break;
}
switch (ch) {
case '(':
return '(';
case ')':
return ')';
case ' ':
return ' ';
case '\"':
lexer_state=LEXER_STATE_QSTR;
break;
case '*':
return '*';
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
count = ch - '0';
lexer_state=LEXER_STATE_NUMBER;
break;
case '{':
count = 0;
synchronizing = TRUE;
lexer_state=LEXER_STATE_LITERAL;
break;
case '\r':
lexer_state=LEXER_STATE_CR;
break;
case '\n':
lexer_state=LEXER_STATE_NORMAL;
return EOL;
break;
default:
ERR_PUSHBACK();
}
break;
case LEXER_STATE_ATOM:
if (!isalpha((unsigned char) ch)) {
int token;
buffer[ buff_ptr - buffer] = '\0';
token = token_lookup((char *) buffer, (int) (buff_ptr - buffer));
if (token!=-1) {
lexer_state=LEXER_STATE_NORMAL;
prot_ungetc(ch, stream);
return token;
} else
ERR_PUSHBACK();
}
if (buff_end <= buff_ptr)
ERR_PUSHBACK();
*buff_ptr++ = tolower(ch);
break;
}
}
return 0;
}