/*
 *advsh The Adventure Shell - re-implemented in C for great justice
 *
 *[not to mention speed].
 *[and just for the exercise]
 */

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <glob.h>
#include <sys/types.h>
#include <sys/stat.h>

void changedir(char *nextdir,char *curdir);
void displayhelp();
char *getverb(char *command);
char *gjlbasename(char *fullpath);

int main(int argc, char **argv)
{
  extern int errno;
  char curdir[BUFSIZ],nextdir[BUFSIZ],*homedir,command[BUFSIZ],noun[BUFSIZ],verb[BUFSIZ],spell[BUFSIZ],invv[5][BUFSIZ],movefile[BUFSIZ];
  glob_t globresults;
  int status,knownflag;
  unsigned int i,j,invc;
  struct stat filestat;
  pid_t spellpid;
  
  homedir=getenv("HOME");
  strcpy(nextdir,homedir);
  strcpy(curdir,"/");
  
  /*We first change to the / directory, on the basis that it exists.
   *Then try to change to the user's home directory, in case that doesn't.
   */
  if(chdir(curdir))
    {
      fprintf(stderr,"advsh: could not cd to /: %s\n",strerror(errno));
      fprintf(stderr,"advsh: in my book, that's a bit fatal.\n");
      exit(EXIT_FAILURE);
    }
  changedir(nextdir,curdir);
  
  invc=0;
  
  printf("This is advsh - the Adventure Shell\n");
  printf("Inspired by a shell script by John \"Unix God\" Coker\n");
  printf("Implemented in C by Acolyte Leeg\n\n");
  printf("Do you require instructions (y/n)? ");
  fgets(command,BUFSIZ,stdin);
  if(!strncmp(command,"y",1) || !strncmp(command,"Y",1))
    {
      displayhelp();
    }
  
  while(!feof(stdin))
    {
      strcpy(command,"");
      if(!strcmp(curdir,homedir))
	{
	  printf("You are standing in your house.\n");
	}
      else
	{
	  printf("You are currently in %s\n",curdir);
	}
      
      status=glob("*",0,NULL,&globresults);
      printf("Available exits:\n");
      if(strcmp(curdir,"/")) printf("up/\t");
      j=0;
      for(i=0;i<globresults.gl_pathc;i++)
	{
	  filestat.st_mode=0;
	  lstat(globresults.gl_pathv[i],&filestat);
	  if (S_ISDIR(filestat.st_mode))
	    {
	      printf("%s/\t",globresults.gl_pathv[i]);
	      j+=((strlen(globresults.gl_pathv[i])+1)/8+1)*8;
	      if(j>70) {printf("\n"); j=0;}
	    }
	}
      printf("\n\nObjects here are:\n");
      j=0;
      for(i=0;i<globresults.gl_pathc;i++)
	{
	  filestat.st_mode=0;
	  lstat(globresults.gl_pathv[i],&filestat);
	  if(!S_ISDIR(filestat.st_mode))
	    {
	      printf("%s",globresults.gl_pathv[i]);
	      j+=((strlen(globresults.gl_pathv[i])+3)/8+1)*8;
	      if(S_ISLNK(filestat.st_mode)) printf("@");
	      if(S_ISFIFO(filestat.st_mode)) printf("|");
	      if(S_ISSOCK(filestat.st_mode)) printf("=");
	      printf("\t");
	      if(j>70) {printf("\n"); j=0;}
	    }
	}
      globfree(&globresults);
      printf("\n? ");
      fgets(command,BUFSIZ,stdin);
      command[strlen(command)-1]='\0';
      if(!strcmp(command,"quit game") || feof(stdin))
	{
	  printf("OK, thanks for playing advsh!  Goodbye!\n");
	  exit(EXIT_SUCCESS);
	}
      knownflag=0;
      strcpy(verb,getverb(command));
      
      if(!strcmp(verb,"go"))
	{
	  knownflag=1;
	  strcpy(noun,&command[3]);
	  if(!strcmp(noun,"up")) strcpy(noun,"..");
	  if(!strcmp(noun,"~")) strcpy(noun,homedir);
	  changedir(noun,curdir);
	}
      
      if(!strcmp(verb,"cast"))
	{
	  knownflag=1;
	  strcpy(noun,&command[5]);
	  if(!strcmp(noun,"spell"))
	    {
	      printf("Enter the spell to cast: ");
	      fgets(spell,BUFSIZ,stdin);
	      spell[strlen(spell)-1]='\0';
	      status=system(spell);
	      if(status==-1)
		{
		  fprintf(stderr,"Your spell fizzles in the humid air and does nothing.\n");
		}
	      else
		{
		  printf("\nYour spell causes those around you to see '%d' projected against the sky.\n\n",status);
		}
	    }
	  else fprintf(stderr,"I have no smegging clue how to cast one of THOSE.\n\n");
	}
      
      if(!strcmp(verb,"invoke"))
        {
	  knownflag=1;
	  strcpy(noun,&command[7]);
	  if(!strcmp(noun,"spell"))
            {
	      printf("Enter the spell to invoke: ");
	      fgets(spell,BUFSIZ,stdin);
	      spell[strlen(spell)-1]='\0';
	      spellpid=fork();
	      if(spellpid==-1)
                {
		  fprintf(stderr,"Your spell fizzles in the air and does nothing.\n");
		  fprintf(stderr,"A representative from the Wights and Spectres Union gives you a message.\nIt reads: %s\n\n",strerror(errno));
                }
	      else
		{
		  if(spellpid==0)
		    {
		      /* work out how to separate the string into an arg list */
		      /* execl() the above */
		    }
		  else
		    {
		      printf("Your invocation succeeds, and scuttles into the distance.  It seems to have\nleft some marking on the ground, which reads: %d\n\n",spellpid);
		    }
		}
            }
	  else
	    {
	      fprintf(stderr,"I'm not invoking that, I can't remember the symbol of Yog-Sothoth.\n\n");
	    }
        }
      
      if(!strcmp(verb,"speak"))
	{
	  knownflag=1;
	  strcpy(noun,&command[6]);
	  if(!strcmp(noun,"help"))
	    {
	      displayhelp();
	    }
	  else
	    {
	      printf("I don't know what you want me to say!\n\n");
	    }
	}
      
      if(!strcmp(verb,"search"))
	{
	  knownflag=1;
	  strcpy(noun,&command[7]);
	  if(!strcmp(noun,"carefully"))
	    {
	      printf("You scrabble around the area for hidden items.\n");
#ifdef GLOB_PERIOD
	      status=glob(".*",GLOB_PERIOD,NULL,&globresults);
#else
	      status=glob(".*",NULL,NULL,&globresults);
#endif /*GLOB_PERIOD*/
	      j=0;
	      printf("You have found:\n");
	      if(globresults.gl_pathc)
		{
		  printf("\n");
		  for(i=0;i<globresults.gl_pathc;i++)
		    {
		      if(!(strncmp(globresults.gl_pathv[i],".",1)))
			{
			  printf("%s\t",globresults.gl_pathv[i]);
			  j+=strlen(globresults.gl_pathv[i]);
			  if(j>70)
			    {
			      j=0;
			      printf("\n");
			    }
			}
		    }
		}
	      else printf("naff all.\n\n");
	    }
	  else
	    {
	      printf("You find nothing of interest.\n\n");
	    }
	}
      
      if(!strcmp(verb,"attack"))
	{
	  knownflag=1;
	  strcpy(noun,&command[7]);
	  printf("You bash at the evil %s with all of your might.\n",noun);
	  status=unlink(noun);
	  if(status==-1)
	    {
	      printf("But your attempts are in vain.\n");
	      printf("A wicked jester appears!  He cackles maniacally, and says\n\"%s\"\n\n",strerror(errno));
	    }
	  else
	    {
	      printf("The evil %s crumples and disappears in a puff of smoke!\n",noun);
	      printf("You gain an experience point.\n\n");
	    }
	}
      
      if(!strcmp(verb,"create"))
	{
	  knownflag=1;
	  strcpy(noun,&command[7]);
	  printf("You mumble an arcane incantation.\n");
	  status=mknod(noun,S_IFREG|0666,0);
	  if(status==-1)
	    {
	      printf("Nothing happens for a while, then a face appears in the wall!\n");
	      printf("Its crude caricature of a mouth snarls and says\n");
	      printf("%s\n\n",strerror(errno));
	    }
	  else
	    {
	      printf("Your attempt to create a %s was successful.\n\n",noun);
	    }
	}
      
      if(!strcmp(verb,"take"))
	{
	  knownflag=1;
	  strcpy(noun,&command[5]);
	  if(!strcmp(noun,"inventory"))
	    {
	      if(!invc)
		{
		  printf("You are not carrying anything.\n");
		}
	      else
		{
		  printf("You are carrying: \n");
		  for(i=0;i<invc;i++)
		    {
		      printf("%s\n",invv[i]);
		    }
		}
	      printf("\n");
	    }
	  else
	    {
	      printf("I do not understand your meaning.\n\n");
	    }
	}
      
      if(!strcmp(verb,"get"))
	{
	  knownflag=1;
	  strcpy(noun,&command[4]);
	  if(invc==5)
	    {
	      printf("You are already carrying too much!\n");
	      printf("You will have to drop something.\n");
	    }
	  else
	    {
	      if(noun[0]=='/')
		{
		  strcpy(invv[invc],noun);
		}
	      else
		{
		  strcpy(invv[invc],curdir);
		  strcat(invv[invc],"/");
		  strcat(invv[invc],noun);
		}
	      if(lstat(invv[invc],&filestat)==-1)
		{
		  printf("There is no object like that around.\n\n");
		}
	      else
		{
		  invc++;
		  printf("You pick up the %s and put it in your sack.\n\n",noun);
		}
	    }
	}
      
      if(!strcmp(verb,"drop"))
	{
	  knownflag=1;
	  strcpy(noun,&command[5]);
	  if(invc==0)
	    {
	      printf("You aren't carrying anything!\n\n");
	    }
	  else
	    {
	      j=10;
	      for(i=0;i<invc;i++)
		{
		  if(!strcmp(noun,invv[i]) || !strcmp(noun,gjlbasename(invv[i])))
		    {
		      j=i;
		    }
		}
	      if(j==10)
		{
		  printf("You don't have one of those!\n\n");
		}
	      else
		{
		  strcpy(movefile,curdir);
		  strcat(movefile,"/");
		  strcat(movefile,gjlbasename(invv[j]));
		  status=link(invv[j],movefile);
		  if(status==-1)
		    {
		      fprintf(stderr,"You find that the object flies back to the room you found it in!\n");
		      fprintf(stderr,"A dragon swoops down from the sky!  It breathes:\n%s\n\n",strerror(errno));
		    }
		  else
		    {
		      status=unlink(invv[j]);
		      if(status==-1)
			{
			  fprintf(stderr,"You become aware that the object here is linked to its original location!\n");
			  fprintf(stderr,"A dwarf shambles in and mutters\n%s\n\n",strerror(errno));
			}
		      else
			{
			  printf("You drop the %s\n\n",gjlbasename(invv[j]));
			}
		    }
		  for(i=j;i<(invc-1);i++)
		    {
		      strcpy(invv[i],invv[i+1]);
		    }
		  invc--;
		}
	    }
	}
      
      /*Sooper-secret Easter Egg ;-)*/
      if(!strcmp(command,"dir c:\\"))
	{
	  knownflag=1;
	  printf("A blessed +3 Club of Clue appears and starts attacking you!\n");
	  printf("You are unable to avoid the blows of the ClueBat!\n\n");
	}
      
      if(!knownflag && strncmp(verb,"#",1)) printf("I don't know how to %s\n\n",command);
    }
  
  return 0; /*for completeness*/
}

void changedir(char *nextdir,char *curdir)
{
  extern int errno;
  int status;
  
  status=chdir(nextdir);
  if(status)
    {
      fprintf(stderr,"\aadvsh: the door to %s remains closed.\n",nextdir);
      fprintf(stderr,"advsh: a booming voice announces \"%s\"\n",strerror(errno));
      fprintf(stderr,"advsh: you were not able to leave %s\n\n",curdir);
    }
  else
    {
      getcwd(curdir,BUFSIZ);
    }
}

void displayhelp()
{
  char snafu[BUFSIZ];
  printf("I am advsh, the adventure shell.\nI can be guided around the filesystem, and know how to perform some actions.\n");
  printf("You may communicate with me using verb/noun pairs, my vocabulary is shown below.\n\n");
  printf("VOCABULARY:\n");
  printf("go <place>:\t\tuse an exit\n");
  printf("speak help:\t\tspeak this verse\n");
  printf("cast spell:\t\tcast a UNIX spell\n");
  printf("invoke spell:\t\tlike casting but it happens in the background\n");
  printf("search carefully:\texamine the surroundings for hidden objects\n");
  printf("attack <object>:\tattempt to destroy an evil entity\n");
  printf("create <object>:\tattempt to generate an object\n");
  printf("get <object>:\t\tput an object into your inventory\n");
  printf("drop <object>:\t\tleave an object in your current location\n");
  printf("take inventory:\t\tshows what is being carried (useful!)\n");
  printf("quit game:\t\texit advsh\n\n");
  printf("Press return to continue with the adventure!\n");
  fgets(snafu,BUFSIZ,stdin);
}

char *getverb(char *command)
{
  char *noun;
  unsigned int i;
  
  noun=(char *)malloc(BUFSIZ*sizeof(char));
  strcpy(noun,command);
  for(i=0;i<strlen(command);i++)
    {
      if(command[i]==' ')
	{
	  noun[i]='\0';
	}
      else
	{
	  noun[i]=command[i];
	}
    }
  return noun;
}

char *gjlbasename(char *fullpath)
{
  char *i;
  
  for(i=fullpath+strlen(fullpath);i>fullpath-1;i--)
    {
      if(*i=='/')
	{
	  return(i+1);
	}
    }
  return fullpath;
}
