1#!/usr/bin/env slsh 2% This is a simple shell that allows one to interact with an SVN repository 3% in a shell-like manner. 4% 5% The location of the repository may be specified either on the 6% command line or via the SVN_REPOS environment variable, e.g., 7% 8% svnsh $HOME/var/svn/repos 9% 10% Use it at your own risk! 11% 12 13private variable SVNSH_VERSION = "0.1.0-1"; 14 15private variable Repository; 16private variable Repository_CWD; 17private variable Echo_Commands = 0; 18 19private variable Rline; 20 21new_exception ("SVNError", AnyError, "svn error"); 22 23private define expand_path (path) 24{ 25 variable dir = Repository_CWD; 26 27 if (path[0] == '/') 28 dir = Repository; 29 30 foreach (strtok (path, "/")) 31 { 32 variable sdir = (); 33 if (sdir == "..") 34 { 35 if (dir == Repository) 36 { 37 vmessage ("Can't go up"); 38 return NULL; 39 } 40 dir = path_dirname (dir); 41 continue; 42 } 43 if (sdir == ".") 44 continue; 45 46 dir = strcat (dir, "/", sdir); 47 } 48 return dir; 49} 50 51private define _svn_execute () 52{ 53 variable args = __pop_args (_NARGS); 54 variable cmd = "svn " + sprintf (__push_args(args)); 55 if (Echo_Commands) 56 () = fprintf (stdout, "%s\n", cmd); 57 variable status = system (cmd); 58 if (status != 0) 59 throw SVNError, sprintf ("%s returned %d", cmd, status); 60} 61 62private define _svn_path_exists (path) 63{ 64 if (path == NULL) 65 return 0; 66 try 67 { 68 _svn_execute ("ls %s >/dev/null 2>&1", path); 69 } 70 catch SVNError: return 0; 71 return 1; 72} 73 74private define _svn_cd (argv) 75{ 76 variable dir = expand_path (argv[0]); 77 if (0 == _svn_path_exists (dir)) 78 { 79 vmessage ("%s does not exist", dir); 80 return; 81 } 82 Repository_CWD = dir; 83} 84 85private define get_opts (argv) 86{ 87 variable i; 88 variable opts = ""; 89 variable args = String_Type[0]; 90 variable n = length (argv); 91 i = 0; 92 while (i < n) 93 { 94 variable arg = argv[i]; 95 if (arg[0] == '-') 96 { 97 if (arg == "-nc") 98 { 99 arg = "-m \"\""; 100 } 101 opts = strcat (opts, " ", arg); 102 i++; 103 continue; 104 } 105 args = argv[[i:]]; 106 break; 107 } 108 109 return opts, args; 110} 111 112private define _svn_cp (argv) 113{ 114 variable opts, files; 115 116 (opts, files) = get_opts (argv); 117 if (length (files) != 2) 118 { 119 () = fprintf (stderr, "Usage: cp dir dir\n"); 120 return; 121 } 122 variable from = expand_path (files[0]); 123 variable to = expand_path (files[1]); 124 if ((from == NULL) or (to == NULL)) 125 return; 126 _svn_execute ("cp %s %s %s", opts, from, to); 127} 128 129private define _svn_ls (argv) 130{ 131 variable opts, files; 132 (opts, files) = get_opts (argv); 133 134 if (opts == "-l") 135 opts = "-v"; 136 137 if (length (files) == 0) 138 { 139 _svn_execute ("ls %s %s", opts, Repository_CWD); 140 return; 141 } 142 143 foreach (files) 144 { 145 variable file = (); 146 file = expand_path (file); 147 if (file == NULL) 148 continue; 149 150 _svn_execute ("ls %s %s", opts, file); 151 } 152} 153 154private define _svn_mv (argv) 155{ 156 variable opts, files; 157 (opts, files) = get_opts (argv); 158 159 if (length (files) != 2) 160 { 161 () = fprintf (stderr, "Usage: mv FROM TO\n"); 162 return; 163 } 164 165 variable from = expand_path (files[0]); 166 variable to = expand_path (files[1]); 167 168 if ((from == NULL) or (to == NULL)) 169 { 170 () = fprintf (stderr, "*** mv failed\n"); 171 return; 172 } 173 174 _svn_execute ("mv %s %s %s", opts, from, to); 175} 176 177private define _svn_mkdir (argv) 178{ 179 variable opts, files; 180 181 (opts, files) = get_opts (argv); 182 if (length (files) != 1) 183 { 184 () = fprintf (stderr, "Usage: mkdir DIR\n"); 185 return; 186 } 187 variable dir = expand_path (files[0]); 188 if (dir == NULL) 189 return; 190 _svn_execute ("mkdir %s %s", opts, dir); 191} 192 193private define _svn_rm (argv) 194{ 195 variable opts, files; 196 197 (opts, files) = get_opts (argv); 198 if (length (files) != 1) 199 { 200 () = fprintf (stderr, "Usage: rm file\n"); 201 return; 202 } 203 variable dir = expand_path (files[0]); 204 if (dir == NULL) 205 return; 206 _svn_execute ("rm %s %s", opts, dir); 207} 208 209private define _svn_most (argv) 210{ 211 if (length (argv) != 1) 212 { 213 () = fprintf (stderr, "Usage: most FILE\n"); 214 return; 215 } 216 variable file = expand_path (argv[0]); 217 if (file == NULL) 218 return; 219 _svn_execute ("cat %s | most", file); 220} 221 222private define _svn_help (argv) 223{ 224 _svn_execute ("help %s", strjoin (argv, " ")); 225 () = fprintf (stdout, "\nAlso use ? for a list of interractive commands\n"); 226} 227 228private define _svn_quit (argv) 229{ 230 Rline = NULL; 231 exit (0); 232} 233 234private variable Cmd_Table = Assoc_Type [Ref_Type]; 235private define _svn_local_help (argv) 236{ 237 () = fprintf (stdout, "Local commands:\n"); 238 foreach (assoc_get_keys (Cmd_Table)) 239 { 240 variable key = (); 241 () = fprintf (stdout, "%s\n", key); 242 } 243} 244 245Cmd_Table["ls"] = &_svn_ls; 246Cmd_Table["cd"] = &_svn_cd; 247Cmd_Table["cp"] = &_svn_cp; 248Cmd_Table["help"] = &_svn_help; 249Cmd_Table["mv"] = &_svn_mv; 250Cmd_Table["mkdir"] = &_svn_mkdir; 251Cmd_Table["rmdir"] = &_svn_rm; 252Cmd_Table["rm"] = &_svn_rm; 253Cmd_Table["most"] = &_svn_most; 254Cmd_Table["quit"] = &_svn_quit; 255Cmd_Table["?"] = &_svn_local_help; 256 257private define take_input () 258{ 259 variable line; 260 forever 261 { 262 try 263 { 264 line = slsh_readline (Rline, sprintf ("%s> ", Repository_CWD)); 265 } 266 catch UserBreakError: continue; 267 if (line == NULL) 268 break; 269 270 variable argv = strtok (line, " \t;\n"); 271 if (length (argv) == 0) 272 continue; 273 274 variable cmd = argv[0]; 275 if (length (argv) == 1) 276 argv = String_Type[0]; 277 else 278 argv = argv[[1:]]; 279 280 variable e; 281 282 try (e) 283 { 284 if (0 == assoc_key_exists (Cmd_Table, cmd)) 285 { 286 () = fprintf (stderr, "%s not supported\n", cmd); 287 continue; 288 } 289 (@Cmd_Table[cmd])(argv); 290 } 291 catch AnyError: 292 { 293 () = fprintf (stderr, "%S:%d:%s *** Caught exception: %S\n", 294 e.file, e.line, e.descr, e.message); 295 } 296 } 297} 298 299public define svn_set_repository (repos) 300{ 301 !if (_svn_path_exists (repos)) 302 { 303 () = fprintf (stderr, "*** Repository %s does not exist\n", repos); 304 exit (1); 305 } 306 Repository = repos; 307 Repository_CWD = repos; 308} 309 310public define slsh_main () 311{ 312 variable repos; 313 if (__argc == 2) 314 repos = __argv[1]; 315 else 316 { 317 repos = getenv ("SVN_REPOS"); 318 if (repos == NULL) 319 { 320 () = fprintf (stderr, "Usage: %s [file://]repository\n", __argv[0]); 321 exit (1); 322 } 323 } 324 325 () = fprintf (stdout, "svnsh version %s.\nUse this at your own risk!\n", SVNSH_VERSION); 326 () = fprintf (stdout, "Enter ? for a list of commands.\n"); 327 328 !if (is_substr (repos, "://")) 329 repos = strcat ("file://", repos); 330 331 svn_set_repository (repos); 332 333 slsh_readline_init ("SVNSH"); 334 Rline = slsh_readline_new ("svnsh"); 335 336 take_input (); 337} 338