wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
/* See the file wizshell.c, wizshell.h or the files in docs/ for information */

#include "wizshell.h"

/* HISTORY STUFF */
/* set_history_size
 * frees up history and reallocates
 * memory for new <size>
 */
void set_history_size(size)
  int size;
{
  int i;
  char **temp;

  if ((temp=(char **) CALLOC(size,sizeof(char *)))==NULL) {
    ERROR("set_history_size","Could not allocate memory");
    return;
  }
 
  for(i=0;i<envir.history_size; i++)
    FREE(*(envir.hist+i));

  if(envir.hist) {
    fprintf(stdout,"History size set to: %d\n",size);
    FREE(envir.hist);
  }

  envir.history_size=size;
  envir.hist=temp;
  for(i=0; i<size; i++)
/*MUSTDO make this variable..  not MAX_BUF */
    if ((*(envir.hist+i)=(char *) CALLOC(MAX_BUF,sizeof(char)))==NULL)
      FATAL("history:  Could not allocate memory");
  envir.current_hist=0;
}

/* add_history
 * copies command and args in format:
 * <command><space><arg1><space><arg2>...
 * to current history location in history
 */
void add_history(comm)
  char **comm;
{
  int a=0,b=0,c=0;
  char* here=*(envir.hist+envir.current_hist);

  while (comm[a]) {
    while (*(comm[a]+b) !='\0') {
      *(here+c)=*(comm[a]+b);
      b++;
      c++;
    }
  *(here+c)=' ';
  a++;
  b=0;
  c++;
  }
  *(here+c-1)='\0';

  envir.current_hist++;
  if(envir.current_hist>=envir.history_size)
    envir.current_hist=0;
  envir.command_num++;
}

/* get_history
 * gets string added to history as command_num=num
 * returns NULL if num is outside history
 */ 
char *get_history(num)
  int num;
{
  int hist_num;

  if(num<0) num+=envir.command_num;
  if(num<envir.command_num-envir.history_size) return NULL;
  if(num>=envir.command_num) return NULL;
  if(num<0) return NULL;
  hist_num=envir.current_hist-envir.command_num+num;
  if (hist_num<0) hist_num+=envir.history_size;
  return *(envir.hist+hist_num);
}

/* print_history
 * prints out strings in history array,
 * either starting at 0 or at the location
 * past current_history if the history has
 * wrapped around.
 */
void print_history()
{
  int i=envir.current_hist;
  int c=envir.command_num-envir.history_size;

  if ((*(*(envir.hist+i))=='\0') || (i>envir.history_size-1)) {
    c=envir.command_num-envir.current_hist;
    i=0;
  }

  printf("     history [%d]\n",envir.history_size);
  while (c<envir.command_num) {
    printf("     %d  %s\n",c,*(envir.hist+i));
    i++;
    c++;
    if(i>envir.history_size-1)
      i=0;
  }
}
/* end HISTORY STUFF */

void do_set(comm)
  char **comm;
{
  char *illegal[8]={"PATH","LOGNAME","SHELL","MAIL",
                    "USER","WSHLOGIN","EDITOR","NETHOST"};
  int ind=8;  /* size of illegal */
  char *tmp;

  if(!comm[2]) {
    if(tmp=getenv(comm[1])) {
      if(!strcmp(comm[1],"HOME")) fprintf(stderr,"/%s\n",tmp);
      else fprintf(stderr,"%s\n",tmp);
    }
    else fprintf(stderr,"setenv: Too few arguments.\n");
    return;
  }
  if(!strcmp(comm[1],"prompt"))
    set_prompt(comm[2]);
  else if(!strcmp(comm[1],"debug")) {
    if(!strcmp(comm[2],"off")) DEBUGON=0;
    else DEBUGON=1;
  } else {
    for(ind--;ind>=0;ind--)
      if(!strncmp(comm[1],illegal[ind],strlen(illegal[ind])))
        if(strlen(comm[1])<=strlen(illegal[ind])
           || comm[1][strlen(illegal[ind])]=='=') {
          fprintf(stderr,"Illegal to set %s.\n",illegal[ind]);
          return;
        }
    MK_STR(tmp,strlen(comm[1])+strlen(comm[2])+2)
    strcpy(tmp,comm[1]);
    *(tmp+strlen(comm[1]))='=';
    if(!strcmp(comm[1],"HOME")) {/* HOME is special case */
      if(comm[2][0]!='/')
        fprintf(stderr,"set: \"home\" must be an absolute path.\n");
      else strcpy(tmp+strlen(comm[1])+1,comm[2]+1);
    }
    else strcpy(tmp+strlen(comm[1])+1,comm[2]);
    wshputenv(tmp);   /* do not free a putenv */
  }
  return;
}

/* ALIAS STUFF */
/* find an alias in a set */
alias_t *find_alias(name,set)
  char *name;
  alias_t *set;
{
  while(set && strcmp(set->name,name))
    set=set->next;
/*  if(set && strcmp(set->name,name)) return NULL;
fprintf(stderr,"found: %s\n",set->name);
*/
  return set;
}

/* do alias to set from line in form "name<whitespace>what<\0>" */
/* if no what, then just print the alias */
void do_alias(line,seth)
  char *line;
  alias_t **seth;
{
  alias_t *tmp,*last,*new;
  char *str,*what,*endname,c;

  endname=line;
  while(*endname!=' ' && *endname!='\t' && *endname!='\0') endname++;
  c=*endname;
  *endname='\0';

  what=endname;
  if(c!='\0') what=eat_white(what+1);
  if(*what=='\0') {
    tmp=find_alias(line,*seth);
    if(tmp && !strcmp(tmp->name,line)) fprintf(stderr,"%s\n",tmp->what);
    return;
  }

                          /* i like being able to alias "alias" ;-) */
  if(!strcmp(line,"unalias") /* || !strcmp(line,"alias") */ ) {
    fprintf(stderr,"%s:  Too dangerous to alias that.\n",line);
    return;
  }
  tmp=*seth;
  last=NULL;
  while(tmp && strcmp(tmp->name,line)<0) {
    last=tmp;
    tmp=tmp->next;
  }

  /* add alias */
  if(tmp && !strcmp(tmp->name,line)) FREE(tmp->what);
  else {
    if((tmp=(alias_t *) MALLOC(sizeof(alias_t)))==NULL)
      FATAL("Could not allocate alias memory")
    MK_STR(tmp->name,strlen(line)+1)
    strcpy(tmp->name,line);
    if(!last) { /* put at front */
      tmp->next=*seth;
      *seth=tmp;
    } else {
      tmp->next=last->next;
      last->next=tmp;
    }
  }

  MK_STR(tmp->what,strlen(what)+1)
  strcpy(tmp->what,what);
  *endname=c;
  return;
}

/* end ALIAS STUFF */

/* replaces
 * look for things to replace in line string before converting to comm array
 * !<bangs>, aliases, $environment vars, ^lastline^change
 * also, converts \! to ! (non history bang) and ignores after #
 */
#define LOOP 40  /* number of aliases to eval before assuming alias loop */
char *replaces(line)
  char *line;
{
  int i,j,a,aliasloop=0,tmpint,end,hist,env,caught,firstword=1;
  char buf[40],lastalias[40],*tmp,*tmp2,*tmp3;
  alias_t *aliastmp;

  line=eat_white(line);

  end=0;

  caught=0;
  i=end-1;
  while(line[++i]!='\0' && line[i]!='#' && line[i]!='\r' && line[i]!='\n')
    if(line[i]=='$' || line[i]=='!' ||
       (!isspace(line[i]) && firstword)) {
      hist=(line[i]=='!');
      env=(line[i]=='$');
      if((hist || env) && i>0 && line[i-1]=='\\') {
        strcpy(line+i-1,line+i);  /* change \! to ! and ignore */
        end=i+1; /* skip $ or ! next time */
        continue;
      }
      end=i;
      j=0;
      if(!hist && !env)
        while(line[i]!=' ' && line[i]!='\t' && line[i]!='\0' && line[i]!='\n')
          buf[j++]=line[i++];
      else
        while(line[++i]!='/' && line[i]!=' ' && line[i]!='\t' && line[i]!='\0'
            && line[i]!='\'' && line[i]!='"' && line[i]!='`' && line[i]!='\n'
            && line[i]!='[' && line[i]!=']' && line[i]!=':')
          buf[j++]=line[i];
      buf[j]='\0';
      if(env)
        tmp=getenv(buf);
      else if(hist) {  /* bang */
        if(sscanf(buf,"%d",&tmpint)==1)
          tmp=get_history(tmpint);
        else {
          if(buf[0]=='\0') {
            i--;
            continue;
          }
          if(buf[0]=='!' && buf[1]=='\0') buf[0]='\0';
          tmpint=envir.command_num-1;
          a=1;
          while(a && (tmp=get_history(tmpint))) {
            a=strlen(buf)-1;
            while(tmp[a]==buf[a] && a-->0);
             a++;
            tmpint--;
          }
        }
      } else {  /* not env or hist, so check for alias */
           /* catch simple alias loop (alias ls ls -s) */
        if(strcmp(lastalias,buf) && (aliastmp=find_alias(buf,aliases))) {
          tmp=aliastmp->what;
          strcpy(lastalias,buf);
        } else {
          firstword=0;
          i=i-j-1; /* start at next char of first word (j=strlen(buf)) */
          continue;
        }
      }
      if(!tmp || (!env && !hist && aliasloop++>LOOP)) {
        if(hist) fprintf(stderr,"%s: Event not found.\n",buf);
        else if(env) fprintf(stderr,"%s: Undefined variable.\n",buf);
        else fprintf(stderr,"Alias loop.\n");
        if(caught) FREE(line);
        MK_STR(line,2)
        line[0]='\0';
        return line;   /* error, take no action */
      }
      MK_STR(tmp2,end+strlen(line)-i+strlen(tmp)+1)
      strncpy(tmp2,line,end);
      if(env && !strcmp(buf,"HOME")) /* $HOME is special case.. */
        *(tmp2+(end++))='/'; /* wsh needs / but programs don't */
      strcpy(tmp2+end,tmp);
      strcpy(tmp2+end+strlen(tmp),line+i);
      if(caught) FREE(line);
      line=tmp2;
      caught=1;
      if(!hist && !env)
        i=-1;   /* start at beginning again */
      else
        i=i-strlen(buf)-2;  /* start at beginning of replacement */
    } /* done with search/replace in line for $ and ! and aliases */

/*  while(line[i]!='\n' && line[i]!='\r' && line[i]!='\0') i++;*/
  line[i]='\0';

  if(!caught) return 0;
  return line;
}         

/* catchline
 * catch some stuff before converting to comm array
 * now just 'alias' 'chat' and 'tell'
 * returns 1 if caught (add to history)
 */
int catchline(line)
  char *line;
{
  char *endword,c,*tmp;
  alias_t *altmp;
  who_t *tw;
  FILE *fp;

  line=eat_white(line);
  endword=line;
  while(*endword!=' ' && *endword!='\t' && *endword!='\0') endword++;
  c=*endword;
  *endword='\0';
  if(!strcmp(line,"alias")) {
    *endword=c;
    tmp=eat_white(endword);
    if(*tmp=='\0') {
      altmp=aliases;
      while(altmp) {
        fprintf(stderr,"%s\t%s\n",altmp->name,altmp->what);
        altmp=altmp->next;
      }
      return 1;
    }
    do_alias(tmp,&aliases);
    return 1;
  }
  if(!strcmp(line,"chat")) {
    *endword=c;
    chat(eat_white(endword));
    return 1;
  }
  if(!strcmp(line,"tell")) {
    *endword=c;
    tell(eat_white(endword));
    return 1;
  }
  *endword=c;
  return 0;
}


/* catchcomm
 * catch commands that don't get execvp'd
 * exit, logout, history stuff
 * other environment sets can go here
 * returns 0 if not caught
 * returns -1 if caught & quit
 * returns 1 if caught and add to history
 * returns 2 if caught, don't add to history
 */
int catchcomm(comm,interactive)
  char **comm;
  int interactive;
{
  char *tmp;
  who_t *tw;
  FILE *fp;
  int tmpint;

  if (!strcmp(comm[0],"acc")) {
    print_acc(write_acc,0);
    print_acc(read_acc,0);
    return 1;
  }
  if (!strcmp(comm[0],"cd")) {
    cWD(comm[1],interactive && !envir.prompt2);
    return 1;
  }
  if (!strcmp(comm[0],"exit")) {
    if(!comm[1]) return -1;
    ERROR("exit","Expression syntax")
    return 1;
  }
  if (!strcmp(comm[0],"history")) {
    if (!comm[1]) {
      print_history();
      return 1;
    }
    if(sscanf(comm[1],"%d",&tmpint)==1)
      set_history_size(tmpint);
    else
      fprintf(stderr,"%s: Badly formed number.\n",comm[1]);
    return 1;
  }
  if (!strcmp(comm[0],"logout")) {
    if(!comm[1]) return -1;
    ERROR("logout","Too many arguments")
    return 1;
  }
  if (!strcmp(comm[0],"pwd")) {
    if(comm[1]) fprintf(stderr,"usage: pwd\n");
    else if(*WD) fprintf(stderr,"%s\n",WD);
    else fprintf(stderr,"/\n");
    return 1;
  }
  if (!strcmp(comm[0],"mesg")) {
    if(!comm[1]) { /* this is the ugly mesg format, I didn't make it up */
      if(envir.mesg) fprintf(stdout,"is y\n");
      else fprintf(stdout,"is n\n");
      return 1;
    }
    if((comm[1][0]=='y' || comm[1][0]=='n') && comm[1][1]=='\0') {
      envir.mesg=(comm[1][0]=='y');
      read_utmp(1);  /* read the utmp, change our entry and write back */
      return 1;
    }
    fprintf(stderr,"usage: mesg [y] [n]\n");
    return 1;
  }
  if(!strcmp(comm[0],"whoami")) {
    fprintf(stderr,"%s\n",getenv("WSHLOGIN"));
    return 1;
  }
  if(!strcmp(comm[0],"who")) {
    free_who(print_utmp(read_utmp(0),comm[1]));
    return 1;
  }
  if(!strcmp(comm[0],"setenv")) {
    if(!comm[1]) {
      char **env;      
      for(env=environ;*env;env++) fprintf(stderr,"%s\n",*env);
    } else do_set(comm);
    return 1;
  }
  if(!strcmp(comm[0],"source") || !strcmp(comm[0],".")) {
    tmpint=source(comm[1],1);
    if(tmpint==1) fprintf(stderr,"%s: No such file or directory\n",comm[1]);
    if(tmpint==-1) fprintf(stderr,"%s: Permission denied\n",comm[1]);
    return 1;
  }
  if(!strcmp(comm[0],"unalias")) {
    if(comm[1]) {
      alias_t *tmp,*last;

      tmp=aliases;
      last=NULL;
      while(tmp && strcmp(tmp->name,comm[1])<0) {
        last=tmp;
        tmp=tmp->next;
      }
      if(!tmp) return 1;
      if(!last) /* we know that it must be the first */
        aliases=aliases->next;
      else
        last->next=tmp->next;
      FREE(tmp);
      return 1;
    }
    ERROR("unalias","Too few arguments")
    return 1;
  }
  if(!strcmp(comm[0],"version")) {
    fprintf(stdout,"This is the WizShell (wsh) version: %s\n\n",VERSION);
    fprintf(stdout,"The wsh is part of the WizPort package\n"
                   "Author:  David Ljung\n"
                   "         (608) 257-COWS\n"
                   "         ljung@cae.wisc.edu\n");
    return 1;
  }

  return 0;
}