/*  

    YABASIC ---  a simple Basic Interpreter
    written by Marc-Oliver Ihm 1995-2004
    homepage: www.yabasic.de
    
    function.c --- code for functions
    
    This file is part of yabasic and may be copied only 
    under the terms of either the Artistic License or 
    the GNU General Public License (GPL), both of which 
    can be found at www.yabasic.de

*/


/* ------------- includes ---------------- */
#include "YabInterface.h"
#ifndef YABASIC_INCLUDED
#include "yabasic.h"       /* all prototypes and structures */
#endif


/* ------------- external references ---------------- */

extern int mylineno;   /* current line number */
extern int yyparse();  /* call bison parser */


/* ------------- local functions ---------------- */

static char *fromto(char *,int,int); /* get portion of string (mid$ et al) */
static void clear_buff(); /* clear system-input buffers */
static void store_buff(char *,int); /* store system-input buffer */
static int do_glob(char *,char *); /* actually do the globbing */
static double other2dec(char *,int); /* convert hex to decimal */
static char *dec2other(double,int); /* convert decimal to hex */
static double peek(char *, YabInterface *); /* peek into internals */
static char *peek2(char *,struct command *); /* peek into internals */
static char *peek3(char *,char *); /* peek into internals */
static int peekfile(int); /* read a byte from stream */
static char *do_system(char *); /* executes command via command.com */
static int do_system2(char *); /* execute command as system */


/* ------------- global variables ---------------- */

struct command *lastdata=NULL; /* used to associate all data-commands with each others */
static struct buff_chain *buffroot; /* start of sys-input buffer */
static struct buff_chain **buffcurr; /* current entry in buff_chain */
static int buffcount; /* number of filled buffers */


/* ------------- subroutines ---------------- */


void token(struct command *cmd) /* extract token from variable */
{
  int split;
  struct stackentry *s;
  struct symbol *sym;
  struct array *ar;
  int num=0,i;
  char *p,*q;
  char **pp;
  char *del,*line;
  int wasdel,isdel;

  
  if (cmd->type==cSPLIT2 || cmd->type==cTOKEN2)
    del=pop(stSTRING)->pointer;
  else
    del=" \t";
  split=(cmd->type==cSPLIT || cmd->type==cSPLIT2);
  s=pop(stSTRINGARRAYREF);
  line=pop(stSTRING)->pointer;
  sym=get_sym(s->pointer,syARRAY,amSEARCH);
  if (!sym || !sym->pointer) {
    sprintf(string,"array '%s()' is not defined",strip(s->pointer));
    error(ERROR,string);
    goto token_done;
  }
  ar=sym->pointer;
  if (ar->dimension>1) {
    error(ERROR,"only one dimensional arrays allowed");
    goto token_done;
  }

  /* count number of tokens */
  isdel=TRUE;
  if (split && *line) num=1;
  else num=0;
  for(p=line;*p;++p) {
    wasdel=isdel;
    isdel=(strchr(del,*p)!=NULL);
    if (split) {
      if (isdel) num++;
    } else {
      if (isdel && isdel!=wasdel) num++;
    }
  }
  if (!split && !isdel) num++;

  /* free previous array content */
  for(i=0;i<ar->bounds[0];i++) free(((char **)ar->pointer)[i]);
  free(ar->pointer);
  ar->pointer=my_malloc((num+1)*sizeof(char *));
  pp=ar->pointer;
  pp[0]=my_strdup("");
  
  /* extract tokens */
  i=1;
  isdel=TRUE;
  if (*line) {
    for(p=q=line;;p++) {
      wasdel=isdel;
      isdel=(strchr(del,*p)!=NULL) || !*p;
      if ((split && isdel) || (!split && (isdel && isdel!=wasdel))) {
	while(strchr(del,*q) && q<p) q++;
	pp[i]=my_strndup(q,p-q+1);
	pp[i][p-q]='\0';
	q=p+1;
	i++;
      }
      if (!*p) break;
    }
  }
      
  ar->bounds[0]=num+1;
 token_done:
  s=push();
  s->type=stNUMBER;
  s->value=num;
}


void tokenalt(struct command *cmd) /* extract token from variable with alternate semantics */
{
  char *del; /* delimiter for strings */
  struct stackentry *t;
  char *old,*new,*tok;
  int split;
  
  if (cmd->type==cSPLITALT2 || cmd->type==cTOKENALT2)
    del=pop(stSTRING)->pointer;
  else
    del=" \t";
  split=(cmd->type==cSPLITALT || cmd->type==cSPLITALT2);
  
  t=pop(stSTRING);
  old=t->pointer;
  t->pointer=NULL; /* prevent push from freeing the memory */
  t=push();
  t->type=stSTRING;
  new=old;
  tok=NULL;
  while(*new) {
    if (!tok && (!strchr(del,*new) || split)) tok=new; /* found start of token */
    if (tok && strchr(del,*new)) break; /* found end of token */
    new++;
  }
  if (*new) {
    *new='\0'; /* terminate token */
    new++;
    if (!split) {
      while(*new) {
        if (!strchr(del,*new)) break; /* found start of next token */
        new++;
      }
    }
  }
  t->pointer=my_strdup(tok?tok:""); /* copy token */
  /* move rest of string */
  while(*new) {
    *old=*new;
    old++;
    new++;
  };
  *old='\0';
}


void glob(void) /* check, if pattern globs string */
{
  char *str,*pat;
  struct stackentry *stack;
  int res;
  
  pat=(char *)pop(stSTRING)->pointer;
  str=(char *)pop(stSTRING)->pointer;
  
  res=do_glob(str,pat);
  stack=push();
  stack->value=res;
  stack->type=stNUMBER;
}


static int do_glob(char *str,char *pat) /* actually do the globbing */
{
  int res;
  
  if (infolevel>=DEBUG) {
    sprintf(string,"globbing '%s' on '%s'",str,pat);
    error(DEBUG,string);
  }
  if (*pat=='\0' && *str=='\0') return TRUE;
  else if (*pat=='\0') return FALSE;
  else if (*pat=='?' && *str=='\0') return FALSE;
  else if (*pat=='?') {
    if (*str=='\0') return FALSE;
    pat++;
    str++;
  }
  else if (*pat=='*') {
    pat++;
    res=FALSE;
    while(*str && !(res=do_glob(str,pat))) str++;
    if (res) return TRUE;
  }
  else if (*str=='\0') return FALSE;
  else {
    while(*pat && *pat!='?' && *pat!='*') {
      if (*pat!=*str) return FALSE;
      str++;
      pat++;
    }
  }
  return do_glob(str,pat);
}


void concat() /* concatenates two strings from stack */
{
  struct stackentry *c;
  char *aa,*bb,*cc;
  
  aa=pop(stSTRING)->pointer;
  bb=pop(stSTRING)->pointer;
  cc=(char *) my_malloc(sizeof(char)*(strlen(aa)+strlen(bb)+1));
  strcpy(cc,bb);
  strcat(cc,aa);
  c=push();
  c->type=stSTRING;
  c->pointer=cc;
}  


void create_changestring(int type) /* create command 'changestring' */
{
  struct command *cmd;
  
  cmd=add_command(cCHANGESTRING,FALSE);
  cmd->args=type;
}


void changestring(struct command *current) /* changes a string */
{
  int type,a2,a3;
  char *newpart;
  char *oldstring;
  int i,len;
  struct stackentry *a1;
  
  type=current->args;
  newpart=pop(stSTRING)->pointer;
  if (type>fTWOARGS) a3=(int)pop(stNUMBER)->value;
  if (type>fONEARGS) a2=(int)pop(stNUMBER)->value;
  a1=pop(stSTRING);
  oldstring=a1->pointer; 
  a1->pointer=NULL; /* this prevents push from freeing the memory */
  
  if (!oldstring || !*oldstring) return;
  switch(type) {
  case fMID:
    for(i=1;i<a2+a3;i++) {
      if (!oldstring[i-1]) break;
      if (i>=a2) {
        if (!newpart[i-a2]) break;
        oldstring[i-1]=newpart[i-a2];
      }
    }
    break;
  case fMID2:
    len=strlen(oldstring);
    for(i=1;i<=len;i++) {
      if (!oldstring[i-1]) break;
      if (i>=a2) {
        if (!newpart[i-a2]) break;
        oldstring[i-1]=newpart[i-a2];
      }
    }
    break;
  case fLEFT:
    for(i=1;i<=a2;i++) {
      if (!oldstring[i-1] || !newpart[i-1]) break;
      oldstring[i-1]=newpart[i-1];
    }
    break;
  case fRIGHT:
    len=strlen(oldstring);
    for(i=1;i<=len;i++) {
      if (i>len-a2) {
        if (!newpart[i-1-len+a2]) break;
        oldstring[i-1]=newpart[i-1-len+a2];
      }
    }
    break;
  }
}


void create_function(int type) /* create command 'function' */
/* type can be sin,cos,mid$ ... */
{
  struct command *cmd;
  
  cmd=add_command(cFUNCTION,FALSE);
  cmd->args=type;  
}


void function(struct command *current,YabInterface* yab) /* performs a function */
{
  struct stackentry *stack,*a1,*a2,*a3,*a4,*a5,*a6;
  char *pointer;
  double value;
  time_t datetime;
  int type,result,len,start,i,max, linenum;
  char *str,*str2, *str3, *str4, *str5;

  a3=NULL;
  a4=NULL;
  a5=NULL;
  a6=NULL;
  type=current->args;
  if (type>fFIVEARGS) a6=pop(stSTRING_OR_NUMBER);
  if (type>fFOURARGS) a5=pop(stSTRING_OR_NUMBER);
  if (type>fTHREEARGS) a4=pop(stSTRING_OR_NUMBER);
  if (type>fTWOARGS) a3=pop(stSTRING_OR_NUMBER);
  if (type>fONEARGS) a2=pop(stSTRING_OR_NUMBER);
  if (type>fZEROARGS) a1=pop(stSTRING_OR_NUMBER);
  linenum = current->line;
  
  switch (type) {
  case fSIN:
    value=sin(a1->value);
    result=stNUMBER;
    break;
  case fASIN:
    value=asin(a1->value);
    result=stNUMBER;
    break;
  case fCOS:
    value=cos(a1->value);
    result=stNUMBER;
    break;
  case fACOS:
    value=acos(a1->value);
    result=stNUMBER;
    break;
  case fTAN:
    value=tan(a1->value);
    result=stNUMBER;
    break;
  case fATAN:
    value=atan(a1->value);
    result=stNUMBER;
    break;
  case fEXP:
    value=exp(a1->value);
    result=stNUMBER;
    break;
  case fLOG:
    value=log(a1->value);
    result=stNUMBER;
    break;
  case fLOG2:
    value=log(a1->value)/log(a2->value);
    result=stNUMBER;
    break;
  case fLEN:
    value=(double) strlen(a1->pointer);
    result=stNUMBER;
    break;
  case fSTR:
    sprintf(string,"%g",a1->value);
    pointer=my_strdup(string);
    result=stSTRING;
    break;
  case fSTR2:
  case fSTR3:
    result=stSTRING;
    if (!myformat(string,a1->value,a2->pointer,a3?a3->pointer:NULL)) {
      pointer=my_strdup("");
      sprintf(string,"'%s' is not a valid format",(char *)a2->pointer);
      error(ERROR,string);
      break;
    }
    pointer=my_strdup(string);
    break;
  case fSQRT:
    value=sqrt(a1->value);
    result=stNUMBER;
    break;
  case fSQR:
    value=a1->value*a1->value;
    result=stNUMBER;
    break;
  case fINT:
    if (a1->value<0)
      value=-floor(-a1->value);
    else
      value=floor(a1->value);
    result=stNUMBER;
    break;
  case fFRAC:
    if (a1->value<0)
      value=a1->value+floor(-a1->value);
    else
      value=a1->value-floor(a1->value);
    result=stNUMBER;
    break;
  case fABS:
    value=fabs(a1->value);
    result=stNUMBER;
    break;
  case fSIG:
    if (a1->value<0) value=-1.;
    else if (a1->value>0) value=1.;
    else value=0.;
    result=stNUMBER;
    break;
  case fMOD:
    value=a1->value-a2->value*(int)(a1->value/a2->value);
    result=stNUMBER;
    break;
  case fRAN:
    value=a1->value*((double)rand()/RAND_MAX);
    result=stNUMBER;
    break;
  case fRAN2:
    value=(double)rand()/RAND_MAX;
    result=stNUMBER;
    break;
  case fMIN:
    if (a1->value>a2->value)
      value=a2->value;
    else
      value=a1->value;
    result=stNUMBER;
    break;
  case fMAX:
    if (a1->value>a2->value)
      value=a1->value;
    else
      value=a2->value;
    result=stNUMBER;
    break;
  case fVAL:
    i=sscanf((char *) a1->pointer,"%lf",&value);
    if (i!=1) value=0;
    result=stNUMBER;
    break;
  case fATAN2:
    value=atan2(a1->value,a2->value);
    result=stNUMBER;
    break;
  case fLEFT:
    str=a1->pointer;
    len=(int)a2->value;
    pointer=fromto(str,0,len-1);
    result=stSTRING;
    break;
  case fRIGHT:
    str=a1->pointer;
    max=strlen(str);
    len=(int)a2->value;
    pointer=fromto(str,max-len,max-1);
    result=stSTRING;
    break;
  case fMID:
    str=a1->pointer;
    start=(int)a2->value;
    len=(int)a3->value;
    pointer=fromto(str,start-1,start+len-2);
    result=stSTRING;
    break;
  case fMID2:
    str=a1->pointer;
    start=(int)a2->value;
    pointer=fromto(str,start-1,strlen(str));
    result=stSTRING;
    break;
  case fINKEY:
    pointer=inkey(a1->value);
    result=stSTRING;
    break;
  case fAND:
    value=(int)a1->value & (int)a2->value;
    result=stNUMBER;
    break;
  case fOR:
    value=(int)a1->value | (int)a2->value;
    result=stNUMBER;
    break;
  case fEOR:
    value=(int)a1->value ^ (int)a2->value;
    result=stNUMBER;
    break;
/*
  case fMOUSEX:
    getmousexybm(a1->pointer,&i,NULL,NULL,NULL);
    value=i;
    result=stNUMBER;
    break;
  case fMOUSEY:
    getmousexybm(a1->pointer,NULL,&i,NULL,NULL);
    value=i;
    result=stNUMBER;
    break;
  case fMOUSEB:
    getmousexybm(a1->pointer,NULL,NULL,&i,NULL);
    value=i;
    result=stNUMBER;
    break;
  case fMOUSEMOD:
    getmousexybm(a1->pointer,NULL,NULL,NULL,&i);
    value=i;
    result=stNUMBER;
    break;*/
  case fCHR:
    pointer=my_malloc(2);
    i=(int)floor(a1->value);
    if (i>255 || i<0) {
      sprintf(string,"can't convert %g to character",a1->value);
      error(ERROR,string);
      return;
    }
    pointer[1]='\0';
    pointer[0]=(unsigned char)i;
    result=stSTRING;
    break;
  case fASC:
    value=((unsigned char *)a1->pointer)[0];
    result=stNUMBER;
    break;
  case fBIN:
    pointer=dec2other(a1->value,2);
    result=stSTRING;
    break;
  case fHEX:
    pointer=dec2other(a1->value,16);
    result=stSTRING;
    break;
  case fDEC:
    value=other2dec(a1->pointer,16);
    result=stNUMBER;
    break;
  case fDEC2:
    value=other2dec(a1->pointer,(int)(a2->value));
    result=stNUMBER;
    break;
  case fUPPER:
    str=a1->pointer;
    pointer=my_malloc(strlen(str)+1);
    i=-1;
    do {
      i++;
      pointer[i]=toupper((int)str[i]);
    } while(pointer[i]);
    result=stSTRING;
    break;
  case fLOWER:
    str=a1->pointer;
    pointer=my_malloc(strlen(str)+1);
    i=-1;
    do {
      i++;
      pointer[i]=tolower((int)str[i]);
    } while(pointer[i]);
    result=stSTRING;
    break;
  case fLTRIM:
    str=a1->pointer;
    while(isspace(*str)) str++;
    pointer=my_strdup(str);
    result=stSTRING;
    break;
  case fRTRIM:
    str=a1->pointer;
    i=strlen(str)-1;
    while(isspace(str[i]) && i>=0) i--;
    str[i+1]='\0';
    pointer=my_strdup(str);
    result=stSTRING;
    break;
  case fTRIM:
    str=a1->pointer;
    i=strlen(str)-1;
    while(isspace(str[i]) && i>=0) i--;
    str[i+1]='\0';
    while(isspace(*str)) str++;
    pointer=my_strdup(str);
    result=stSTRING;
    break;
  case fINSTR:
    str=a1->pointer;
    str2=a2->pointer;
    if (*str2)
      pointer=strstr(str,str2);
    else
      pointer=NULL;
    if (pointer==NULL) 
      value=0;
    else
      value=pointer-str+1;
    result=stNUMBER;
    break;   
  case fINSTR2:
    str=a1->pointer;
    str2=a2->pointer;
    start=(int)a3->value;
    if (start>strlen(str)) {
      value=0;
    } else {
      if (start<1) start=1;
      pointer=strstr(str+start-1,str2);
      if (pointer==NULL) 
	value=0;
      else
	value=pointer-str+1;
    }
    result=stNUMBER;
    break;   
  case fRINSTR:
    str=a1->pointer;
    str2=a2->pointer;
    len=strlen(str2);
    for(i=strlen(str)-1;i>=0;i--) if (!strncmp(str+i,str2,len)) break;
    value=i+1;
    result=stNUMBER;
    break;   
  case fRINSTR2:
    str=a1->pointer;
    str2=a2->pointer;
    len=strlen(str2);
    start=(int)a3->value;
    if (start<1) {
      value=0;
    } else {
      if (start>strlen(str)) start=strlen(str);
      for(i=start-1;i;i--) if (!strncmp(str+i,str2,len)) break;
      value=i+1;
    }
    result=stNUMBER;
    break;   
  case fDATE:
    pointer=my_malloc(100);
    time(&datetime);
    strftime(pointer,100,"%w-%m-%d-%Y-%a-%b",localtime(&datetime));
    result=stSTRING;
    break;
  case fTIME:
    pointer=my_malloc(100);
    time(&datetime);
    strftime(pointer,100,"%H-%M-%S",localtime(&datetime));
    sprintf(pointer+strlen(pointer),"-%d",
      (int)(time(NULL)-compilation_start));
    result=stSTRING;
    break;
  case fSYSTEM:
    str=a1->pointer;
    pointer=do_system(str);
    result=stSTRING;
    break;
  case fSYSTEM2:
    str=a1->pointer;
    value=do_system2(str);
    result=stNUMBER;
    break;
  case fPEEK:
    str=a1->pointer;
    value=peek(str, yab);
    result=stNUMBER;
    break;
  case fPEEK2:
    str=a1->pointer;
    pointer=peek2(str,current);
    result=stSTRING;
    break;
  case fPEEK3:
    str=a1->pointer;
    str2=a2->pointer;
    pointer=peek3(str,str2);
    result=stSTRING;
    break;
  case fPEEK4:
    value=peekfile((int)a1->value);
    result=stNUMBER;
    break;
  case fGETCHAR:
    pointer=getchars((int)a1->value,(int)a2->value,(int)a3->value,(int)a4->value);
    result=stSTRING;
    break;
  case fTELL:
    i=(int)(a1->value);
    if (badstream(i,0)) return;
    if (!(stream_modes[i] & (smREAD | smWRITE| smREADWRITE))) {
      sprintf(string,"stream %d not opened",i);
      error(ERROR,string);
      value=0;
    } else {
      value=ftell(streams[i]);
    }
    result=stNUMBER;
    break;
  case fMESSAGE:
    pointer = getmessages(yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fMOUSE:
    str = a1->pointer;
    pointer = getmousemessages(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTRANSLATE:
    str = a1->pointer;
    pointer = gettranslation(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fMENUTRANSLATE:
    str = a1->pointer;
    pointer = getmenutranslation(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTEXTGET:
    str = a1->pointer;
    pointer = textget(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTEXTGET3:
    str = a1->pointer;
    pointer = textget3(str,a2->value,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTEXTGET6:
    str = a1->pointer;
    str2 = a2->pointer;
    pointer = textget6(str,str2,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTEXTCONTROLGET:
    str = a1->pointer;
    pointer = textcontrolget(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fNUMWINDOWS:
    value = numwindows();
    result = stNUMBER;
    break;
  case fISMOUSEIN:
    str = a1->pointer;
    value = ismousein(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fMOUSEMOVE: //vasper
    pointer = getmousein(yab,linenum, current->lib->s); 
    result = stSTRING;
    break;    
  case fDRAWIMAGE:
    str=a3->pointer;
    str2=a4->pointer;
    value = createimage(a1->value,a2->value,str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDRAWIMAGE2:
    str=a5->pointer;
    str2=a6->pointer;
    value = createimage2(a1->value,a2->value,a3->value,a4->value,str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDRAWSVG:
    str=a5->pointer;
    str2=a6->pointer;
    value = createsvg(a1->value,a2->value,a3->value,a4->value,str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fLOAD:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    pointer = getloadfilepanel(str,str2,str3,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fSAVE:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    str4=a4->pointer;
    pointer = getsavefilepanel(str,str2,str3,str4,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fKEYBOARD:
    str=a1->pointer;
    pointer = keyboardmessages(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fCLIPBOARDPASTE:
    pointer = clipboardpaste(yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fCOLUMNBOXGET:
    str=a1->pointer;
    pointer = columnboxget(str,a2->value,a3->value,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fLISTBOXGET:
    str=a1->pointer;
    pointer = listboxget(str,a2->value,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTREEBOXGET:
    str=a1->pointer;
    pointer = treeboxget(str,a2->value,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fTEXTGET2:
    str=a1->pointer;
    str2=a2->pointer;
    value = textget2(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTEXTGET4:
    str=a1->pointer;
    str2=a2->pointer;
    value = textget4(str,str2,a3->value,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTEXTGET5:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    value = textget5(str,str2,str3,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fPOPUPMENU:
    str=a3->pointer;
    str2=a4->pointer;
    pointer = popupmenu(a1->value, a2->value, str, str2,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fSCROLLBARGET:
    str=a1->pointer;
    str2=a2->pointer;
    value = scrollbarget(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fSPLITVIEWGET:
    str=a1->pointer;
    str2=a2->pointer;
    value = splitviewget(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fSTACKVIEWGET:
    str=a1->pointer;
    value = stackviewget(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTABVIEWGET:
    str=a1->pointer;
    value = tabviewget(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fSPINCONTROLGET:
    str=a1->pointer;
    value = spincontrolget(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fLISTBOXCOUNT:
    str=a1->pointer;
    value = listboxcount(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTREEBOXCOUNT:
    str=a1->pointer;
    value = treeboxcount(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDROPBOXCOUNT:
    str=a1->pointer;
    value = dropboxcount(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDROPBOXGET:
    str=a1->pointer;
    pointer = dropboxget(str,a2->value,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fDRAWGET3:
    str=a1->pointer;
    pointer = drawget3(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fSLIDERGET:
    str=a1->pointer;
    value = sliderget(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fCOLORCONTROLGET:
    str=a1->pointer;
    str2=a2->pointer;
    value = colorcontrolget(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fCALENDAR:
    str=a1->pointer;
    pointer = calendar2(str,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fCOLUMNBOXCOUNT:
    str=a1->pointer;
    value = columnboxcount(str,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fWINDOWGET:
    str=a1->pointer;
    str2=a2->pointer;
    value = windowgetnum(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fVIEWGET: //vasper
    str=a1->pointer;
    str2=a2->pointer;
    value = viewgetnum(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;    
  case fDRAWGET1:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    value = drawget1(str,str2,str3,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDRAWGET2:
    str=a1->pointer;
    str2=a2->pointer;
    value = drawget2(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDRAWGET4:
    str=a3->pointer;
    str2=a4->pointer;
    value = drawget4(a1->value, a2->value, str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fLISTBOXGETNUM:
    str=a1->pointer;
    value = listboxgetnum(str, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fDROPBOXGETNUM:
    str=a1->pointer;
    value = dropboxgetnum(str, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTREEBOXGETNUM:
    str=a1->pointer;
    value = treeboxgetnum(str, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fCOLUMNBOXGETNUM:
    str=a1->pointer;
    value = columnboxgetnum(str, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fBITMAPLOAD:
    str=a1->pointer;
    str2=a2->pointer;
    value = bitmapload(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fBITMAPGET:
    str=a1->pointer;
    str2=a2->pointer;
    value = bitmapgetnum(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fBITMAPCOLOR:
    str = a3->pointer;
    str2 = a4->pointer;
    value = bitmapcolor(a1->value, a2->value, str, str2, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fBITMAPSAVE:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    value = bitmapsave(str,str2,str3,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fSOUND:
    str=a1->pointer;
    value = sound(str, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTREEBOXGETOPT:
    str=a1->pointer;
    str2=a2->pointer;
    value = treeboxgetopt(str, str2, a3->value, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTHREADKILL:
    str=a1->pointer;
    value = threadkill(str, a2->value, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fTHREADGET:
    str=a1->pointer;
    str2=a2->pointer;
    value = threadget(str, str2, yab, linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fALERT:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    str4=a4->pointer;
    str5=a5->pointer;
    value = newalert(str,str2,str3,str4,str5,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fPRINTER:
    str=a1->pointer;
    str2=a2->pointer;
    str3=a3->pointer;
    value = printer(str,str2,str3,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fISCOMPUTERON:
    value = iscomputeron(yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fMESSAGESEND:
    str=a1->pointer;
    str2=a2->pointer;
    value = messagesend(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  case fATTRIBUTEGET1:
    str=a1->pointer;
    str2=a2->pointer;
    pointer = attributeget1(str,str2,yab,linenum, current->lib->s);
    result = stSTRING;
    break;
  case fATTRIBUTEGET2:
    str=a1->pointer;
    str2=a2->pointer;
    value = attributeget2(str,str2,yab,linenum, current->lib->s);
    result = stNUMBER;
    break;
  default:
    error(ERROR,"function called but not implemented");
    return;
  }
  
  stack=push();
  /* copy result */
  stack->type=result;
  if (result==stSTRING)
    stack->pointer=pointer;
  else
    stack->value=value;
}


static int do_system2(char *cmd) /* execute command as system */
{
#ifdef UNIX
  int ret;
  #ifdef BUILD_NCURSES
  if (curinized) reset_shell_mode();
  #endif
  ret=system(cmd);
  #ifdef BUILD_NCURSES
  if (curinized) reset_prog_mode();
  #endif
  return ret;
#else
  STARTUPINFO start;
  PROCESS_INFORMATION proc;
  DWORD ec; /* exit code */
  SECURITY_ATTRIBUTES prosec;
  SECURITY_ATTRIBUTES thrsec;
  char *comspec;
		
  ZeroMemory(&prosec,sizeof(prosec));
  prosec.nLength=sizeof(prosec);
  prosec.bInheritHandle=TRUE;
  ZeroMemory(&thrsec,sizeof(thrsec));
  thrsec.nLength=sizeof(thrsec);
  thrsec.bInheritHandle=TRUE;
  ZeroMemory(&start,sizeof(start));
  start.cb=sizeof(STARTUPINFO); 
  start.dwFlags=STARTF_USESTDHANDLES;
  start.hStdOutput=GetStdHandle(STD_OUTPUT_HANDLE);
  start.hStdError=GetStdHandle(STD_ERROR_HANDLE);
  start.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
  comspec=getenv("COMSPEC");
  if (!comspec) comspec="command.com";
  sprintf(string,"%s /C %s",comspec,cmd);
  if (!CreateProcess(NULL,string,&prosec,&thrsec,TRUE,0,
    NULL,NULL,&start,&proc)) {
    sprintf(string,"couldn't execute '%s'",cmd);
    error(ERROR,string);
    return -1;
  }
  WaitForSingleObject(proc.hProcess,INFINITE);
  if (!GetExitCodeProcess(proc.hProcess,&ec)) ec=-1;
  CloseHandle(proc.hProcess);
  CloseHandle(proc.hThread);
  return ec;
#endif  
}


static void clear_buff() /* clear system-input buffers */
{
  buffcurr=&buffroot;
  buffcount=0;
}


static void store_buff(char *buff,int len) /* store system-input buffer */
{
  *buffcurr=my_malloc(sizeof(struct buff_chain));
  memcpy((*buffcurr)->buff,buff,SYSBUFFLEN+1);
  (*buffcurr)->len=len;
  buffcurr=&((*buffcurr)->next);
  buffcount++;
}


char *recall_buff() /* recall store buffer */
{
  struct buff_chain *curr,*old;
  char *result;
  int done,len;
		
  result=(char *)my_malloc(buffcount*(SYSBUFFLEN+1));
  curr=buffroot;
  len=0;
  for(done=0;done<buffcount;done++) {
    memcpy(result+len,curr->buff,SYSBUFFLEN);
    len+=curr->len;
    old=curr;
    curr=curr->next;
    my_free(old);
  }
  return result;
}


static char *do_system(char *cmd) /* executes command via command.com */
{
  static char buff[SYSBUFFLEN+1]; /* buffer to store command */
  int len; /* number of bytes read */
#ifdef UNIX
  FILE *p; /* points to pipe */
  int c; /* char read from pipe */
#else
  int ret;
  STARTUPINFO start;
  PROCESS_INFORMATION proc;
  HANDLE piperead,pipewrite; /* both ends of pipes */
  SECURITY_ATTRIBUTES prosec;
  SECURITY_ATTRIBUTES thrsec;
  char *comspec;
#endif
  
  clear_buff();
  
#ifdef UNIX
  p=popen(cmd,"r");
  if (p==NULL) {
    sprintf(string,"couldn't execute '%s'",cmd);
    error(ERROR,string);
    return my_strdup("");
  }
  do {
    len=0;
    while(len<SYSBUFFLEN) {
      c=fgetc(p);
      if (c==EOF) {
        buff[len]='\0';
        break;
      }
      buff[len]=c;
      len++;
    }
    store_buff(buff,len);
  } while(c!=EOF);
  pclose(p);
		
#else	     
  ZeroMemory(&prosec,sizeof(prosec));
  prosec.nLength=sizeof(prosec);
  prosec.bInheritHandle=TRUE;
  ZeroMemory(&thrsec,sizeof(thrsec));
  thrsec.nLength=sizeof(thrsec);
  thrsec.bInheritHandle=TRUE;
		
  /* create pipe for writing */
  CreatePipe(&piperead,&pipewrite,&prosec,0);
		
  ZeroMemory(&start,sizeof(start));
  start.cb=sizeof(STARTUPINFO); 
  start.dwFlags=STARTF_USESTDHANDLES;
  start.hStdOutput=pipewrite;
  start.hStdError=pipewrite;
  start.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
		
  comspec=getenv("COMSPEC");
  if (!comspec) comspec="command.com";
  sprintf(string,"%s /C %s",comspec,cmd);
  if (!CreateProcess(NULL,string,&prosec,&thrsec,TRUE,0,
    NULL,NULL,&start,&proc)) {
    sprintf(string,"couldn't execute '%s'",cmd);
    error(ERROR,string);
    return my_strdup("");
  }
  CloseHandle(pipewrite);
		
  do {
    /* wait for output to arrive */
    if (!ReadFile(piperead,buff,SYSBUFFLEN,(LPDWORD)&len,NULL))
      ret=GetLastError();
    else
      ret=0;
    buff[len]='\0';
    if (len>0) store_buff(buff,len);
  } while(ret!=ERROR_BROKEN_PIPE && ret!=ERROR_HANDLE_EOF);
  CloseHandle(piperead);
  CloseHandle(proc.hProcess);
  CloseHandle(proc.hThread);
#endif
  return recall_buff();
}


void getmousexybm(char *s,int *px,int *py,int *pb,int *pm) /* get mouse coordinates */
{
  int x=0,y=0,b=0,m=0;
  char c;
  
  if (*s) {
    sscanf(s,"MB%d%c+%d:%04d,%04d",&b,&c,&m,&x,&y);
    if (px) *px=x;
    if (py) *py=y;
    if (pb) {
      if (c=='d') 
        *pb=b;
      else
        *pb=-b;
    }
    if (pm) *pm=m;
    return;
  }
/*
  if (px) *px=mousex;
  if (py) *py=mousey;
  if (pb) *pb=mouseb;
  if (pm) *pm=mousemod;*/
}


static char *dec2other(double d,int base) /* convert double to hex or binary number */
{
  int len;
  double dec,dec2;
  char *other;
  int negative=FALSE;
  
  if (d<0) {
    dec2=floor(-d);
    negative=TRUE;
  } else {
    dec2=floor(d);
  }
  len=negative?2:1;
  for(dec=dec2;dec>=base;dec/=base) len++;
  other=my_malloc(len+1);
  other[len]='\0';
  dec=dec2;
  for(len--;len>=0;len--) {
    other[len]="0123456789abcdef"[(int)(floor(dec-base*floor(dec/base)+0.5))];
    dec=floor(dec/base);
  }
  if (negative) other[0]='-';
  return other;
}


static double other2dec(char *hex,int base) /* convert hex or binary to double number */
{
  double dec;
  static char *digits="0123456789abcdef";
  char *found;
  int i,len;
  
  if (base!=2 && base !=16) {
    sprintf(string,"Cannot convert base-%d numbers",base);
    error(ERROR,string);
    return 0.;
  }
  dec=0;
  len=strlen(hex);
  for(i=0;i<len;i++) {
    dec*=base;
    found=strchr(digits,tolower(hex[i]));
    if (!found || found-digits>=base) {
      sprintf(string,"Not a base-%d number: '%s'",base,hex);
      error(ERROR,string);
      return 0.;
    }
    dec+=found-digits;
  }
  return dec;
}


int myformat(char *dest,double num,char *format,char *sep) /* format number according to string */
{
  int i1,i2; /* dummy */
  char c1; /* dummy */
  static char ctrl[6];
  char *found,*form;
  int pre,post,dot,len,i,j,digit,colons,dots;
  int neg=FALSE;
  double ip,fp,round;
  static char *digits="0123456789";
  
  form=format;
  if (*form=='%') { /* c-style format */
    strcpy(ctrl,"+- #0"); /* allowed control chars for c-format */
    form++;
    while((found=strchr(ctrl,*form))!=NULL) {
      *found='?';
      form++;
    } 
    if (sscanf(form,"%u.%u%c%n",&i1,&i2,&c1,&i)!=3 &&
	sscanf(form,"%u.%c%n",&i2,&c1,&i)!=2 &&
	sscanf(form,".%u%c%n",&i2,&c1,&i)!=2 &&
	sscanf(form,"%u%c%n",&i2,&c1,&i)!=2) return FALSE;
    if (!strchr("feEgG",c1) || form[i]) return FALSE;
    /* seems okay, let's print */
    sprintf(dest,format,num);
  } else { /* basic-style format */
    if (num<0) {
      neg=TRUE;
      num=-num;
    }
    colons=0;
    dots=0;
    pre=0;
    post=0;
    for(form=format;*form;form++) {
      if (*form==',') {
	if (dots) return FALSE;
	colons++;
      } else if (*form=='.') {
	dots++;
      } else if (*form=='#') {
	if (dots) 
	  post++;
	else
	  pre++;
      } else {
	return FALSE;
      }
    }
    if (dots>1) return FALSE;
    len=strlen(format);
    dest[len]='\0';
    round=0.5;
    for(i=0;i<post;i++) round/=10.;
    if (fabs(num)<round) neg=FALSE;
    num+=round;
    ip=floor(num);
    fp=num-ip;
    if (fp>1 || fp<0) fp=0;
    dest[pre+colons]=format[pre+colons];
    if ((int)ip) {
      for(i=pre+colons-1;i>=0;i--) {
	if (neg && !(int)ip) {
	  neg=0;
	  dest[i]='-';
	} else {
	  if (format[i]=='#') {
	    digit=((int)ip)%10;
	    ip/=10;
	    if (((int)ip) || digit>0) 
	      dest[i]=digits[digit];
	    else
	      dest[i]=' ';
	  } else {
	    if ((int)ip)
	      dest[i]=format[i];
	    else
	      dest[i]=' ';
	  }
	}
      }
    } else {
      i=pre+colons-1;
      dest[i--]='0';
    }
    if ((neg && i<0) || ((int)ip)) {
      strcpy(dest,format);
      return TRUE;
    }
    if (neg) dest[i--]='-';
    for(;i>=0;i--) dest[i]=' ';
    for(i=pre+colons+1;i<len;i++) {
      fp*=10;
      digit=(int)fp;
      fp-=digit;
      dest[i]=digits[digit];
    }
    if (sep && sep[0] && sep[1]) {
      for(i=0;i<len;i++) {
	if (dest[i]==',') dest[i++]=sep[0];
	if (dest[i]=='.') dest[i++]=sep[1];
      }
    }
  }
  return TRUE;
}


static char *fromto(char *str,int from,int to) /* gives back portion of string */
/* from and to can be in the range 1...strlen(str) */
{
  int len,i;
  char *part;
  
  len=strlen(str);
  if (from>to || to<0 || from>len-1) {
    /* give back empty string */
    part=my_malloc(1);
    part[0]='\0';
  }
  else {
    if (from<=0) from=0;
    if (to>=len) to=len-1;
    part=my_malloc(sizeof(char)*(to-from+2)); /* characters and '/0' */
    for(i=from;i<=to;i++) part[i-from]=str[i]; /* copy */
    part[i-from]='\0';
  }
  return part;
}



void mywait() /* wait given number of seconds */
{
  double delay;
  
#ifdef UNIX
  struct timeval tv;
#else	
  MSG msg;
  int timerid;
#endif
  
  delay=pop(stNUMBER)->value;
  if (delay<0) delay=0.;
#ifdef UNIX
  tv.tv_sec=(int)delay;
  tv.tv_usec=(delay-(int)delay)*1000000;
  select(0,NULL,NULL,NULL,&tv);
#else /* WINDOWS */
  timerid=SetTimer(NULL,0,(int)(delay*1000),(TIMERPROC) NULL);
  GetMessage((LPMSG)&msg,NULL,WM_TIMER,WM_TIMER);
  KillTimer(NULL,timerid);
#endif
}


void mybell() /* ring ascii bell */
{
#ifdef UNIX
#ifdef BEOS	
  yi_beep();
#else
  printf("\007");
  fflush(stdout);
#endif
#else /* WINDOWS */
  Beep(1000,100);
#endif
}



void create_poke(char flag) /* create Command 'cPOKE' */
{
  struct command *cmd;
		
  if (flag=='S' || flag=='D')
    cmd=add_command(cPOKEFILE,FALSE);
  else
    cmd=add_command(cPOKE,FALSE);
  cmd->tag=flag;
}


void poke(struct command *cmd) /* poke into internals */
{
  char *dest,*s,c;
  char *sarg=NULL;
  double darg;
		
  if (cmd->tag=='s')
    sarg=pop(stSTRING)->pointer;
  else 
    darg=pop(stNUMBER)->value;
		
  dest=pop(stSTRING)->pointer;
  for(s=dest;*s;s++) *s=tolower((int)*s);
  /*
  if (!strcmp(dest,"fontheight") && !sarg) {
    fontheight=(int)darg;
#ifdef UNIX
    calc_psscale();
#endif
  }
  else if (!strcmp(dest,"font") && sarg) {
    font=my_strdup(sarg);
  } */
  if (!strcmp(dest,"dump") && sarg && !strcmp(sarg,"symbols")) {
    dump_sym();
  }
  else if (!strcmp(dest,"dump") && sarg && 
	   (!strcmp(sarg,"sub") || !strcmp(sarg,"subs") || !strcmp(sarg,"subroutine") || !strcmp(sarg,"subroutines"))) {
    dump_sub(0);
  }
  /*
  else if (!strcmp(dest,"winwidth") && !sarg) {
    winwidth=(int)darg;
    if (winwidth<1) {
      error(ERROR,"winwidth less than 1 pixel");
      return;
    }
#ifdef UNIX
    calc_psscale();
#endif
  }
  else if (!strcmp(dest,"winheight") && !sarg) {
    winheight=(int)darg;
    if (winheight<1) {
      error(ERROR,"winheight less than 1 pixel");
      return;
    }
#ifdef UNIX
    calc_psscale();
#endif
  }
  else if (!strcmp(dest,"textalign") && sarg) {
    if (!check_alignement(sarg)) return;
    strncpy(text_align,sarg,2);
  }
  else if (!strcmp(dest,"windoworigin") && sarg) {
    moveorigin(sarg);
  }*/
  else if (!strcmp(dest,"infolevel") && sarg) {
    c=tolower((int)*sarg);
    switch(c) {
    case 'd': infolevel=DEBUG;break;
    case 'n': infolevel=NOTE;break;
    case 'w': infolevel=WARNING;break;
    case 'e': infolevel=ERROR;break;
    case 'f': infolevel=FATAL;break;
    default:
      error(ERROR,"invalid infolevel");
      return;
    }
    if (infolevel>=DEBUG) {
      sprintf(string,"switching infolevel to '%c'",c);
      error(DEBUG,string);
    }
  }
  else if (!strcmp(dest,"stdout") && sarg) {
    fputs(sarg,stdout);
  }
  else if (!strcmp(dest,"read_controls") && !sarg) {
    read_controls= darg ? 1:0;
  }
  else if (dest[0]=='#') {
    error(ERROR,"don't use quotes when poking into file");
  }
  else {
    error(ERROR,"invalid poke");
  }
  return;
}


void pokefile(struct command *cmd) /* poke into file */
{
  char *sarg=NULL;
  double darg;
  int stream;
		
  if (cmd->tag=='S')
    sarg=pop(stSTRING)->pointer;
  else 
    darg=pop(stNUMBER)->value;
  stream=(int)(pop(stNUMBER)->value);
  
  if (badstream(stream,0)) return;
  
  if (!(stream_modes[stream] & smWRITE)) {
    sprintf(string,"Stream %d not open for writing",stream);
    error(ERROR,string);
    return;
  } 
  if (sarg) {
    fputs(sarg,streams[stream]);
  } else {
    if (darg<0 || darg>255) {
      error(ERROR,"stream poke out of byte range (0..255)");
      return;
    }
    fputc((int)darg,streams[stream]);
  }
}


static double peek(char *dest, YabInterface *yab) /* peek into internals */
{
  char *s;
		
  for(s=dest;*s;s++) *s=tolower((int)*s);
  /*if (!strcmp(dest,"winwidth")) return winwidth;
  else if (!strcmp(dest,"winheight")) return winheight;
  else if (!strcmp(dest,"fontheight")) return fontheight;
  else*/
  if (!strcmp(dest,"screenheight")) return LINES;
  else if (!strcmp(dest,"screenwidth")) return COLS;
  else if (!strcmp(dest, "deskbarposition")) return yi_DeskbarPosition(yab);	//deskbarposition;
  else if (!strcmp(dest, "deskbarexpanded")) return yi_DeskbarExpanded(yab);	//deskbarexpanded;
  else if (!strcmp(dest, "deskbarwidth")) return yi_DeskbarWidth(yab); 			//deskbarwidth;
  else if (!strcmp(dest, "deskbarheight")) return yi_DeskbarHeight(yab);		//deskbarheight;
  else if (!strcmp(dest, "deskbar-x")) return yi_DeskbarX(yab);					//deskbar-x;
  else if (!strcmp(dest, "deskbar-y")) return yi_DeskbarY(yab);					//deskbar-y;
  else if (!strcmp(dest, "desktopwidth")) return yi_DesktopWidth(yab);			//desktopwidth;
  else if (!strcmp(dest, "desktopheight")) return yi_DesktopHeight(yab);		//desktopheight;
  else if (!strcmp(dest, "scrollbarwidth")) return yi_ScrollbarWidth(yab);		// scrollbarwidth
  else if (!strcmp(dest, "menuheight")) return yi_MenuHeight(yab);				// menuheight
  else if (!strcmp(dest, "tabheight")) return yi_TabHeight(yab);				// tabheight
  else if (!strcmp(dest,"argument") || !strcmp(dest,"arguments") ) return yabargc;	// number of arguments
  else if (!strcmp(dest,"version")) return strtod(VERSION,NULL);
  else if (!strcmp(dest,"error")) return errorcode;
  else if (!strcmp(dest,"read_controls")) return read_controls;
  else if (!strcmp(dest,"isbound")) return is_bound;
  
   
  else if (dest[0]=='#') {
    error(ERROR,"don't use quotes when peeking into a file");
    return 0;
  }
 
  sprintf(stderr, "PEEK is set to %s\n", dest);
  error(ERROR,"invalid peek");
  return 0;
}


static int peekfile(int stream) /* read a byte from stream */
{
  if (stream && badstream(stream,0)) return 0;
  if (stream && !(stream_modes[stream] & smREAD | smREADWRITE)) {
    sprintf(string,"stream %d not open for reading",stream);
    error(ERROR,string);
    return 0;
  } 
  return fgetc(stream?streams[stream]:stdin);
}


static char *peek2(char *dest,struct command *curr) /* peek into internals */
{
  char *s;
  
		
  for(s=dest;*s;s++) *s=tolower((int)*s);
  if (!strcmp(dest,"infolevel")) {
    if (infolevel==DEBUG) return my_strdup("debug");
    else if (infolevel==NOTE) return my_strdup("note");
    else if (infolevel==WARNING) return my_strdup("warning");
    else if (infolevel==ERROR) return my_strdup("error");
    else if (infolevel==FATAL) return my_strdup("fatal");
    else return my_strdup("unkown");
  }
  /*
  else if (!strcmp(dest,"textalign")) return my_strdup(text_align);
  else if (!strcmp(dest,"windoworigin")) return my_strdup(winorigin);*/
  else if (!strcmp(dest,"error")) return my_strdup(errorstring);
  else if (!strcmp(dest,"library")) return my_strdup(curr->lib->s);
  else if (!strcmp(dest,"os")) {
#ifdef HAIKU
    return my_strdup("Haiku");
#else
    return my_strdup("BeOS");
#endif
  }
  else if (!strcmp(dest,"directory")) return my_strdup(appdirectory);
  else if (!strcmp(dest,"refsreceived")) return my_strdup(refsRec);
  /* else if (!strcmp(dest,"font")) return my_strdup(font); */
  else if (!strcmp(dest,"argument") || !strcmp(dest,"arguments")) {
    if (yabargc>0) {
      s=yabargv[0];
      yabargc--;
      yabargv++;
    }
    else {
      s="";
    }
    return my_strdup(s);
  }
  else {
    error(ERROR,"invalid peek");
  }
  return my_strdup("");
}


static char *peek3(char *dest,char *cont) /* peek into internals */
{
  char *s;
		
  for(s=dest;*s;s++) *s=tolower((int)*s);
  if (!strcmp(dest,"env") || !strcmp(dest,"environment")) {
    return my_strdup(getenv(cont));
  } else {
    error(ERROR,"invalid peek");
  }
  return my_strdup("");
}


void create_exception(int flag) /* create command 'exception' */
{
  struct command *cmd;  
  
  cmd=add_command(cEXCEPTION,FALSE);
  cmd->args=flag;
}


void exception(struct command *cmd) /* change handling of exceptions */
{
  if (cmd->args) {
    signal(SIGINT,signal_handler); /* enable keyboard interrupt */
#ifdef SIGHUP
    signal(SIGHUP,signal_handler);
#endif
#ifdef SIGQUIT
    signal(SIGQUIT,signal_handler);
#endif
#ifdef SIGABRT
    signal(SIGABRT,signal_handler);
#endif
#ifdef SIGTERM
    signal(SIGTERM,signal_handler);
#endif
  }
  else {
    signal(SIGINT,SIG_IGN); /* ignore keyboard interrupt */
#ifdef SIGHUP
    signal(SIGHUP,SIG_IGN);
#endif
#ifdef SIGQUIT
    signal(SIGQUIT,SIG_IGN);
#endif
#ifdef SIGABRT
    signal(SIGABRT,SIG_IGN);
#endif
#ifdef SIGTERM
    signal(SIGTERM,SIG_IGN);
#endif
  }
  return;
}


void create_restore(char *label) /* create command 'restore' */
{
  struct command *c;
  
  c=add_command(cRESTORE,FALSE);
  c->pointer=my_strdup(label);
}


void restore(struct command *cmd) /* reset data pointer to given label */
{
  struct command *label;
  struct command **datapointer;
  
  datapointer=&(cmd->lib->datapointer);
  if (cmd->type==cRESTORE) { /* first time; got to search the label */
    if (*((char *)cmd->pointer)=='\0') {
      /* no label, restore to first command */
      label=cmd->lib->firstdata;
    } else {
      label=search_label(cmd->pointer,smLABEL|smGLOBAL);
      if (!label) {
	/* did not find label */
	sprintf(string,"can't find label '%s'",(char *)cmd->pointer);
	error(ERROR,string);
	return;
      }
    }
    *datapointer=label;
    if (lastdata) {
      while((*datapointer)->type!=cDATA && (*datapointer)!=cmdhead) {
	*datapointer=(*datapointer)->next;
      }
    }
    cmd->pointer=*datapointer;
    cmd->type=cQRESTORE;
  } else {
    *datapointer=cmd->pointer;
  }
  return;
}


void create_dbldata(double value)  /* create command dbldata */
{
  struct command *c;
  
  c=add_command(cDATA,FALSE);
  c->pointer=my_malloc(sizeof(double));
  if (lastdata) lastdata->nextassoc=c;
  lastdata=c;
  *((double *)c->pointer)=value;
  c->tag='d'; /* double value */
}


void create_strdata(char *value)  /* create command strdata */
{
  struct command *c;
  
  c=add_command(cDATA,FALSE);
  if (lastdata) lastdata->nextassoc=c;
  lastdata=c;
  c->pointer=my_strdup(value);
  c->tag='s'; /* string value */
}


void create_readdata(char type) /* create command readdata */
{
  struct command *cmd;
  
  cmd=add_command(cREADDATA,FALSE);
  cmd->tag=type;
}


void readdata(struct command *cmd) /* read data items */
{
  struct stackentry *read;
  char type;
  struct command **datapointer;
  
  datapointer=&(cmd->lib->datapointer);
  type=cmd->tag;
  while(*datapointer && ((*datapointer)->type!=cDATA || cmd->lib!=(*datapointer)->lib)) {
    *datapointer=(*datapointer)->nextassoc;
  }
  if (!*datapointer) {
    error(ERROR,"run out of data items");
    return;
  }
  if (type!=(*datapointer)->tag) {
    error(ERROR,"type of READ and DATA don't match");
    return;
  }
  read=push();
  if (type=='d') { /* read a double value */
    read->type=stNUMBER;
    read->value= *((double *)(*datapointer)->pointer);}
  else {
    read->type=stSTRING;
    read->pointer=my_strdup((*datapointer)->pointer);
  }
  *datapointer=(*datapointer)->nextassoc; /* next item */
}


void create_dblrelop(char c) /* create command dblrelop */ 
{
  int type;
  
  switch(c) {
  case '=': type=cEQ;break;
  case '!': type=cNE;break;
  case '<': type=cLT;break;
  case '{': type=cLE;break;
  case '>': type=cGT;break;
  case '}': type=cGE;break;
  }
  add_command(type,FALSE);
}


void dblrelop(struct command *type)  /* compare topmost double-values */
{
  double a,b,c;
  struct stackentry *result;
  
  b=pop(stNUMBER)->value;
  a=pop(stNUMBER)->value;
  switch(current->type) {
  case cEQ:c=(a==b);break;
  case cNE:c=(a!=b);break;
  case cLE:c=(a<=b);break;
  case cLT:c=(a<b);break;
  case cGE:c=(a>=b);break;
  case cGT:c=(a>b);break;
  }
  result=push();
  result->value=c;
  result->type=stNUMBER;
}    


void create_strrelop(char c) /* create command strrelop */ 
{
  int type;
  
  switch(c) {
  case '=': type=cSTREQ;break;
  case '!': type=cSTRNE;break;
  case '<': type=cSTRLT;break;
  case '{': type=cSTRLE;break;
  case '>': type=cSTRGT;break;
  case '}': type=cSTRGE;break;
  }
  add_command(type,FALSE);
}


void strrelop(struct command *type)  /* compare topmost string-values */
{
  char *a,*b;
  double c;
  struct stackentry *result;
  
  b=pop(stSTRING)->pointer;
  a=pop(stSTRING)->pointer;
  switch(current->type) {
  case cSTREQ:c=(strcmp(a,b)==0);break;
  case cSTRNE:c=(strcmp(a,b)!=0);break;
  case cSTRLT:c=(strcmp(a,b)<0);break;
  case cSTRLE:c=(strcmp(a,b)<=0);break;
  case cSTRGT:c=(strcmp(a,b)>0);break;
  case cSTRGE:c=(strcmp(a,b)>=0);break;
  }
  result=push();
  result->value=c;
  result->type=stNUMBER;
}    

void switch_compare(void) /* compare topmost values for switch statement */
{
  struct stackentry *result,*first,*second;
  double r=0.;

  first=pop(stANY);
  second=stackhead->prev;
  if ((second->type==stSWITCH_STRING || second->type==stSTRING) && first->type==stSTRING) {
    if (second->type==stSWITCH_STRING) 
      r=1.;
    else 
      r=(strcmp(first->pointer,second->pointer)==0)?1.:0.;
  } else if ((second->type==stSWITCH_NUMBER || second->type==stNUMBER) && first->type==stNUMBER) {
    if (second->type==stSWITCH_NUMBER)
      r=1.;
    else
      r=(first->value==second->value);
  } else {
    error(ERROR,"Cannot mix strings and numbers in a single switch statement");
  }
  result=push();
  result->type=stNUMBER;
  result->value=r;
}


void logical_shortcut(struct command *type)  /* shortcut and/or if possible */
{
  struct stackentry *result;
  double is;

  is=stackhead->prev->value;
  if ((type->type==cORSHORT && is!=0) || (type->type==cANDSHORT && is==0)) {
    result=push();
    error(DEBUG,"logical shortcut taken");
    result->type=stNUMBER;
    result->value=is;
  } else {
    current=current->next;
  }
}


void create_boole(char c) /* create command boole */ 
{
  int type;
  
  switch(c) {
  case '|': type=cOR;break;
  case '&': type=cAND;break;
  case '!': type=cNOT;break;
  }
  add_command(type,FALSE);
}


void boole(struct command *type)  /* perform and/or/not */
{
  int a,b,c;
  struct stackentry *result;
  
  a=(int)pop(stNUMBER)->value;
  if (current->type==cNOT) 
    c=!a;
  else {
    b=(int)pop(stNUMBER)->value;
    if (current->type==cAND)
      c=a&&b;
    else
      c=a||b;
  }
  result=push();
  result->value=c;
  result->type=stNUMBER;
}

