ATOI has now an optional argument to specify the base, defaulting to 10. asyncrecv...
[vde.git] / vde-2 / utils / cmdparse.c
blob94164a1063b560277438d28b37f325da91d8c873
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 <sys/poll.h>
24 #include <errno.h>
26 #ifndef HAVE_OPEN_MEMSTREAM
27 #include <utils/open_memstream.h>
28 #endif
30 #include <utils/cmdparse.h>
32 #define BUFSIZE 256
33 #define TIMEOUT 10000
35 enum command {ERR, IN, THROW, SEND, SHIFT, IF, GOTO, COPY, EXIT, EXITRV, SKIP, IFARG, RVATOI, OUTSHIFT, OUTTAG};
37 char *commandname[]= {
38 "",
39 "IN",
40 "THROW",
41 "SEND",
42 "SHIFT",
43 "IF",
44 "GOTO",
45 "COPY",
46 "EXIT",
47 "EXITRV",
48 "SKIP",
49 "IFARG",
50 "RVATOI",
51 "OUTSHIFT",
52 "OUTTAG"
55 #define NUMCOMMANDS (sizeof(commandname)/sizeof(char *))
57 static const char *nullstring="";
59 struct utmstate {
60 int num;
61 enum command command;
62 const char *string;
63 #define value nextnum;
64 int nextnum;
65 struct utmstate *next;
68 static struct utmstate *utmsadd(struct utmstate *head, struct utmstate *this)
70 if (!head || head->num > this->num) {
71 this->next=head;
72 return this;
73 } else {
74 head->next=utmsadd(head->next,this);
75 return head;
79 static enum command searchcommand(char *name)
81 int i;
82 for (i=0; i<NUMCOMMANDS && strcmp(name,commandname[i]) != 0; i++)
84 if (i<NUMCOMMANDS)
85 return i;
86 else
87 return ERR;
90 static inline char *blankskip(char *s)
92 while (*s && (*s==' ' || *s=='\t'))
93 s++;
94 return s;
97 static inline char *fieldskip(char *s)
99 while (*s && *s!=' ' && *s!='\t' && *s!='\n')
100 s++;
101 return s;
104 static int readchar(int fd, struct utm_buf *inbuf, int timeout)
106 if (!inbuf->buf) {
107 inbuf->buf=(char *)malloc(sizeof(char)*BUFSIZE);
108 if(!inbuf->buf) { perror("readchar"); exit(-1); }
109 inbuf->len=inbuf->pos=0;
111 if (inbuf->len <= inbuf->pos)
113 struct pollfd pfd={fd, POLLIN, 0};
114 if (poll(&pfd,1,timeout) <= 0) {
115 return -1;
117 inbuf->len=read(fd,inbuf->buf,BUFSIZE);
118 if (inbuf->len==0)
119 return -1;
120 else
121 inbuf->pos=0;
123 return(inbuf->buf[(inbuf->pos)++]);
126 struct utmstate *sgoto(struct utmstate *head,int nextnum)
128 if (head) {
129 if (nextnum == head->num)
130 return head;
131 else
132 return sgoto(head->next,nextnum);
133 } else {
134 //fprintf(stderr,"Error Label not found: %d\n",nextnum);
135 return NULL;
139 void utm_freestate(struct utmstate *head)
141 struct utmstate* rest = head->next;
142 free(head);
143 utm_freestate(rest);
146 struct utm *utm_alloc(char *conf)
148 FILE *f;
149 struct utm *utm=NULL;
150 int line=0;
151 char buf[BUFSIZE];
152 if ((f=fopen(conf,"r")) == NULL) {
153 //fprintf(stderr,"Configuration file error %s\n",conf);
154 errno=ENOENT;
155 return NULL;
157 utm=(struct utm*)malloc(sizeof(struct utm));
158 if(!utm) {perror("utm_alloc"); exit(-1); }
159 utm->timeout=TIMEOUT ; utm->head = NULL;
160 while (fgets(buf,BUFSIZE,f) != NULL) {
161 char *s=buf;
162 int num;
163 line++;
164 s=blankskip(s);
165 num=atoi(s);
166 if (num>0) {
167 /* create new automata state */
168 enum command cmd;
169 char *currfield;
170 char c;
171 s=fieldskip(s);
172 s=blankskip(s);
173 currfield=s;
174 s=fieldskip(s);
175 c=*s;*s=0;
176 if ((cmd=searchcommand(currfield)) != ERR) {
177 struct utmstate *new=malloc(sizeof(struct utmstate));
178 if(!new) {perror("utm_alloc"); exit(-1); }
179 new->num = num;
180 new->command = cmd;
181 *s=c;
182 s=blankskip(s);
183 currfield=s;
184 if (*currfield=='\'') { /* first argument is a string */
185 char *t;
186 char skip=0; /*not escaped*/
187 t=currfield=++s; /* skip ' */
188 while (*s && (skip || *s != '\'')) {
189 if (*s == '\\' && *(s+1) != 0) {
190 s++; /* skip \ */
191 switch (*s) {
192 case 'n': *s='\n'; break;
193 case 't': *s='\t'; break;
194 case 'f': *s='\f'; break;
197 *t++ = *s++;
199 c=*s;*t=0;
200 new->string=strdup(currfield);
201 if (c) s++;
202 s=blankskip(s);
203 currfield=s;
204 } else {
205 new->string=nullstring;
207 new->nextnum=atoi(currfield);
208 utm->head=utmsadd(utm->head,new);
210 } else {
211 /* add constant definition */
212 if (strncmp("TIMEOUT",s,7)==0)
213 utm->timeout=atoi(s+8);
216 fclose(f);
217 return(utm);
220 void utm_free(struct utm *utm)
222 if(utm){
223 if(utm->head) utm_freestate(utm->head);
224 free(utm);
228 int utm_run(struct utm *utm, struct utm_buf *buf, int fd, int argc, char **argv, struct utm_out *out, int debug)
230 struct utmstate *status = utm->head;
231 int len=0, curr=0, linebufsize=0, rv=-1;
232 char *linebuf=NULL;
234 if(debug) {int i; printf("c: %d\n", argc); for(i=0; i <=argc ; i++) printf("a[%d]: %s\n", i, argv[i]); }
236 while (1) {
237 int patlen=strlen(status->string);
238 if (debug) printf("NOW %d parsing %s\n",status->num,linebuf?(linebuf+curr):NULL);
239 switch (status -> command) {
240 case ERR: /* error, return */
241 if(linebuf) free(linebuf);
242 return -1;
243 break;
244 case IN: /* eat from inbuf while timeout or pattern found */
246 int ltimeout=0;
247 do {
248 if (len==linebufsize) {
249 linebufsize += BUFSIZE;
250 linebuf=realloc(linebuf,sizeof(char)*(linebufsize+1));
251 if(!linebuf){ perror("utm_run"); exit(-1); }
253 if ((linebuf[len]=readchar(fd,buf,utm->timeout)) < 0)
254 ltimeout=1;
255 else
256 len++;
257 } while (!ltimeout && (len < patlen || strncmp(status->string,linebuf+(len-patlen),patlen) != 0));
258 linebuf[len]=0;
259 if(ltimeout)
260 status=sgoto(utm->head,status->nextnum);
261 else
262 status=status->next;
264 break;
265 case THROW: /* drop current linebuf */
266 curr=0;
267 if(linebuf) *linebuf=0;
268 len=0;
269 status=status->next;
270 break;
271 case SEND: /* write command to fd */
273 const char *t=status->string;
274 char *ptr;
275 size_t size;
276 FILE *mf=open_memstream(&ptr,&size);
277 while (*t) { /* create the string */
278 if (*t == '$' && (t==status->string || *(t-1) != '\\')) {
279 t++;
280 if (*t == '*' || *t == '0') { /*all parms*/
281 int i;
282 for (i=0;i<argc;i++) {
283 if (i) fprintf(mf," ");
284 fprintf(mf,argv[i]);
286 } else {
287 int num=atoi(t);
288 while (*t >='0' && *t <= '9') t++;
289 if (num < argc)
290 fprintf(mf,argv[num]);
292 } else
293 fprintf(mf,"%c",*t);
294 t++;
296 fclose(mf);
297 write (fd,ptr,size);
298 free(ptr);
300 status=status->next;
301 break;
302 case SHIFT: /* eat first argument */
303 argc--; argv++;
304 status=status->next;
305 break;
306 case IF: /* goto nextnum if pattern match */
307 if (linebuf && (strncmp(linebuf+curr,status->string,patlen) == 0) )
308 status=sgoto(utm->head,status->nextnum);
309 else
310 status=status->next;
311 break;
312 case GOTO: /* simple goto */
313 status=sgoto(utm->head,status->nextnum);
314 break;
315 case COPY: /* copy current linebuf to current outbuf */
316 if(linebuf){
317 int tocpy=strlen(linebuf+curr)+1;
318 out->buf=realloc(out->buf, out->sz+tocpy);
319 if(!out->buf){ perror("utm_run"); exit(-1); }
320 memcpy(out->buf+out->sz, linebuf+curr, tocpy);
321 out->sz+=tocpy;
323 status=status->next;
324 break;
325 case EXIT: /* exit with value */
326 rv = status->nextnum;
327 case EXITRV: /* exit with retval */
328 if(linebuf) free(linebuf);
329 return rv;
330 break;
331 case SKIP: /* skip after the first occurence of string or N chars */
332 if(linebuf){
333 char *skip=NULL;
334 if(strlen(status->string)) skip=strstr(linebuf, status->string);
335 if(skip) curr=(status->string+strlen(status->string))-linebuf;
336 else curr+=status->nextnum;
337 if(curr>len) curr=len; /* normalize */
339 status=status->next;
340 break;
341 case IFARG: /* goto if there are still arguments */
342 if (argc>=0)
343 status=sgoto(utm->head,status->nextnum);
344 else
345 status=status->next;
346 break;
347 case RVATOI: /* remember current number as return value the
348 optional argument is the base to convert from*/
349 if(!linebuf){
350 rv = -1;
351 }else if( status->nextnum <= 0 ){
352 rv = strtol(linebuf+curr, NULL, 10);
353 }else if( status->nextnum >= 2 && status->nextnum <= 36 ){
354 rv = strtol(linebuf+curr, NULL, status->nextnum);
355 }else{
356 rv = -1;
358 status=status->next;
359 break;
360 case OUTSHIFT: /* alloc another output buffer and use it */
361 out->next=utmout_alloc();
362 out=out->next;
363 status=status->next;
364 break;
365 case OUTTAG: /* set tag of current output buffer */
366 out->tag=status->nextnum;
367 status=status->next;
368 break;
369 default:
370 if(linebuf) free(linebuf);
371 return -1;
372 break;
377 struct utm_out *utmout_alloc(void)
379 struct utm_out *out = NULL;
380 out = (struct utm_out*)malloc(sizeof(struct utm_out));
381 if(!out) { perror(__func__); exit(-1);}
382 memset(out, 0, sizeof(struct utm_out));
383 return out;
386 void utmout_free(struct utm_out *out)
388 while(out) {
389 if(out->buf) free(out->buf);
390 out = out->next;