- fixed poll emulation management
[vde.git] / vde-2 / utils / cmdparse.c
blob9cc84a75be5cdd250cfbe736225563c76b6dbc05
1 /*
2 * Copyright (C) 2007 - Renzo Davoli, Luca Bigliardi
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License
5 * as published by the Free Software Foundation; either version 2
6 * of the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include "compat/poll.h"
24 #include <errno.h>
26 #include "compat/open_memstream.h"
28 #include <utils/cmdparse.h>
30 #define BUFSIZE 256
31 #define TIMEOUT 10000
33 enum command {ERR, IN, THROW, SEND, SHIFT, IF, GOTO, COPY, EXIT, EXITRV, SKIP, IFARG, RVATOI, OUTSHIFT, OUTTAG};
35 char *commandname[]= {
36 "",
37 "IN",
38 "THROW",
39 "SEND",
40 "SHIFT",
41 "IF",
42 "GOTO",
43 "COPY",
44 "EXIT",
45 "EXITRV",
46 "SKIP",
47 "IFARG",
48 "RVATOI",
49 "OUTSHIFT",
50 "OUTTAG"
53 #define NUMCOMMANDS (sizeof(commandname)/sizeof(char *))
55 static const char *nullstring="";
57 struct utmstate {
58 int num;
59 enum command command;
60 const char *string;
61 #define value nextnum;
62 int nextnum;
63 struct utmstate *next;
66 static struct utmstate *utmsadd(struct utmstate *head, struct utmstate *this)
68 if (!head || head->num > this->num) {
69 this->next=head;
70 return this;
71 } else {
72 head->next=utmsadd(head->next,this);
73 return head;
77 static enum command searchcommand(char *name)
79 int i;
80 for (i=0; i<NUMCOMMANDS && strcmp(name,commandname[i]) != 0; i++)
82 if (i<NUMCOMMANDS)
83 return i;
84 else
85 return ERR;
88 static inline char *blankskip(char *s)
90 while (*s && (*s==' ' || *s=='\t'))
91 s++;
92 return s;
95 static inline char *fieldskip(char *s)
97 while (*s && *s!=' ' && *s!='\t' && *s!='\n')
98 s++;
99 return s;
102 static int readchar(int fd, struct utm_buf *inbuf, char *out, int timeout)
104 if (!inbuf->buf) {
105 inbuf->buf=(char *)malloc(sizeof(char)*BUFSIZE);
106 if(!inbuf->buf) { perror("readchar"); exit(-1); }
107 inbuf->len=inbuf->pos=0;
109 if (inbuf->len <= inbuf->pos)
111 struct pollfd pfd={fd, POLLIN, 0};
112 if (poll(&pfd,1,timeout) <= 0) {
113 return -1;
115 inbuf->len=read(fd,inbuf->buf,BUFSIZE);
116 if (inbuf->len==0)
117 return -1;
118 else
119 inbuf->pos=0;
121 *out = (inbuf->buf[(inbuf->pos)++]);
122 return 0;
125 struct utmstate *sgoto(struct utmstate *head,int nextnum)
127 if (head) {
128 if (nextnum == head->num)
129 return head;
130 else
131 return sgoto(head->next,nextnum);
132 } else {
133 //fprintf(stderr,"Error Label not found: %d\n",nextnum);
134 return NULL;
138 void utm_freestate(struct utmstate *head)
140 struct utmstate* rest = head->next;
141 free(head);
142 utm_freestate(rest);
145 struct utm *utm_alloc(char *conf)
147 FILE *f;
148 struct utm *utm=NULL;
149 int line=0;
150 char buf[BUFSIZE];
151 if ((f=fopen(conf,"r")) == NULL) {
152 //fprintf(stderr,"Configuration file error %s\n",conf);
153 errno=ENOENT;
154 return NULL;
156 utm=(struct utm*)malloc(sizeof(struct utm));
157 if(!utm) {perror("utm_alloc"); exit(-1); }
158 utm->timeout=TIMEOUT ; utm->head = NULL;
159 while (fgets(buf,BUFSIZE,f) != NULL) {
160 char *s=buf;
161 int num;
162 line++;
163 s=blankskip(s);
164 num=atoi(s);
165 if (num>0) {
166 /* create new automata state */
167 enum command cmd;
168 char *currfield;
169 char c;
170 s=fieldskip(s);
171 s=blankskip(s);
172 currfield=s;
173 s=fieldskip(s);
174 c=*s;*s=0;
175 if ((cmd=searchcommand(currfield)) != ERR) {
176 struct utmstate *new=malloc(sizeof(struct utmstate));
177 if(!new) {perror("utm_alloc"); exit(-1); }
178 new->num = num;
179 new->command = cmd;
180 *s=c;
181 s=blankskip(s);
182 currfield=s;
183 if (*currfield=='\'') { /* first argument is a string */
184 char *t;
185 char skip=0; /*not escaped*/
186 t=currfield=++s; /* skip ' */
187 while (*s && (skip || *s != '\'')) {
188 if (*s == '\\' && *(s+1) != 0) {
189 s++; /* skip \ */
190 switch (*s) {
191 case 'n': *s='\n'; break;
192 case 't': *s='\t'; break;
193 case 'f': *s='\f'; break;
196 *t++ = *s++;
198 c=*s;*t=0;
199 new->string=strdup(currfield);
200 if (c) s++;
201 s=blankskip(s);
202 currfield=s;
203 } else {
204 new->string=nullstring;
206 new->nextnum=atoi(currfield);
207 utm->head=utmsadd(utm->head,new);
209 } else {
210 /* add constant definition */
211 if (strncmp("TIMEOUT",s,7)==0)
212 utm->timeout=atoi(s+8);
215 fclose(f);
216 return(utm);
219 void utm_free(struct utm *utm)
221 if(utm){
222 if(utm->head) utm_freestate(utm->head);
223 free(utm);
227 int utm_run(struct utm *utm, struct utm_buf *buf, int fd, int argc, char **argv, struct utm_out *out, int debug)
229 struct utmstate *status = utm->head;
230 int len=0, curr=0, linebufsize=0, rv=-1;
231 char *linebuf=NULL;
233 if(debug) {int i; printf("c: %d\n", argc); for(i=0; i <=argc ; i++) printf("a[%d]: %s\n", i, argv[i]); }
235 while (1) {
236 int patlen=strlen(status->string);
237 if (debug) printf("NOW %d parsing %s\n",status->num,linebuf?(linebuf+curr):NULL);
238 switch (status -> command) {
239 case ERR: /* error, return */
240 if(linebuf) free(linebuf);
241 return -1;
242 break;
243 case IN: /* eat from inbuf while timeout or pattern found */
245 int ltimeout=0;
246 do {
247 if (len==linebufsize) {
248 linebufsize += BUFSIZE;
249 linebuf=realloc(linebuf,sizeof(char)*(linebufsize+1));
250 if(!linebuf){ perror("utm_run"); exit(-1); }
252 if (readchar(fd, buf, &linebuf[len], utm->timeout) < 0)
253 ltimeout=1;
254 else
255 len++;
256 } while (!ltimeout && (len < patlen || strncmp(status->string,linebuf+(len-patlen),patlen) != 0));
257 linebuf[len]=0;
258 if(ltimeout)
259 status=sgoto(utm->head,status->nextnum);
260 else
261 status=status->next;
263 break;
264 case THROW: /* drop current linebuf */
265 curr=0;
266 if(linebuf) *linebuf=0;
267 len=0;
268 status=status->next;
269 break;
270 case SEND: /* write command to fd */
272 const char *t=status->string;
273 char *ptr;
274 size_t size;
275 FILE *mf=open_memstream(&ptr,&size);
276 while (*t) { /* create the string */
277 if (*t == '$' && (t==status->string || *(t-1) != '\\')) {
278 t++;
279 if (*t == '*' || *t == '0') { /*all parms*/
280 int i;
281 for (i=0;i<argc;i++) {
282 if (i) fprintf(mf," ");
283 fprintf(mf,argv[i]);
285 } else {
286 int num=atoi(t);
287 while (*t >='0' && *t <= '9') t++;
288 if (num < argc)
289 fprintf(mf,argv[num]);
291 } else
292 fprintf(mf,"%c",*t);
293 t++;
295 fclose(mf);
296 write (fd,ptr,size);
297 free(ptr);
299 status=status->next;
300 break;
301 case SHIFT: /* eat first argument */
302 argc--; argv++;
303 status=status->next;
304 break;
305 case IF: /* goto nextnum if pattern match */
306 if (linebuf && (strncmp(linebuf+curr,status->string,patlen) == 0) )
307 status=sgoto(utm->head,status->nextnum);
308 else
309 status=status->next;
310 break;
311 case GOTO: /* simple goto */
312 status=sgoto(utm->head,status->nextnum);
313 break;
314 case COPY: /* copy current linebuf to current outbuf */
315 if(linebuf){
316 int tocpy=strlen(linebuf+curr)+1;
317 out->buf=realloc(out->buf, out->sz+tocpy);
318 if(!out->buf){ perror("utm_run"); exit(-1); }
319 memcpy(out->buf+out->sz, linebuf+curr, tocpy);
320 out->sz+=tocpy;
322 status=status->next;
323 break;
324 case EXIT: /* exit with value */
325 rv = status->nextnum;
326 case EXITRV: /* exit with retval */
327 if(linebuf) free(linebuf);
328 return rv;
329 break;
330 case SKIP: /* skip after the first occurence of string or N chars */
331 if(linebuf){
332 char *skip=NULL;
333 if(strlen(status->string)) skip=strstr(linebuf, status->string);
334 if(skip) curr=(status->string+strlen(status->string))-linebuf;
335 else curr+=status->nextnum;
336 if(curr>len) curr=len; /* normalize */
338 status=status->next;
339 break;
340 case IFARG: /* goto if there are still arguments */
341 if (argc>=0)
342 status=sgoto(utm->head,status->nextnum);
343 else
344 status=status->next;
345 break;
346 case RVATOI: /* remember current number as return value the
347 optional argument is the base to convert from*/
348 if(!linebuf){
349 rv = -1;
350 }else if( status->nextnum <= 0 ){
351 rv = strtol(linebuf+curr, NULL, 10);
352 }else if( status->nextnum >= 2 && status->nextnum <= 36 ){
353 rv = strtol(linebuf+curr, NULL, status->nextnum);
354 }else{
355 rv = -1;
357 status=status->next;
358 break;
359 case OUTSHIFT: /* alloc another output buffer and use it */
360 out->next=utmout_alloc();
361 out=out->next;
362 status=status->next;
363 break;
364 case OUTTAG: /* set tag of current output buffer */
365 out->tag=status->nextnum;
366 status=status->next;
367 break;
368 default:
369 if(linebuf) free(linebuf);
370 return -1;
371 break;
376 struct utm_out *utmout_alloc(void)
378 struct utm_out *out = NULL;
379 out = (struct utm_out*)malloc(sizeof(struct utm_out));
380 if(!out) { perror(__func__); exit(-1);}
381 memset(out, 0, sizeof(struct utm_out));
382 return out;
385 void utmout_free(struct utm_out *out)
387 while(out) {
388 if(out->buf) free(out->buf);
389 out = out->next;