Files
lcdproc/server/parse.c
T

368 lines
8.4 KiB
C

/*
* parse.c
* This file is part of LCDd, the lcdproc server.
*
* This file is released under the GNU General Public License. Refer to the
* COPYING file distributed with this package.
*
* Copyright (c) 1999, William Ferrell, Scott Scriven
*
*
* Handles input commands from clients, by splitting strings into tokens
* and passing arguments to the appropriate handler.
*
* It works much like a command line. Only the first token is used to
* determine what function to call.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "shared/LL.h"
#include "shared/sockets.h"
#include "shared/report.h"
#include "clients.h"
#include "commands/command_list.h"
#include "parse.h"
/* This is a big function... TOO big. How to trim....
* TODO: Simplify... simplify...
*/
#define MAX_ARGUMENTS 40
int
parse_all_client_messages2 ()
{
int i; /* int j, len;*/
/*int newtoken, inquote;*/
Client * c;
char *str, *p, *q, *s;
/* char *tok;*/
int argc;
char *argv[MAX_ARGUMENTS];
/*char delimiters[] = " ";*/
char leftquote[] = "\"'`([{";
char rightquote[] = "\"'`)]}";
char errmsg[256];
int invalid = 0;
int quoteindex;
debug( RPT_DEBUG, "parse_all_client_messages()" );
for (i = 0; i < MAX_ARGUMENTS; i++) {
argv[i] = NULL;
}
#define SEPARATOR_CHAR ' '
#define LINE_TERM_CHAR '\0'
#define COMMENT_CHAR '#'
for( c=clients_getfirst(); c; c=clients_getnext()) {
/* And parse all its messages...*/
/*debug(RPT_DEBUG, "parse: Getting messages...");*/
for (str = client_get_message (c); str; str = client_get_message (c)) {
debug (RPT_DEBUG, "parse: ...%s", str);
/* Now, split up the string...*/
argc = 0;
i = 0;
q = p = str;
if (*p == COMMENT_CHAR) {
continue; /* found a comment line - skip it...*/
}
debug (RPT_DEBUG, "starting string scan...");
do {
/* bypass initial white space...*/
while ((*p == SEPARATOR_CHAR) && (*p)) {
p++;
q++;
}
/* If (*p) is null here, we reached the end of
* an empty parameter... so one of two things
* is true:
*
* 1. There is nothing but white space on this line (odd..)
* 2. This is trailing white space (odd... but allowable)
*/
if (*p == LINE_TERM_CHAR) {
break;
/* if there are no arguments, argc == 0 and will fail
* appropriately...
*
* if this is trailing white space, ignore the argc++ at the
* end and claim this as the end...
*/
}
/* Handle quoted strings...*/
if ((s = strchr(leftquote, *p)) != NULL) {
quoteindex = s - leftquote;
/*debug(RPT_DEBUG, "found <%c> at index [%d] = <%c>", *p, quoteindex, leftquote[quoteindex]);*/
q = ++p; /* past open quote...*/
while ((rightquote[quoteindex] != *p) && (*p != LINE_TERM_CHAR)) {
p++;
}
if (*p == LINE_TERM_CHAR) {
/* We just sucked up the rest of the command line: ERROR!!*/
snprintf (errmsg, sizeof(errmsg), "huh? unterminated string! missing ending %c\n",
rightquote[quoteindex]);
sock_send_string (c->sock, errmsg);
continue;
} else {
*p = LINE_TERM_CHAR; /* terminate string*/
p++; /* bypass to next character*/
/* Note that next character could be a EndOfLine (null)
* if the string was last on the line, or it could be
* something else... is it a blank?
*/
if (*p != SEPARATOR_CHAR && *p != LINE_TERM_CHAR) {
sock_send_string (c->sock, "huh? improperly terminated string! (missing whitespace)\n");
continue;
}
}
/* Otherwise, normal string...*/
} else {
while (*p != SEPARATOR_CHAR && *p != LINE_TERM_CHAR)
p++;
}
/* Not end of line?*/
if (*p) {
*p = LINE_TERM_CHAR;
/*debug(RPT_DEBUG, "found new token: %s", q);*/
argv[i++] = q;
q = ++p;
} else {
/*debug(RPT_DEBUG, "found new token: %s", q);*/
argv[i++] = q;
}
/* At the end of this statement,
* *p will be '\0' if end of input reached;
* otherwise, it is the first character of the
* next part of the string.
*/
argc++;
} while (*p);
/*debug(RPT_DEBUG, "exiting string scan...");*/
argv[argc] = NULL;
if (argc < 1)
continue;
/* Now find and call the appropriate function...*/
invalid = 1;
for (i = 0; commands[i].keyword; i++) {
if (0 == strcmp (argv[0], commands[i].keyword)) {
invalid = commands[i].function (c, argc, argv);
break; /* found our function - don't continue on...*/
}
}
if (invalid) {
snprintf (errmsg, sizeof(errmsg), "huh? Invalid command \"%.40s\"\n", argv[0]);
sock_send_string (c->sock, errmsg);
}
free (str); /* fixed memory leak?*/
} /* end for(str...)*/
}
return 0;
}
int parse_message (char * str, Client * c);
int
parse_all_client_messages ()
{
Client * c;
char * str;
debug( RPT_DEBUG, "parse_all_client_messages()" );
for( c=clients_getfirst(); c; c=clients_getnext()) {
/* And parse all its messages...*/
/*debug(RPT_DEBUG, "parse: Getting messages...");*/
for (str = client_get_message (c); str; str = client_get_message (c)) {
parse_message (str, c);
free (str);
}
}
return 0;
}
int parse_message (char * str, Client * c)
{
typedef enum { ST_INITIAL, ST_WHITESPACE, ST_IGNORE, ST_ARGUMENT, ST_FINAL } State;
State state = ST_INITIAL;
char escape_chars[] = "nrt";
char escape_trans[] = "\n\r\t";
char errmsg[256];
int error = 0;
char used_quote = 0; /* The quote used to open a quote string */
int pos = 0;
char ch;
int i;
char * arg_space;
int argc =0 ;
char *argv[MAX_ARGUMENTS];
int argpos = 0;
void close_arg () {
argv[argc][argpos] = 0;
argv[argc+1] = argv[argc] + argpos + 1;
argc ++;
argpos = 0;
}
arg_space = malloc(strlen(str)+1);
argv[0] = arg_space;
/* We will create a new string that is shorter or equally long as
* the original string str.
*/
while( state != ST_FINAL ) {
ch = str[pos++];
switch( state ) {
case ST_INITIAL:
case ST_WHITESPACE:
switch( ch ) {
case '\r':
case '\t':
case ' ':
break;
case '\n':
case 0:
state = ST_FINAL;
break;
default:
state = ST_ARGUMENT;
pos --; /* Rescan current char */
}
break;
case ST_IGNORE:
switch( ch ) {
case '\n':
state = ST_FINAL;
break;
}
break;
case ST_ARGUMENT:
switch( ch ) {
case '\r':
case '\t':
case ' ':
if (used_quote) {
/* We're in a quoted string, add it */
argv[argc][argpos++] = ch;
}
else {
close_arg();
state = ST_WHITESPACE;
}
break;
case '\n':
if (used_quote) {
/* Just ad it */
argv[argc][argpos++] = ch;
break;
}
/* else go to case 0 */
case 0:
if (used_quote) {
error = 2;
}
close_arg();
state = ST_FINAL;
break;
case '\\':
if (str[pos]) {
/* We solve quoted chars here right away */
char * p;
p = strchr( escape_chars, str[pos++] );
if (p != NULL) {
/* Insert a replacement for the code */
argv[argc][argpos++] = escape_trans[(int)*p];
}
else {
/* Copy char litterally */
argv[argc][argpos++] = str[pos];
}
}
else {
close_arg();
error = 2;
state = ST_FINAL;
}
case '\"':
case '{':
if (!used_quote) {
used_quote = ch;
break;
}
/* else fall through to default... */
default:
if (used_quote) {
/* Have we reached the end of the
* quote already ?
*/
if ((used_quote == '{' && ch == '}')
|| (used_quote == '\"' && ch == '\"')) {
used_quote = 0;
break;
}
}
argv[argc][argpos++] = ch;
}
break;
case ST_FINAL:
/* This will never be reached */
break;
}
}
argv[argc] = NULL;
if (error) {
snprintf (errmsg, sizeof(errmsg), "huh? Could not parse command\n");
sock_send_string (c->sock, errmsg);
free( arg_space );
return 0;
}
/* Now find and call the appropriate function...*/
error = 1;
for (i = 0; commands[i].keyword; i++) {
if (0 == strcmp (argv[0], commands[i].keyword)) {
error = commands[i].function (c, argc, argv);
break; /* found our function - don't continue on...*/
}
}
if (error == 1) {
snprintf (errmsg, sizeof(errmsg), "huh? Invalid command \"%.40s\"\n", argv[0]);
sock_send_string (c->sock, errmsg);
}
else if (error) {
snprintf (errmsg, sizeof(errmsg), "huh? Function returned error \"%.40s\"\n", argv[0]);
sock_send_string (c->sock, errmsg);
}
free( arg_space );
return 0;
}