Added support for DE200C VFD
[lcdproc-de200c.git] / server / parse.c
blob0f431fc9dd00715cfd54b93924de731f0fcadaee
1 /*
2 * parse.c
3 * This file is part of LCDd, the lcdproc server.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 1999, William Ferrell, Scott Scriven
11 * Handles input commands from clients, by splitting strings into tokens
12 * and passing arguments to the appropriate handler.
14 * It works much like a command line. Only the first token is used to
15 * determine what function to call.
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
23 #include "shared/LL.h"
24 #include "shared/sockets.h"
25 #include "shared/report.h"
26 #include "clients.h"
27 #include "commands/command_list.h"
28 #include "parse.h"
31 #define MAX_ARGUMENTS 40
34 static inline int is_whitespace(char x) {
35 return ((x == ' ') || (x == '\t') || (x == '\r'));
38 static inline int is_final(char x) {
39 return ((x == '\n') || (x == '\0'));
42 static inline int is_opening_quote(char x, char q) {
43 return ((q == '\0') && ((x == '\"') || (x == '{')));
46 static inline int is_closing_quote(char x, char q) {
47 return (((q == '{') && (x == '}')) || ((q == '\"') && (x == '\"')));
51 static int parse_message(const char *str, Client *c)
53 typedef enum { ST_INITIAL, ST_WHITESPACE, ST_ARGUMENT, ST_FINAL } State;
54 State state = ST_INITIAL;
56 int error = 0;
57 char quote = '\0'; /* The quote used to open a quote string */
58 int pos = 0;
59 char *arg_space;
60 int argc = 0;
61 char *argv[MAX_ARGUMENTS];
62 int argpos = 0;
63 CommandFunc function = NULL;
65 debug(RPT_DEBUG, "%s(str=\"%.120s\", client=[%d])", __FUNCTION__, str, c->sock);
67 /* We will create a list of strings that is shorter or equally long as
68 * the original string str.
70 arg_space = malloc(strlen(str)+1);
71 if (arg_space == NULL) {
72 report(RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
73 sock_send_error(c->sock, "error allocating memory!\n");
76 argv[0] = arg_space;
78 while ((state != ST_FINAL) && !error) {
79 char ch = str[pos++];
81 switch (state) {
82 case ST_INITIAL:
83 case ST_WHITESPACE:
84 if (is_whitespace(ch))
85 break;
86 if (is_final(ch)) {
87 state = ST_FINAL;
88 break;
90 /* otherwise fall through */
91 state = ST_ARGUMENT;
92 case ST_ARGUMENT:
93 if (is_final(ch)) {
94 if (quote)
95 error = 2;
96 if (argc >= MAX_ARGUMENTS-1) {
97 error = 1;
99 else {
100 argv[argc][argpos] = '\0';
101 argv[argc+1] = argv[argc] + argpos + 1;
102 argc++;
103 argpos = 0;
105 state = ST_FINAL;
107 else if (ch == '\\') {
108 if (str[pos]) {
109 /* We solve quoted chars here right away */
110 const char escape_chars[] = "nrt";
111 const char escape_trans[] = "\n\r\t";
112 char *p = strchr(escape_chars, str[pos]);
114 /* Is it wise to have the characters \n, \r & \t expanded ?
115 * Can the displays deal with them ?
117 if (p != NULL) {
118 /* Insert a replacement for the code */
119 argv[argc][argpos++] = escape_trans[p - escape_chars];
121 else {
122 /* Copy char literally */
123 argv[argc][argpos++] = str[pos];
125 pos++;
127 else {
128 error = 2;
129 /* alternative: argv[argc][argpos++] = ch; */
130 if (argc >= MAX_ARGUMENTS-1) {
131 error = 1;
133 else {
134 argv[argc][argpos] = '\0';
135 argv[argc+1] = argv[argc] + argpos + 1;
136 argc++;
137 argpos = 0;
139 state = ST_FINAL;
142 else if (is_opening_quote(ch, quote)) {
143 quote = ch;
145 else if (is_closing_quote(ch, quote)) {
146 quote = '\0';
147 if (argc >= MAX_ARGUMENTS-1) {
148 error = 1;
150 else {
151 argv[argc][argpos] = '\0';
152 argv[argc+1] = argv[argc] + argpos + 1;
153 argc++;
154 argpos = 0;
156 state = ST_WHITESPACE;
158 else if (is_whitespace(ch) && (quote == '\0')) {
159 if (argc >= MAX_ARGUMENTS-1) {
160 error = 1;
162 else {
163 argv[argc][argpos] = '\0';
164 argv[argc+1] = argv[argc] + argpos + 1;
165 argc++;
166 argpos = 0;
168 state = ST_WHITESPACE;
170 else {
171 argv[argc][argpos++] = ch;
173 break;
174 case ST_FINAL:
175 /* This will never be reached */
176 break;
179 if (argc < MAX_ARGUMENTS)
180 argv[argc] = NULL;
181 else
182 error = 1;
184 if (error) {
185 sock_send_error(c->sock, "Could not parse command\n");
186 free(arg_space);
187 return 0;
190 #if 0 /* show what we have parsed */
191 int i;
192 for (i = 0; i < argc; i++) {
193 printf("%s%c", argv[i], (i == argc-1) ? '\n' : ' ');
195 #endif
197 /* Now find and call the appropriate function...*/
198 function = get_command_function(argv[0]);
200 if (function != NULL) {
201 error = function(c, argc, argv);
202 if (error) {
203 sock_printf_error(c->sock, "Function returned error \"%.40s\"\n", argv[0]);
204 report(RPT_WARNING, "Command function returned an error after command from client on socket %d: %.40s", c->sock, str);
207 else {
208 sock_printf_error(c->sock, "Invalid command \"%.40s\"\n", argv[0]);
209 report(RPT_WARNING, "Invalid command from client on socket %d: %.40s", c->sock, str);
212 free(arg_space);
213 return 0;
218 parse_all_client_messages(void)
220 Client *c;
222 debug(RPT_DEBUG, "%s()", __FUNCTION__);
224 for (c = clients_getfirst(); c != NULL; c = clients_getnext()) {
225 char *str;
227 /* And parse all its messages...*/
228 /*debug(RPT_DEBUG, "parse: Getting messages...");*/
229 for (str = client_get_message(c); str != NULL; str = client_get_message(c)) {
230 parse_message(str, c);
231 free(str);
234 return 0;