readchar fix
[vde.git] / vde-2 / utils / cmdparse.c
blob62a9541f286eeebe3c21912b3f42d8cd81c1228f
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, char *out, 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 *out = (inbuf->buf[(inbuf->pos)++]);
124 return 0;
127 struct utmstate *sgoto(struct utmstate *head,int nextnum)
129 if (head) {
130 if (nextnum == head->num)
131 return head;
132 else
133 return sgoto(head->next,nextnum);
134 } else {
135 //fprintf(stderr,"Error Label not found: %d\n",nextnum);
136 return NULL;
140 void utm_freestate(struct utmstate *head)
142 struct utmstate* rest = head->next;
143 free(head);
144 utm_freestate(rest);
147 struct utm *utm_alloc(char *conf)
149 FILE *f;
150 struct utm *utm=NULL;
151 int line=0;
152 char buf[BUFSIZE];
153 if ((f=fopen(conf,"r")) == NULL) {
154 //fprintf(stderr,"Configuration file error %s\n",conf);
155 errno=ENOENT;
156 return NULL;
158 utm=(struct utm*)malloc(sizeof(struct utm));
159 if(!utm) {perror("utm_alloc"); exit(-1); }
160 utm->timeout=TIMEOUT ; utm->head = NULL;
161 while (fgets(buf,BUFSIZE,f) != NULL) {
162 char *s=buf;
163 int num;
164 line++;
165 s=blankskip(s);
166 num=atoi(s);
167 if (num>0) {
168 /* create new automata state */
169 enum command cmd;
170 char *currfield;
171 char c;
172 s=fieldskip(s);
173 s=blankskip(s);
174 currfield=s;
175 s=fieldskip(s);
176 c=*s;*s=0;
177 if ((cmd=searchcommand(currfield)) != ERR) {
178 struct utmstate *new=malloc(sizeof(struct utmstate));
179 if(!new) {perror("utm_alloc"); exit(-1); }
180 new->num = num;
181 new->command = cmd;
182 *s=c;
183 s=blankskip(s);
184 currfield=s;
185 if (*currfield=='\'') { /* first argument is a string */
186 char *t;
187 char skip=0; /*not escaped*/
188 t=currfield=++s; /* skip ' */
189 while (*s && (skip || *s != '\'')) {
190 if (*s == '\\' && *(s+1) != 0) {
191 s++; /* skip \ */
192 switch (*s) {
193 case 'n': *s='\n'; break;
194 case 't': *s='\t'; break;
195 case 'f': *s='\f'; break;
198 *t++ = *s++;
200 c=*s;*t=0;
201 new->string=strdup(currfield);
202 if (c) s++;
203 s=blankskip(s);
204 currfield=s;
205 } else {
206 new->string=nullstring;
208 new->nextnum=atoi(currfield);
209 utm->head=utmsadd(utm->head,new);
211 } else {
212 /* add constant definition */
213 if (strncmp("TIMEOUT",s,7)==0)
214 utm->timeout=atoi(s+8);
217 fclose(f);
218 return(utm);
221 void utm_free(struct utm *utm)
223 if(utm){
224 if(utm->head) utm_freestate(utm->head);
225 free(utm);
229 int utm_run(struct utm *utm, struct utm_buf *buf, int fd, int argc, char **argv, struct utm_out *out, int debug)
231 struct utmstate *status = utm->head;
232 int len=0, curr=0, linebufsize=0, rv=-1;
233 char *linebuf=NULL;
235 if(debug) {int i; printf("c: %d\n", argc); for(i=0; i <=argc ; i++) printf("a[%d]: %s\n", i, argv[i]); }
237 while (1) {
238 int patlen=strlen(status->string);
239 if (debug) printf("NOW %d parsing %s\n",status->num,linebuf?(linebuf+curr):NULL);
240 switch (status -> command) {
241 case ERR: /* error, return */
242 if(linebuf) free(linebuf);
243 return -1;
244 break;
245 case IN: /* eat from inbuf while timeout or pattern found */
247 int ltimeout=0;
248 do {
249 if (len==linebufsize) {
250 linebufsize += BUFSIZE;
251 linebuf=realloc(linebuf,sizeof(char)*(linebufsize+1));
252 if(!linebuf){ perror("utm_run"); exit(-1); }
254 if (readchar(fd, buf, &linebuf[len], utm->timeout) < 0)
255 ltimeout=1;
256 else
257 len++;
258 } while (!ltimeout && (len < patlen || strncmp(status->string,linebuf+(len-patlen),patlen) != 0));
259 linebuf[len]=0;
260 if(ltimeout)
261 status=sgoto(utm->head,status->nextnum);
262 else
263 status=status->next;
265 break;
266 case THROW: /* drop current linebuf */
267 curr=0;
268 if(linebuf) *linebuf=0;
269 len=0;
270 status=status->next;
271 break;
272 case SEND: /* write command to fd */
274 const char *t=status->string;
275 char *ptr;
276 size_t size;
277 FILE *mf=open_memstream(&ptr,&size);
278 while (*t) { /* create the string */
279 if (*t == '$' && (t==status->string || *(t-1) != '\\')) {
280 t++;
281 if (*t == '*' || *t == '0') { /*all parms*/
282 int i;
283 for (i=0;i<argc;i++) {
284 if (i) fprintf(mf," ");
285 fprintf(mf,argv[i]);
287 } else {
288 int num=atoi(t);
289 while (*t >='0' && *t <= '9') t++;
290 if (num < argc)
291 fprintf(mf,argv[num]);
293 } else
294 fprintf(mf,"%c",*t);
295 t++;
297 fclose(mf);
298 write (fd,ptr,size);
299 free(ptr);
301 status=status->next;
302 break;
303 case SHIFT: /* eat first argument */
304 argc--; argv++;
305 status=status->next;
306 break;
307 case IF: /* goto nextnum if pattern match */
308 if (linebuf && (strncmp(linebuf+curr,status->string,patlen) == 0) )
309 status=sgoto(utm->head,status->nextnum);
310 else
311 status=status->next;
312 break;
313 case GOTO: /* simple goto */
314 status=sgoto(utm->head,status->nextnum);
315 break;
316 case COPY: /* copy current linebuf to current outbuf */
317 if(linebuf){
318 int tocpy=strlen(linebuf+curr)+1;
319 out->buf=realloc(out->buf, out->sz+tocpy);
320 if(!out->buf){ perror("utm_run"); exit(-1); }
321 memcpy(out->buf+out->sz, linebuf+curr, tocpy);
322 out->sz+=tocpy;
324 status=status->next;
325 break;
326 case EXIT: /* exit with value */
327 rv = status->nextnum;
328 case EXITRV: /* exit with retval */
329 if(linebuf) free(linebuf);
330 return rv;
331 break;
332 case SKIP: /* skip after the first occurence of string or N chars */
333 if(linebuf){
334 char *skip=NULL;
335 if(strlen(status->string)) skip=strstr(linebuf, status->string);
336 if(skip) curr=(status->string+strlen(status->string))-linebuf;
337 else curr+=status->nextnum;
338 if(curr>len) curr=len; /* normalize */
340 status=status->next;
341 break;
342 case IFARG: /* goto if there are still arguments */
343 if (argc>=0)
344 status=sgoto(utm->head,status->nextnum);
345 else
346 status=status->next;
347 break;
348 case RVATOI: /* remember current number as return value the
349 optional argument is the base to convert from*/
350 if(!linebuf){
351 rv = -1;
352 }else if( status->nextnum <= 0 ){
353 rv = strtol(linebuf+curr, NULL, 10);
354 }else if( status->nextnum >= 2 && status->nextnum <= 36 ){
355 rv = strtol(linebuf+curr, NULL, status->nextnum);
356 }else{
357 rv = -1;
359 status=status->next;
360 break;
361 case OUTSHIFT: /* alloc another output buffer and use it */
362 out->next=utmout_alloc();
363 out=out->next;
364 status=status->next;
365 break;
366 case OUTTAG: /* set tag of current output buffer */
367 out->tag=status->nextnum;
368 status=status->next;
369 break;
370 default:
371 if(linebuf) free(linebuf);
372 return -1;
373 break;
378 struct utm_out *utmout_alloc(void)
380 struct utm_out *out = NULL;
381 out = (struct utm_out*)malloc(sizeof(struct utm_out));
382 if(!out) { perror(__func__); exit(-1);}
383 memset(out, 0, sizeof(struct utm_out));
384 return out;
387 void utmout_free(struct utm_out *out)
389 while(out) {
390 if(out->buf) free(out->buf);
391 out = out->next;