~ubuntu-branches/debian/squeeze/erlang/squeeze

« back to all changes in this revision

Viewing changes to lib/ssh/examples/ssh_sample_cli.erl

  • Committer: Bazaar Package Importer
  • Author(s): Erlang Packagers, Sergei Golovan
  • Date: 2006-12-03 17:07:44 UTC
  • mfrom: (2.1.11 feisty)
  • Revision ID: james.westby@ubuntu.com-20061203170744-rghjwupacqlzs6kv
Tags: 1:11.b.2-4
[ Sergei Golovan ]
Fixed erlang-base and erlang-base-hipe prerm scripts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
-module(ssh_sample_cli).
 
2
 
 
3
%% api
 
4
-export([listen/1, listen/2]).
 
5
 
 
6
%% %% our shell function
 
7
%% -export([start_our_shell/1]).
 
8
 
 
9
%% our command functions
 
10
-export([cli_prime/1, cli_primes/1, cli_gcd/2, cli_lcm/2,
 
11
         cli_factors/1, cli_exit/0, cli_rho/1, cli_help/0,
 
12
         cli_crash/0, cli_users/0, cli_self/0,
 
13
         cli_user/0, cli_host/0]).
 
14
 
 
15
%% imports
 
16
-import(lists, [reverse/1, reverse/2, seq/2, prefix/2]).
 
17
-import(math, [sqrt/1]).
 
18
 
 
19
 
 
20
listen(Port) ->
 
21
    listen(Port, []).
 
22
 
 
23
listen(Port, Options) ->
 
24
    ssh_cli:listen(fun(U, H) -> start_our_shell(U, H) end, Port, Options).
 
25
 
 
26
%% our_routines
 
27
our_routines() ->
 
28
    [
 
29
     {"crash", cli_crash,    "            crash the cli"},
 
30
     {"exit", cli_exit,      "            exit application"},
 
31
     {"factors", cli_factors,"<int>       prime factors of <int>"},
 
32
     {"gcd", cli_gcd,        "<int> <int> greatest common divisor"},
 
33
     {"help", cli_help,      "            help text"},
 
34
     {"lcm", cli_lcm,        "<int> <int> least common multiplier"},
 
35
     {"prime", cli_prime,    "<int>       check for primality"},
 
36
     {"primes", cli_primes,  "<int>       print all primes up to <int>"},
 
37
     {"rho", cli_rho,        "<int>       prime factors using rho's alg."},
 
38
     {"who",  cli_users,     "            lists users"},
 
39
     {"user", cli_user,      "            print name of user"},
 
40
     {"host", cli_host,      "            print host addr"},
 
41
     {"self", cli_self,      "            print my pid"}
 
42
    ].
 
43
 
 
44
%% (we could of course generate this from module_info() something like this)
 
45
%% our_routines1() ->
 
46
%%     {value, {exports, Exports}} =
 
47
%%      lists:keysearch(exports, 1, module_info()),
 
48
%%     get_cli(Exports, []).
 
49
 
 
50
%% our_args1(N) -> our_args1(N, "").
 
51
%% our_args1(0, S) -> S;
 
52
%% our_args1(N, S) -> our_args1(N-1, S ++ "<int> ").
 
53
 
 
54
%% get_cli([], Acc) ->
 
55
%%     lists:sort(Acc);
 
56
%% get_cli([{A, Arity} | Rest], Acc) ->
 
57
%%     L = atom_to_list(A),
 
58
%%     case lists:prefix("cli_", L) of
 
59
%%      true -> get_cli(Rest, [{tl4(L), A, our_args1(Arity)} | Acc]);
 
60
%%      false -> get_cli(Rest, Acc)
 
61
%%     end.
 
62
 
 
63
%% the longest common prefix of two strings
 
64
common_prefix([C | R1], [C | R2], Acc) ->
 
65
    common_prefix(R1, R2, [C | Acc]);
 
66
common_prefix(_, _, Acc) ->
 
67
    reverse(Acc).
 
68
 
 
69
%% longest prefix in a list, given a prefix
 
70
longest_prefix(List, Prefix) ->
 
71
    case [A || {A, _, _} <- List, prefix(Prefix, A)] of
 
72
        [] ->
 
73
            {none, List};
 
74
        [S | Rest] ->
 
75
            NewPrefix0 =
 
76
                lists:foldl(fun(A, P) ->
 
77
                                    common_prefix(A, P, [])
 
78
                            end, S, Rest),
 
79
            NewPrefix = nthtail(length(Prefix), NewPrefix0),
 
80
            {prefix, NewPrefix, [S | Rest]}
 
81
    end.                        
 
82
 
 
83
%%% our expand function (called when the user presses TAB)
 
84
%%% input: a reversed list with the row to left of the cursor
 
85
%%% output: {yes|no, Expansion, ListofPossibleMatches}
 
86
%%% where the atom no yields a beep
 
87
%%% Expansion is a string inserted at the cursor
 
88
%%% List... is a list that will be printed
 
89
%%% Here we beep on prefixes that don't match and when the command
 
90
%%% filled in
 
91
expand([$  | _]) ->
 
92
    {no, "", []};
 
93
expand(RevBefore) ->    
 
94
    Before = reverse(RevBefore),
 
95
    case longest_prefix(our_routines(), Before) of
 
96
        {prefix, P, [_]} ->
 
97
            {yes, P ++ " ", []};
 
98
        {prefix, "", M} ->
 
99
            {yes, "", M};
 
100
        {prefix, P, _M} ->
 
101
            {yes, P, []};
 
102
        {none, _M} ->
 
103
            {no, "", []}
 
104
    end.
 
105
 
 
106
%%% spawns out shell loop, we use plain io to input and output
 
107
%%% over ssh (the group module is our group leader, and takes
 
108
%%% care of sending input to the ssh_sample_cli server)
 
109
start_our_shell(User, Peer) ->
 
110
    spawn(fun() ->
 
111
                  io:setopts([{expand_fun, fun(Bef) -> expand(Bef) end}]),
 
112
                  io:format("Enter command\n"),
 
113
                  put(user, User),
 
114
                  put(peer_name, Peer),
 
115
                  our_shell_loop()
 
116
          end).
 
117
 
 
118
%%% an ordinary Read-Eval-Print-loop
 
119
our_shell_loop() ->
 
120
    % Read
 
121
    Line = io:get_line({format, "CLI> ", []}),
 
122
    % Eval
 
123
    Result = eval_cli(Line),
 
124
    % Print
 
125
    io:format("---> ~p\n", [Result]),
 
126
    case Result of
 
127
        done -> exit(normal);
 
128
        crash -> 1 / 0;
 
129
        _ -> our_shell_loop()
 
130
    end.
 
131
 
 
132
%%% translate a command to a function
 
133
command_to_function(Command) ->
 
134
    case lists:keysearch(Command, 1, our_routines()) of
 
135
        {value, {_, Proc, _}} -> Proc;
 
136
        false -> unknown_cli
 
137
    end.
 
138
 
 
139
%%% evaluate a command line
 
140
eval_cli(Line) ->
 
141
    case string:tokens(Line, " \n") of
 
142
        [] -> [];
 
143
        [Command | ArgStrings] ->
 
144
            Proc = command_to_function(Command),
 
145
            case fix_args(ArgStrings) of
 
146
                {ok, Args} ->
 
147
                    case catch apply(?MODULE, Proc, Args) of
 
148
                        {'EXIT', Error} ->
 
149
                            {error, Error}; % wrong_number_of_arguments};
 
150
                        Result ->
 
151
                            Result
 
152
                    end;
 
153
                Error ->
 
154
                    Error
 
155
            end
 
156
    end.
 
157
 
 
158
%%% make command arguments to integers
 
159
fix_args(ArgStrings) ->
 
160
    case catch [list_to_integer(A) || A <- ArgStrings] of
 
161
        {'EXIT', _} ->
 
162
            {error, only_integer_arguments};
 
163
        Args ->
 
164
            {ok, Args}
 
165
    end.
 
166
                     
 
167
%%% the commands, check for reasonable arguments here too
 
168
cli_prime(N) when N < 1000000000 ->
 
169
    rho(N) == [N] andalso is_prime(N);
 
170
cli_prime(N) when N < 10000 ->
 
171
    is_prime(N).
 
172
 
 
173
cli_primes(N) when N < 1000000 ->
 
174
    primes(N).
 
175
 
 
176
cli_gcd(A, B) when is_integer(A), is_integer(B) ->
 
177
    gcd(A, B).
 
178
 
 
179
cli_lcm(A, B) when is_integer(A), is_integer(B) ->
 
180
    lcm(A, B).
 
181
 
 
182
cli_factors(A) when A < 1000000 ->
 
183
    factors(A).
 
184
 
 
185
cli_user() ->
 
186
    get(user).
 
187
 
 
188
cli_host() ->
 
189
    get(peer_name).
 
190
 
 
191
cli_users() ->
 
192
    case ssh_userauth:get_auth_users() of
 
193
        {ok, UsersPids} ->
 
194
            UsersPids; % [U || {U, _} <- UsersPids];
 
195
        E ->
 
196
            E
 
197
    end.
 
198
 
 
199
cli_self() ->
 
200
    self().
 
201
 
 
202
cli_crash() ->
 
203
    crash.
 
204
    
 
205
cli_rho(A) ->
 
206
    rho(A).
 
207
 
 
208
cli_exit() ->
 
209
    done.
 
210
 
 
211
help_str(L) ->
 
212
    help_str(L, []).
 
213
help_str([], Acc) ->
 
214
    lists:sort(Acc);
 
215
help_str([{CommandName, _, HelpS} | Rest], Acc) ->
 
216
    C = string:left(CommandName, 10),
 
217
    help_str(Rest, [[C, " ", HelpS, $\n] | Acc]).
 
218
 
 
219
cli_help() ->
 
220
    HelpString = ["CLI Sample\n" | help_str(our_routines())],
 
221
    io:format("~s\n", [HelpString]).
 
222
 
 
223
%% a quite simple Sieve of Erastothenes (not tail-recursive, though)
 
224
primes(Size) ->
 
225
    era(sqrt(Size), seq(2,Size)).
 
226
 
 
227
era(Max, [H|T]) when H =< Max ->
 
228
    [H | era(Max, sieve([H|T], H))];
 
229
era(_Max, L) -> 
 
230
    L.
 
231
 
 
232
sieve([H|T], N) when H rem N =/= 0 ->
 
233
    [H | sieve(T, N)];
 
234
sieve([_H|T], N) ->
 
235
    sieve(T, N);
 
236
sieve([], _N) ->
 
237
    [].
 
238
 
 
239
%% another sieve, for getting the next prime incrementally
 
240
next_prime([], _) ->
 
241
    2;
 
242
next_prime([2], 2) ->
 
243
    3;
 
244
next_prime(Primes, P) ->
 
245
    next_prime1(Primes, P).
 
246
 
 
247
next_prime1(Primes, P) ->
 
248
    P1 = P + 2,
 
249
    case divides(Primes, trunc(sqrt(P1)), P1) of
 
250
        false -> P1;
 
251
        true -> next_prime1(Primes, P1)
 
252
    end.
 
253
 
 
254
divides([], _, _) ->
 
255
    false;
 
256
divides([A | _], Nsqrt, _) when A > Nsqrt ->
 
257
    false;
 
258
divides([A | _], _, N) when N rem A == 0 ->
 
259
    true;
 
260
divides([_ | R], Nsqrt, N) ->
 
261
    divides(R, Nsqrt, N).
 
262
 
 
263
is_prime(P) ->
 
264
    lists:all(fun(A) -> P rem A =/= 0 end, primes(trunc(sqrt(P)))).
 
265
 
 
266
%% Normal gcd, Euclid
 
267
gcd(R, Q) when abs(Q) < abs(R) -> gcd1(Q,R);
 
268
gcd(R, Q) -> gcd1(R,Q).
 
269
 
 
270
gcd1(0, Q) -> Q;
 
271
gcd1(R, Q) ->
 
272
    gcd1(Q rem R, R).
 
273
 
 
274
%% Least common multiple of (R,Q)
 
275
lcm(0, _Q) -> 0;
 
276
lcm(_R, 0) -> 0;
 
277
lcm(R, Q) ->
 
278
    (Q div gcd(R, Q)) * R.
 
279
 
 
280
%%% Prime factors of a number (na�ve implementation)
 
281
factors(N) ->
 
282
    Nsqrt = trunc(sqrt(N)),
 
283
    factors([], N, 2, Nsqrt, []).
 
284
    
 
285
factors(_Primes, N, Prime, Nsqrt, Factors) when Prime > Nsqrt ->
 
286
    reverse(Factors, [N]);
 
287
factors(Primes, N, Prime, Nsqrt, Factors) ->
 
288
    case N rem Prime of
 
289
        0 ->
 
290
            %%io:format("factor ------- ~p\n", [Prime]),
 
291
            N1 = N div Prime,
 
292
            factors(Primes, N1, Prime, trunc(sqrt(N1)), [Prime|Factors]);
 
293
        _ ->
 
294
            Primes1 = Primes ++ [Prime],
 
295
            Prime1 = next_prime(Primes1, Prime),
 
296
            factors(Primes1, N, Prime1, Nsqrt, Factors)
 
297
    end.
 
298
 
 
299
%%% Prime factors using Rho's algorithm ("reminded" from wikipedia.org)
 
300
%%% (should perhaps have used Brent instead, but it's not as readable)
 
301
rho_pseudo(X, C, N) ->
 
302
    (X * X + C) rem N.
 
303
 
 
304
rho(N) when N > 1000 ->
 
305
    case rho(2, 2, 1, N, fun(X) -> rho_pseudo(X, 1, N) end) of
 
306
        failure ->
 
307
            [N];
 
308
        F ->
 
309
            lists:sort(rho(F) ++ rho(N div F))
 
310
    end;
 
311
rho(N) ->
 
312
    factors(N).
 
313
 
 
314
rho(X, Y, 1, N, Pseudo) ->
 
315
    X1 = Pseudo(X),
 
316
    Y1 = Pseudo(Pseudo(Y)),
 
317
    D = gcd(absdiff(X1, Y1), N),
 
318
    rho(X1, Y1, D, N, Pseudo);
 
319
rho(_X, _Y, D, N, _Pseudo) when 1 < D, D < N ->
 
320
    D;
 
321
rho(_X, _Y, D, N, _Pseudo) when D == N ->
 
322
    failure.
 
323
    
 
324
absdiff(A, B) when A > B ->
 
325
    A - B;
 
326
absdiff(A, B) ->
 
327
    B - A.
 
328
 
 
329
%%% nthtail as in lists, but no badarg if n > the length of list
 
330
nthtail(0, A) -> A;
 
331
nthtail(N, [_ | A]) -> nthtail(N-1, A);
 
332
nthtail(_, _) -> [].