#!/usr/bin/env slsh
% This is a simple shell that allows one to interact with an SVN repository
% in a shell-like manner.
% 
% The location of the repository may be specified either on the 
% command line or via the SVN_REPOS environment variable, e.g.,
% 
%     svnsh $HOME/var/svn/repos
%
% Use it at your own risk!
%

static variable SVNSH_VERSION = "0.1.0-1";

static variable Repository;
static variable Repository_CWD;
static variable Echo_Commands = 0;

static variable Rline;

new_exception ("SVNError", AnyError, "svn error");

static define expand_path (path)
{
   variable dir = Repository_CWD;

   if (path[0] == '/')
     dir = Repository;

   foreach (strtok (path, "/"))
     {
	variable sdir = ();
	if (sdir == "..")
	  {
	     if (dir == Repository)
	       {
		  vmessage ("Can't go up");
		  return NULL;
	       }
	     dir = path_dirname (dir);
	     continue;
	  }
	if (sdir == ".")
	  continue;
	
	dir = strcat (dir, "/", sdir);
     }
   return dir;
}

static define _svn_execute ()
{
   variable args = __pop_args (_NARGS);
   variable cmd = "svn " + sprintf (__push_args(args));
   if (Echo_Commands)
     () = fprintf (stdout, "%s\n", cmd);
   variable status = system (cmd);
   if (status != 0)
     throw SVNError, sprintf ("%s returned %d", cmd, status);
}

static define _svn_path_exists (path)
{
   if (path == NULL)
     return 0;
   try
     {
	_svn_execute ("ls %s >/dev/null 2>&1", path);
     }
   catch SVNError: return 0;
   return 1;
}

static define _svn_cd (argv)
{
   variable dir = expand_path (argv[0]);
   if (0 == _svn_path_exists (dir))
     {
	vmessage ("%s does not exist", dir);
	return;
     }
   Repository_CWD = dir;
}

static define get_opts (argv)
{
   variable i;
   variable opts = "";
   variable args = String_Type[0];
   variable n = length (argv);
   i = 0;
   while (i < n)
     {
	variable arg = argv[i];
	if (arg[0] == '-')
	  {
	     if (arg == "-nc")
	       {
		  arg = "-m \"\"";
	       }
	     opts = strcat (opts, " ", arg);
	     i++;
	     continue;
	  }
	args = argv[[i:]];
	break;
     }

   return opts, args;
}

static define _svn_cp (argv)
{
   variable opts, files;

   (opts, files) = get_opts (argv);
   if (length (files) != 2)
     {
	() = fprintf (stderr, "Usage: cp dir dir\n");
	return;
     }
   variable from = expand_path (files[0]);
   variable to = expand_path (files[1]);
   if ((from == NULL) or (to == NULL))
     return;
   _svn_execute ("cp %s %s %s", opts, from, to);
}

static define _svn_ls (argv)
{
   variable opts, files;
   (opts, files) = get_opts (argv);

   if (opts == "-l")
     opts = "-v";

   if (length (files) == 0)
     {
	_svn_execute ("ls %s %s", opts, Repository_CWD);
	return;
     }

   foreach (files)
     {
	variable file = ();
	file = expand_path (file);
	if (file == NULL)
	  continue;
	
	_svn_execute ("ls %s %s", opts, file);
     }
}

static define _svn_mv (argv)
{
   variable opts, files;
   (opts, files) = get_opts (argv);
   
   if (length (files) != 2)
     {
	() = fprintf (stderr, "Usage: mv FROM TO\n");
	return;
     }
   
   variable from = expand_path (files[0]);
   variable to = expand_path (files[1]);
   
   if ((from == NULL) or (to == NULL))
     {
	() = fprintf (stderr, "*** mv failed\n");
	return;
     }

   _svn_execute ("mv %s %s %s", opts, from, to);
}

static define _svn_mkdir (argv)
{
   variable opts, files;

   (opts, files) = get_opts (argv);
   if (length (files) != 1)
     {
	() = fprintf (stderr, "Usage: mkdir DIR\n");
	return;
     }
   variable dir = expand_path (files[0]);
   if (dir == NULL)
     return;
   _svn_execute ("mkdir %s %s", opts, dir);
}

static define _svn_rm (argv)
{
   variable opts, files;

   (opts, files) = get_opts (argv);
   if (length (files) != 1)
     {
	() = fprintf (stderr, "Usage: rm file\n");
	return;
     }
   variable dir = expand_path (files[0]);
   if (dir == NULL)
     return;
   _svn_execute ("rm %s %s", opts, dir);
}

static define _svn_most (argv)
{
   if (length (argv) != 1)
     {
	() = fprintf (stderr, "Usage: most FILE\n");
	return;
     }
   variable file = expand_path (argv[0]);
   if (file == NULL)
     return;
   _svn_execute ("cat %s | most", file);
}

static define _svn_help (argv)
{
   _svn_execute ("help %s", strjoin (argv, " "));
   () = fprintf (stdout, "\nAlso use ? for a list of interractive commands\n");
}

static define _svn_quit (argv)
{
   Rline = NULL;
   exit (0);
}

static variable Cmd_Table = Assoc_Type [Ref_Type];
static define _svn_local_help (argv)
{
   () = fprintf (stdout, "Local commands:\n");
   foreach (assoc_get_keys (Cmd_Table))
     {
	variable key = ();
	() = fprintf (stdout, "%s\n", key);
     }
}

	
Cmd_Table["ls"] = &_svn_ls;
Cmd_Table["cd"] = &_svn_cd;
Cmd_Table["cp"] = &_svn_cp;
Cmd_Table["help"] = &_svn_help;
Cmd_Table["mv"] = &_svn_mv;
Cmd_Table["mkdir"] = &_svn_mkdir;
Cmd_Table["rmdir"] = &_svn_rm;
Cmd_Table["rm"] = &_svn_rm;
Cmd_Table["most"] = &_svn_most;
Cmd_Table["quit"] = &_svn_quit;
Cmd_Table["?"] = &_svn_local_help;

static define take_input ()
{
   variable line;
   forever 
     {
	try
	  {
	     line = slsh_readline (Rline, sprintf ("%s> ", Repository_CWD));
	  }
	catch UserBreakError: continue;
	if (line == NULL)
	  break;

	variable argv = strtok (line, " \t;\n");
	if (length (argv) == 0)
	  continue;

	variable cmd = argv[0];
	if (length (argv) == 1)
	  argv = String_Type[0];
	else
	  argv = argv[[1:]];

	variable e;

	try (e)
	  {
	     if (0 == assoc_key_exists (Cmd_Table, cmd))
	       {
		  () = fprintf (stderr, "%s not supported\n", cmd);
		  continue;
	       }
	     (@Cmd_Table[cmd])(argv);
	  }
	catch AnyError:
	  {
	     () = fprintf (stderr, "%S:%d:%s *** Caught exception: %S\n",
			   e.file, e.line, e.descr, e.message);
	  }
     }
}

public define svn_set_repository (repos)
{
   !if (_svn_path_exists (repos))
     {
	() = fprintf (stderr, "*** Repository %s does not exist\n", repos);
	exit (1);
     }
   Repository = repos;
   Repository_CWD = repos;
}
   
public define slsh_main ()
{
   variable repos;
   if (__argc == 2)
     repos = __argv[1];
   else
     {
	repos = getenv ("SVN_REPOS");
	if (repos == NULL)
	  {
	     () = fprintf (stderr, "Usage: %s [file://]repository\n", __argv[0]);
	     exit (1);
	  }
     }

   () = fprintf (stdout, "svnsh version %s.\nUse this at your own risk!\n", SVNSH_VERSION);
   () = fprintf (stdout, "Enter ? for a list of commands.\n");

   !if (is_substr (repos, "://"))
     repos = strcat ("file://", repos);
   
   svn_set_repository (repos);

   slsh_readline_init ("SVNSH");
   Rline = slsh_readline_new ("svnsh");

   take_input ();
}
