
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "lafe.h"


int commands=0;
int command_stack=0;
char **commandline=NULL;


/* replace a leading tilde by home directory */
char *expand_tilde(char *path) {
  int len;
  char *buffer;

  if (path && path[0]=='~') {
    len = strlen(getenv("HOME")) + strlen(path);
    buffer=malloc(len);
    sprintf(buffer,"%s%s",getenv("HOME"),path+1);
    free(path);
    return buffer;
  } else {
    return path;
  }
}


void verbosity(char *arg) {
  if (!strcmp("on",arg)) {
    verbose=1;
  } else if (!strcmp("off",arg)) {
    verbose=0;
  } else {
    verbose ^= 1;
  }
  if (verbose) printf("Verbose On\n"); else printf("Verbose Off\n"); 
}



void shell(char *arg) {
  if (system(arg)) perror("system");
}

/* lines limited to size of buffer.  Shouldn't be much of a problem. */
void runfeed(char *arg) {
  FILE *pipe;
  char buffer[4096];
  char *end;
  int fails=0;

  pipe = popen(arg,"r");
  if (!pipe) {
    perror("popen");
    return;
  }

  buffer[sizeof(buffer)-1]=0;

  while (fgets(buffer,sizeof(buffer)-1,pipe) != NULL) {
    end=strchr(buffer,'\n');
    if (end) {
      *end = 0;
      parse_command(buffer);
    } else {
      fails++;
    }
  }

  if (fails) fprintf(stderr,"%d lines too long\n",fails);

  pclose(pipe);
}

void execute(char *arg) {
  FILE *fp;
  int i;
  char buffer[4096];
  char *end;
  int fails=0;
  char *filename;

  buffer[sizeof(buffer)-1]=0;

  filename = strdup(arg);
  /* truncate anything past first argument */
  for (i=0;filename[i] && !isspace(filename[i]);i++) ;
  filename[i]=0;

  /* expand a leading ~ into $HOME */
  filename = expand_tilde(filename);

  fp = fopen(filename,"r");
  if (!fp) {
    perror(filename);
    free(filename);
    return;
  }


  while (fgets(buffer,sizeof(buffer)-1,fp) != NULL) {
    end=strchr(buffer,'\n');
    if (end) {
      *end = 0;
      parse_command(strdup(buffer));
    } else {
      fails++;
    }
  }

  if (fails) fprintf(stderr,"%d lines too long\n",fails);


  fclose(fp);
  free(filename);

}



/**************************************************************************
 *   parse commands.  Look for commands matching local commands
 *   and call the local handler.  All others are sent directly to
 *   the server on socket s.
 **************************************************************************/
struct local_command {
  char *name;	      /* command name .. "execute" */
  int shortest;       /* shortest abbreviation */
  void (*process)();  /* function to parse arguments */
  char *description;  /* short description */
};

/* should be in alphabetical order */
static struct local_command local_command[] = {
  { "addgame", 7, addgame, "game country password host port logfile directory" },
  { "alias", 5, alias, "[NAME [DEFINITION]]" },
  { "execute", 4, execute, "FILENAME" },
  { "help", 4, help, "" },
  { "history", 4, history, "" },
  { "runfeed", 7, runfeed, "SHELL_COMMAND" },
  { "setvar", 4, setvar, "[NAME [VALUE]]" },
  { "shell", 5, shell, "SHELL_COMMAND" },
  { "verbose", 5, verbosity, "[on|off]" },
  { "waitsync", 5, waitsync, "" }
};

void help(char *dummy) {
  int i;
  extern char *LafeRevision;

  printf("\
\t\tLatency Free Empire\t\n\
\t\t  %s\n\
\n\
\tusage: lafe [country [password [host [port]]]]\n\
\nlocal commands:\n",LafeRevision);

  for (i=0;i<sizeof(local_command)/sizeof(*local_command);i++) {
    printf("%10s %s\n",local_command[i].name,local_command[i].description);
  }
}

/* line must be individually malloced.  must not have any linefeeds. */
void parse_command(char *line) {
  int len; /* length of line */
  int loc; /* location in local_command[] */
  int pos; /* position in command string */
  int start;  /* start of command string */

  char *lastline,*nextline;;


  /* if there are newlines, split into multiple lines and reparse */
  lastline=line;
  if ((nextline=strchr(lastline,'\n'))) {

    do {
      *nextline=0;
      parse_command(strdup(lastline));
      lastline=nextline+1;
    } while ((nextline=strchr(lastline,'\n')));
    parse_command(strdup(lastline));
    free(line);
    return;
  }

  for (start=0;line[start] && isspace(line[start]);start++) ;


  /* strip off first \ and don't do any additional parsing */
  if (line[start]=='\\') {
    char *oldline=line;
    line=strdup(oldline+1);
    free(oldline);

    /* do full alias and variable expansion */
  } else {

  /* not entirely safe, but it's fast and convenient. May expand some
   * prompts if you're typing too fast, so be careful. */
    if (commands>0 || mode=='6' || !mode) {
      line=expand_alias(line);
      line=expand_variables(line);
    }

    if (strchr(line,'\n')) {
      parse_command(line);
    } else {


      loc=pos=0;

      while (loc<sizeof(local_command)/sizeof(*local_command)) {
	if (line[start+pos]==local_command[loc].name[pos]) {
	  pos++;
	  
	  /* found a match, call handler and return */
	  if (pos>=local_command[loc].shortest) {
	    int arg;	/* position of arguments */
	    for (arg=pos;line[arg] && !isspace(line[arg]);arg++) ;
	    for (;line[arg] && isspace(line[arg]);arg++) ;
	    local_command[loc].process(line+arg);
	    free(line);
	    return;
	  }
	  
	} else if (line[start+pos] > local_command[loc].name[pos]) {
	  loc++;
	  pos=0;
	} else {
	  break;
	}
      }
    }
  }

  /* no match found, send to server if connected to server */
  if (s != -1) {
    len=strlen(line);
    line[len]='\n';
    if (write(s,line,len+1) != len+1) {
      perror("command write failure");

    }
    line[len]=0;

    if (!commands) {
      if (log) fprintf(log,"%s\n",line);
    }

    if (commands > command_stack-1) {
      if (!commandline) {
	command_stack=10;
	commandline=malloc(sizeof(*commandline)*command_stack);
      } else {
	command_stack *= 2;
	commandline = realloc(commandline,sizeof(*commandline)*command_stack);
      }
    }
    if (!commandline) {
      fprintf(stderr, "Memory allocation failure, commandlines lost\n");
      command_stack=0;
      commands=0;
      free(line);
    } else {
      commandline[commands++]=line;
    }

  } else {
    if (verbose)
      fprintf(stderr,"# %s\n",line);
    free(line);
  }

}


