#!/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! % private variable SVNSH_VERSION = "0.1.0-1"; private variable Repository; private variable Repository_CWD; private variable Echo_Commands = 0; private variable Rline; new_exception ("SVNError", AnyError, "svn error"); private 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; } private 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); } private 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; } private 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; } private 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; } private 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); } private 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); } } private 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); } private 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); } private 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); } private 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); } private define _svn_help (argv) { _svn_execute ("help %s", strjoin (argv, " ")); () = fprintf (stdout, "\nAlso use ? for a list of interractive commands\n"); } private define _svn_quit (argv) { Rline = NULL; exit (0); } private variable Cmd_Table = Assoc_Type [Ref_Type]; private 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; private 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 (); }