static int builtin_cd( int line, int argc, char **argv, time_t stoptime ) { int result; char *dirname; if(argc==2) { dirname = argv[1]; } else if(argc==1) { struct passwd *p; p = getpwuid(getuid()); if(p) { dirname = p->pw_dir; } else { ftsh_error(FTSH_ERROR_FAILURE,line,"cd: couldn't determine your home directory"); dirname = 0; } } else { ftsh_error(FTSH_ERROR_SYNTAX,line,"cd: too many arguments"); result = 0; } if(dirname) { ftsh_error(FTSH_ERROR_COMMAND,line,"CD %s",dirname); if(chdir(dirname)==0) { result = 1; } else { ftsh_error(FTSH_ERROR_FAILURE,line,"cd: couldn't move to %s: %s",dirname,strerror(errno)); result = 0; } } else { result = 0; } return result; }
static int ast_do_internal( int line, int argc, char **argv, int fds[3], time_t stoptime ) { struct ast_function *f; builtin_func_t b; int oldfds[3]; int result=0; f = hash_table_lookup(ftable,argv[0]); b = builtin_lookup(argv[0]); if(f) ftsh_error(FTSH_ERROR_STRUCTURE,f->function_line,"FUNCTION %s",f->name->text); if(b || variable_frame_push(f->function_line,argc,argv)) { if(fds[0]!=0) { oldfds[0] = dup(0); if(oldfds[0]<0) ftsh_fatal(line,"out of file descriptors"); dup2(fds[0],0); } if(fds[1]!=1) { oldfds[1] = dup(1); if(oldfds[1]<0) ftsh_fatal(line,"out of file descriptors"); dup2(fds[1],1); } if(fds[2]!=2) { oldfds[2] = dup(2); if(oldfds[2]<0) ftsh_fatal(line,"out of file descriptors"); dup2(fds[2],2); } if(f) { result = ast_group_execute(f->body,stoptime); } else { result = b(line,argc,argv,stoptime); } if(fds[2]!=2) { dup2(oldfds[2],2); close(oldfds[2]); } if(fds[1]!=1) { dup2(oldfds[1],1); close(oldfds[1]); } if(fds[0]!=0) { dup2(oldfds[0],0); close(oldfds[0]); } if(f) variable_frame_pop(); } if(f) ftsh_error(FTSH_ERROR_STRUCTURE,f->end_line,"END"); return result; }
int variable_frame_push( int line, int argc, char **argv ) { struct vstack *v; int i; if(vstackdepth>1000) { ftsh_error(FTSH_ERROR_FAILURE,line,"aborting: you have recursed %d times",vstackdepth); return 0; } v = malloc(sizeof(*v)); if(!v) { ftsh_error(FTSH_ERROR_FAILURE,line,"out of memory"); return 0; } v->argc = argc; v->argv = argv; v->rval = 0; v->next = head; for( i=0; i<argc; i++ ) { ftsh_error(FTSH_ERROR_STRUCTURE,line,"${%d} = %s",i,argv[i]); } head = v; vstackdepth++; return 1; }
int ast_assign_execute( struct ast_assign *a, time_t stoptime ) { int result; if(a->expr) { char *value; char *word; value = expr_eval(a->expr,stoptime); if(value) { word = ast_bareword_execute(a->line,value); if(word) { ftsh_error(FTSH_ERROR_COMMAND,a->line,"%s=%s",a->name->text,word); if(buffer_save(a->name->text,word)) { result=1; } else { ftsh_error(FTSH_ERROR_FAILURE,a->line,"couldn't store variable '%s': %s",a->name->text,strerror(errno)); result=0; } free(word); } else { result = 0; } free(value); } else { result=0; } } else { ftsh_error(FTSH_ERROR_COMMAND,a->line,"%s=",a->name->text); buffer_delete(a->name->text); result = 1; } return result; }
static void multi_kill( int n, struct multi_fork_status *p, time_t stoptime, int line ) { int i; for( i=0; i<n; i++ ) { if(p[i].state==MULTI_FORK_STATE_CRADLE) { p[i].state = MULTI_FORK_STATE_GRAVE; } else if(p[i].state==MULTI_FORK_STATE_RUNNING) { ftsh_error(FTSH_ERROR_PROCESS,line,"sending SIGTERM to process %d",p[i].pid); kill(p[i].pid,SIGTERM); kill(-p[i].pid,SIGTERM); } } multi_wait(n,p,time(0)+multi_fork_kill_timeout,line,0); while(1) { int total=0; for( i=0; i<n; i++ ) { if(p[i].state==MULTI_FORK_STATE_RUNNING) { ftsh_error(FTSH_ERROR_PROCESS,line,"%d: sending SIGKILL to process %d",i,p[i].pid); kill(p[i].pid,SIGKILL); kill(-p[i].pid,SIGKILL); total++; } } if( total==0 ) break; if( multi_fork_kill_mode==MULTI_FORK_KILL_MODE_WEAK ) break; multi_wait(n,p,time(0)+5,line,0); } }
int ast_whileloop_execute( struct ast_whileloop *w, time_t stoptime ) { int result=1; ftsh_boolean_t b; while(1) { ftsh_error(FTSH_ERROR_STRUCTURE,w->while_line,"WHILE"); if(expr_to_boolean(w->expr,&b,stoptime)) { if(b) { ftsh_error(FTSH_ERROR_STRUCTURE,w->while_line,"WHILE expression is true"); if(ast_group_execute(w->body,stoptime)) { continue; } else { result = 0; break; } } else { ftsh_error(FTSH_ERROR_STRUCTURE,w->while_line,"WHILE expression is false"); result = 1; break; } } else { ftsh_error(FTSH_ERROR_STRUCTURE,w->while_line,"WHILE expression failed"); result = 0; break; } } ftsh_error(FTSH_ERROR_STRUCTURE,w->end_line,"END"); return result; }
int ast_forloop_execute( struct ast_forloop *f, time_t stoptime ) { char *loopname; char *name; char *line; int result=1; switch(f->type) { case AST_FOR: loopname = "FOR"; break; case AST_FORALL: loopname = "FORALL"; break; case AST_FORANY: loopname = "FORANY"; break; } ftsh_error(FTSH_ERROR_STRUCTURE,f->for_line,"%s %s",loopname,f->name->text); name = ast_word_execute(f->for_line,f->name); if(name) { line = ast_expr_list_execute(f->for_line,f->list,stoptime); if(line) { int argc; char **argv; if(string_split_quotes( line, &argc, &argv )) { switch(f->type) { case AST_FOR: result = ast_for_execute(f,stoptime,name,argc,argv); break; case AST_FORANY: result = ast_forany_execute(f,stoptime,name,argc,argv); break; case AST_FORALL: result = ast_forall_execute(f,stoptime,name,argc,argv); break; } free(argv); } else { ftsh_error(FTSH_ERROR_FAILURE,f->for_line,"out of memory!"); result = 0; } free(line); } free(name); } else { result = 0; } ftsh_error(FTSH_ERROR_STRUCTURE,f->end_line,"END"); return result; }
int ast_try_execute( struct ast_try *t, time_t stoptime ) { int result = ast_try_body_execute(t,stoptime); if(!result && t->catch_block ) { ftsh_error(FTSH_ERROR_STRUCTURE,t->catch_line,"CATCH"); result = ast_group_execute(t->catch_block,stoptime); } ftsh_error(FTSH_ERROR_STRUCTURE,t->end_line,"END"); return result; }
int ast_return_execute( struct ast_return *s, time_t stoptime ) { char *value; value = expr_eval(s->expr,stoptime); if(value) { ftsh_error(FTSH_ERROR_STRUCTURE,s->line,"return value is %s",value); variable_rval_set(value); return 1; } else { ftsh_error(FTSH_ERROR_FAILURE,s->line,"couldn't compute return value"); return 0; } }
int ast_program_execute( struct ast_group *program, time_t stoptime ) { struct ast_group *g; struct ast_function *f, *old; /* First, fill up the function table with all of the functions in this entire syntax tree. */ ftable = hash_table_create(127,hash_string); if(!ftable) ftsh_fatal(0,"out of memory"); for( g=program; g; g=g->next ) { if(g->command->type==AST_COMMAND_FUNCTION) { f = g->command->u.function; old = hash_table_remove(ftable,f->name->text); if(old) { ftsh_error(FTSH_ERROR_SYNTAX,f->function_line,"function %s is defined twice (first at line %d)",f->name->text,old->function_line); return 0; } if(!hash_table_insert(ftable,f->name->text,f)) { ftsh_fatal(f->function_line,"out of memory"); } } } return ast_group_execute(program,stoptime); }
static int ast_do_simple( int line, int argc, char **argv, int fds[3], time_t stoptime ) { char *cmd; int length=0; int i; for( i=0; i<argc; i++ ) { length+=strlen(argv[i])+1; } cmd = xxmalloc(length+1); cmd[0] = 0; for( i=0; i<argc; i++ ) { strcat(cmd,argv[i]); strcat(cmd," "); } ftsh_error(FTSH_ERROR_COMMAND,line,"%s",cmd); free(cmd); if(hash_table_lookup(ftable,argv[0]) || builtin_lookup(argv[0]) ) { return ast_do_internal(line,argc,argv,fds,stoptime); } else { return ast_do_external(line,argc,argv,fds,stoptime); } }
static int ast_redirect_open( struct ast_redirect *r, int line, int fds[3] ) { int fd; char *target; if(!r) return 1; target = ast_word_execute( line, r->target ); if(!target) return 0; switch(r->kind) { case AST_REDIRECT_FILE: switch(r->mode) { case AST_REDIRECT_INPUT: fd = open(target,O_RDONLY); break; case AST_REDIRECT_OUTPUT: fd = open(target,O_WRONLY|O_CREAT|O_TRUNC,0777); break; case AST_REDIRECT_APPEND: fd = open(target,O_WRONLY|O_CREAT|O_APPEND,0777); break; } break; case AST_REDIRECT_BUFFER: switch(r->mode) { case AST_REDIRECT_INPUT: fd = buffer_open_input(target); break; case AST_REDIRECT_OUTPUT: fd = buffer_open_output(target); break; case AST_REDIRECT_APPEND: fd = buffer_open_append(target); break; } break; case AST_REDIRECT_FD: fd = fds[atoi(target)]; break; } if(fd<0) { ftsh_error(FTSH_ERROR_FAILURE,line,"couldn't redirect fd %d to %s: %s",r->source,target,strerror(errno)); free(target); return 0; } else { r->actual = fd; fds[r->source] = fd; if(r->next) { return ast_redirect_open(r->next,line,fds); } else { return 1; } } }
static char * ast_bareword_execute( int linenum, char *line ) { int argc; char **argv; char *result=0; if(string_split_quotes(line,&argc,&argv)) { if(argc==1) { result = xxstrdup(argv[0]); } else if(argc>1) { ftsh_error(FTSH_ERROR_SYNTAX,linenum,"expected only one word here, but got garbage following '%s'",argv[0]); } else { ftsh_error(FTSH_ERROR_SYNTAX,linenum,"expected a word here, but found nothing"); } free(argv); } return result; }
static int ast_do_external( int line, int argc, char **argv, int fds[3], time_t stoptime ) { timed_exec_t tresult; int status; int result; pid_t pid; tresult = timed_exec(line,argv[0],argv,fds,&pid,&status,stoptime); if(tresult==TIMED_EXEC_TIMEOUT) { ftsh_error(FTSH_ERROR_FAILURE,line,"%s [%d] ran out of time",argv[0],pid); result = 0; } else if(tresult==TIMED_EXEC_NOEXEC) { ftsh_error(FTSH_ERROR_FAILURE,line,"%s [%d] couldn't be executed: %s",argv[0],pid,strerror(errno)); result = 0; } else { result = process_status(argv[0],pid,status,line); } return result; }
static int builtin_export( int line, int argc, char **argv, time_t stoptime ) { char *expr; char *name; char *value; if(argc<2) { ftsh_error(FTSH_ERROR_SYNTAX,line,"export: exactly one argument needed"); return 0; } else if(argc>2) { ftsh_error(FTSH_ERROR_SYNTAX,line,"export: too many arguments"); return 0; } name = argv[1]; value = buffer_load(name); if(!value) value = xxstrdup(""); expr = malloc(strlen(name)+strlen(value)+3); if(!expr) { free(name); free(value); return 0; } ftsh_error(FTSH_ERROR_COMMAND,line,"EXPORT %s (%s)",name,value); sprintf(expr,"%s=%s",name,value); /* Depending on the libc, this call may leak memory */ /* by leaving multiple exprs allocated. No solution */ /* except to leak. Don't export in an infinite loop. */ putenv(expr); free(name); free(value); return 1; }
int ast_conditional_execute( struct ast_conditional *c, time_t stoptime ) { int result; ftsh_boolean_t b; ftsh_error(FTSH_ERROR_STRUCTURE,c->if_line,"IF"); if(expr_to_boolean(c->expr,&b,stoptime)) { if(b) { result = ast_group_execute(c->positive,stoptime); } else { result = ast_group_execute(c->negative,stoptime); } } else { result = 0; } ftsh_error(FTSH_ERROR_STRUCTURE,c->end_line,"END"); return result; }
static int multi_wait( int n, struct multi_fork_status *p, time_t stoptime, int line, int stop_on_failure ) { int status; int interval; int i; pid_t pid; int total; while(1) { total=0; for(i=0;i<n;i++) { if( p[i].state==MULTI_FORK_STATE_GRAVE ) { total++; } } if(total>=n) return MULTI_FORK_SUCCESS; if(stop_on_failure && cancel_pending()) return MULTI_FORK_FAILURE; if(stoptime) { interval = stoptime-time(0); if(interval<=0) { return MULTI_FORK_TIMEOUT; } else { alarm(interval); } } else { /* Although we hope that this algorithm is correct, there are many ways to get it wrong, so regardless, bail out every 10 seconds and reconsider. */ alarm(10); } pid = waitpid(-1,&status,0); if(pid>0) { ftsh_error(FTSH_ERROR_PROCESS,line,"process %d has completed",pid); for(i=0;i<n;i++) { if( p[i].state==MULTI_FORK_STATE_RUNNING && p[i].pid==pid ) { p[i].status = status; p[i].state = MULTI_FORK_STATE_GRAVE; if(WIFEXITED(status)&&(WEXITSTATUS(status)==0)) { break; } else if(stop_on_failure) { return MULTI_FORK_FAILURE; } else { break; } } } } } }
static int ast_for_execute( struct ast_forloop *f, time_t stoptime, const char *name, int argc, char **argv ) { int i=0; int result=0; for(i=0;i<argc;i++) { ftsh_error(FTSH_ERROR_STRUCTURE,f->for_line,"%s=%s",name,argv[i]); result = buffer_save(name,argv[i]); if(!result) break; result = ast_group_execute(f->body,stoptime); if(!result) break; } return result; }
static int multi_start( int n, struct multi_fork_status *p, time_t stoptime, int line ) { int i; pid_t pid; for(i=0;i<n;i++) { if(cancel_pending()) return MULTI_FORK_FAILURE; if(stoptime && (time(0)>stoptime)) return MULTI_FORK_TIMEOUT; pid = fork(); if(pid==0) { return i; } else if(pid>0) { ftsh_error(FTSH_ERROR_PROCESS,line,"started new process %d",pid); p[i].pid = pid; p[i].state = MULTI_FORK_STATE_RUNNING; } else { ftsh_error(FTSH_ERROR_FAILURE,line,"couldn't create new process: %s\n",strerror(errno)); return MULTI_FORK_FAILURE; } } return MULTI_FORK_SUCCESS; }
static int builtin_exit( int line, int argc, char **argv, time_t stoptime ) { int value; if(argc<2) { value = 0; } else { if(sscanf(argv[1],"%d",&value)!=1) { value = 1; } } ftsh_error(FTSH_ERROR_STRUCTURE,line,"exit: exiting with status %d",value); exit(value); }
int variable_shift( int n, int line ) { int i; if(head->argc>=n) { head->argc-=n; for(i=0;i<head->argc;i++) { head->argv[i] = head->argv[i+n]; } return 1; } else { ftsh_error(FTSH_ERROR_SYNTAX,line,"cannot shift %d arguments; there are only %d",n,head->argc); return 0; } }
static int process_status( const char *name, pid_t pid, int status, int line ) { int result=0; if(WIFEXITED(status)) { int code = WEXITSTATUS(status); if(code==0) { ftsh_error(FTSH_ERROR_PROCESS,line,"%s [%d] exited normally with status %d",name,pid,code); result = 1; } else { ftsh_error(FTSH_ERROR_FAILURE,line,"%s [%d] exited normally status with %d",name,pid,code); result = 0; } } else if(WIFSIGNALED(status)) { int sig = WSTOPSIG(status); ftsh_error(FTSH_ERROR_FAILURE,line,"%s [%d] exited abnormally with signal %d (%s)",name,pid,sig,string_signal(sig)); result = 0; } else { ftsh_error(FTSH_ERROR_FAILURE,line,"%s [%d] exited for unknown reasons (wait status %d)",name,pid,status); result = 0; } return result; }
char * ast_function_execute( int line, int argc, char **argv, time_t stoptime ) { struct ast_function *f; char *rval = 0; if(variable_frame_push(line,argc,argv)) { f = hash_table_lookup(ftable,argv[0]); if(f) { if(ast_group_execute(f->body,stoptime)) { if(variable_rval_get()) { rval = xxstrdup(variable_rval_get()); ftsh_error(FTSH_ERROR_STRUCTURE,line,"function %s returns %s",argv[0],rval); } else { ftsh_error(FTSH_ERROR_FAILURE,line,"function %s did not return a value",argv[0]); } } } else { ftsh_error(FTSH_ERROR_FAILURE,line,"function %s is not defined",argv[0]); } variable_frame_pop(); } return rval; }
char * ast_word_list_execute( int linenum, struct ast_word *w ) { char *t, *line = 0; int i, len; glob_t g; while( w ) { /* This isn't correct. We need more thought on how to handle wildcards. if(strpbrk(w->text,"*[")) { */ if(0) { if(glob(w->text,GLOB_FLAGS,0,&g)==0) { len=1; for(i=0;i<g.gl_pathc;i++) { len += strlen(g.gl_pathv[i])+1; } t = xxmalloc(len); t[0]=0; for(i=0;i<g.gl_pathc;i++) { strcat(t,g.gl_pathv[i]); strcat(t," "); } globfree(&g); } else { ftsh_error(FTSH_ERROR_FAILURE,linenum,"couldn't expand pattern %s",w->text); if(line) free(line); return 0; } } else { t=xxstrdup(w->text); } if(line) { line = string_combine_multi( line, " ", t, 0 ); } else { line = t; } w = w->next; } return variable_subst(line,linenum); }
static int ast_forall_execute( struct ast_forloop *f, time_t stoptime, const char *name, int argc, char **argv ) { int i; int pid; int result; struct multi_fork_status *s; s = xxmalloc(sizeof(*s)*argc); pid = multi_fork(argc,s,stoptime,f->for_line); if(pid>=0) { random_init(); if(stoptime && (time(0)>stoptime)) _exit(1); ftsh_error(FTSH_ERROR_STRUCTURE,f->for_line,"%s=%s starting",name,argv[pid]); result = buffer_save(name,argv[pid]); if(!result) _exit(1); result = ast_group_execute(f->body,stoptime); if(result) { _exit(0); } else { _exit(1); } } else { for(i=0;i<argc;i++) { char str[LINE_MAX]; if(s[i].state==MULTI_FORK_STATE_GRAVE) { snprintf(str,sizeof(str),"%s=%s",name,argv[i]); process_status(str,s[i].pid,s[i].status,f->for_line); } } free(s); if(pid==MULTI_FORK_SUCCESS) { return 1; } else { return 0; } } }
static int ast_forany_execute( struct ast_forloop *f, time_t stoptime, const char *name, int argc, char **argv ) { int result=0; int start = rand()%argc; int i = start; while(1) { ftsh_error(FTSH_ERROR_STRUCTURE,f->for_line,"%s=%s",name,argv[i]); result = buffer_save(name,argv[i]); if(result) { result = ast_group_execute(f->body,stoptime); if(result) break; } i++; if(i>=argc) i=0; if(i==start) { result = 0; break; } } return result; }
timed_exec_t timed_exec( int line, const char *path, char **argv, int fds[3], pid_t *pid, int *status, time_t stoptime ) { int fresult; int pfds[2]; int child_errno; int actual; struct multi_fork_status s; actual = pipe(pfds); if(actual!=0) return TIMED_EXEC_NOEXEC; fresult = multi_fork(1,&s,stoptime,line); if(fresult>=0) { /* Move our standard I/O streams into the expected places. */ /* It seems that cygwin doesn't like dup2 on the same fd. */ int i, maxfd; for( i=0; i<=2; i++ ) { if( fds[i]!=i ) { if( dup2(fds[i],i) != i ) { ftsh_error(FTSH_ERROR_PROCESS,line,"failure to dup2(%d,%d): %s\n",fds[i],i,strerror(errno)); goto done; } } } /* Close all of the file descriptors that we don't need. */ maxfd = sysconf( _SC_OPEN_MAX ); if(maxfd<=0) maxfd = 255; for(i=3;i<maxfd;i++) { if(i==pfds[1]) continue; close(i); } /* Set the pipe to automatically close after exec. */ if( fcntl(pfds[1],F_SETFD,FD_CLOEXEC)==0 ) { setsid(); execvp(path,argv); } /* If anything goes wrong, write the errno to the pipe, where the parent process can collect and print it. */ done: child_errno = errno; full_write(pfds[1],&child_errno,sizeof(child_errno)); _exit(1); } else { /* Now clear the pipe. If it contains an int, then the process forked, but was unable to exec. Set the reason appropriately. Otherwise, live with what we have. */ close(pfds[1]); actual = full_read(pfds[0],&child_errno,sizeof(int)); close(pfds[0]); *status = s.status; *pid = s.pid; if(actual==sizeof(int)) { return TIMED_EXEC_NOEXEC; } else if(fresult==MULTI_FORK_SUCCESS) { return TIMED_EXEC_SUCCESS; } else if(fresult==MULTI_FORK_TIMEOUT) { return TIMED_EXEC_TIMEOUT; } else { return TIMED_EXEC_FAILURE; } } }
static int builtin_exec( int line, int argc, char **argv, time_t stoptime ) { execvp(argv[1],&argv[1]); ftsh_error(FTSH_ERROR_FAILURE,line,"exec: %s failed: %s",argv[1],strerror(errno)); return 0; }
char * variable_subst( char *value, int line ) { char *subvalue, *newvalue; char *dollar, *start, *end; char terminator, oldend; int length; int withquotes = 0; int escape = 0; while(1) { /* Find a non-escaped dollar */ for( dollar=value; *dollar; dollar++ ) { if(escape) { escape = 0; } else { if(*dollar=='\\') { escape = 1; } else if(*dollar=='$') { break; } } } /* If we didn't find it, stop. */ if(!*dollar) return value; /* Is the variable name bracketed? */ if( *(dollar+1)=='{' ) { start = dollar+2; terminator = '}'; } else if( *(dollar+1)=='(' ) { start = dollar+2; terminator = ')'; } else { start = dollar+1; terminator = 0; } if(terminator) { end = strchr(start,terminator); } else { for(end=start;ISVALID(*end);end++) { /* nothing */ } } if(terminator && !end) { ftsh_error(FTSH_ERROR_FAILURE,line,"variable reference began with %c but didn't end",*(dollar+1)); return 0; } if((end-start)<1) { ftsh_error(FTSH_ERROR_FAILURE,line,"empty variable reference"); return 0; } withquotes = (dollar>value && *(dollar-1)=='\"') && (*end) && (terminator ? *(end+1)=='\"' : *end=='\"' ); oldend = *end; *end = 0; subvalue = variable_get(start,line,withquotes); *end = oldend; if(!subvalue) { subvalue = xxstrdup(""); } length = strlen(value) - (end-dollar) + strlen(subvalue) + 1; newvalue = malloc(length); if(!newvalue) { free(subvalue); free(value); return 0; } *dollar = 0; strcpy(newvalue,value); strcat(newvalue,subvalue); if(terminator && *end) { strcat(newvalue,end+1); } else { strcat(newvalue,end); } free(subvalue); free(value); value = newvalue; } }
static int ast_try_body_execute( struct ast_try *t, time_t stoptime ) { int i=0; int result=0; ftsh_integer_t loops=0; int interval = ftsh_expmin; int sleeptime; time_t starttime, every; if(t->time_limit) { ftsh_integer_t timeout; if(expr_to_integer(t->time_limit->expr,&timeout,stoptime)) { timeout *= t->time_limit->units; if(stoptime==0) { stoptime = timeout+time(0); } else { stoptime = MIN(stoptime,timeout+time(0)); } } else { return 0; } } if(t->every_limit) { ftsh_integer_t i; if(expr_to_integer(t->every_limit->expr,&i,stoptime)) { every = i*t->every_limit->units; } else { return 0; } } else { every = 0; } if(t->loop_limit) { if(expr_to_integer(t->loop_limit->expr,&loops,stoptime)) { /* no problem */ } else { return 0; } } if(!t->time_limit && ! t->loop_limit) { loops = 1; } while(1) { ftsh_error(FTSH_ERROR_STRUCTURE,t->try_line,"TRY attempt %d",i); starttime = time(0); if(ast_group_execute(t->body,stoptime)) { result = 1; break; } i++; if( stoptime && (time(0) > stoptime) ) { ftsh_error(FTSH_ERROR_FAILURE,t->try_line,"TRY time expired"); result = 0; break; } if(loops && (i>=loops)) { ftsh_error(FTSH_ERROR_FAILURE,t->try_line,"TRY loop limit reached"); result = 0; break; } if(every) { ftsh_error(FTSH_ERROR_STRUCTURE,t->end_line,"TRY restricted to EVERY %s seconds",every); sleeptime = starttime+every-time(0); if(sleeptime<0) sleeptime = 0; ftsh_error(FTSH_ERROR_STRUCTURE,t->end_line,"TRY sleeping for %d seconds",sleeptime); } else { if(ftsh_exprand) { sleeptime = interval*(1 + 1.0*rand()/RAND_MAX); } else { sleeptime = interval; } ftsh_error(FTSH_ERROR_STRUCTURE,t->end_line,"TRY sleeping for %d seconds (base %d)",sleeptime,interval); interval = MIN(interval*ftsh_expfactor,ftsh_expmax); } sleep_for(sleeptime); } return result; }