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