1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2003-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(ct_telnet).
22
23-export([open/1, open/2, open/3, open/4, close/1]).
24-export([cmd/2, cmd/3, cmdf/3, cmdf/4, get_data/1,
25	 send/2, send/3, sendf/3, sendf/4,
26	 expect/2, expect/3]).
27
28%% Callbacks
29-export([init/3,handle_msg/2,reconnect/2,terminate/2]).
30
31%% Tool internals
32-export([silent_teln_expect/6, teln_receive_until_prompt/3,
33	 format_data/2]).
34-export([start_gen_log/1, end_gen_log/0, log/3, log/4]).
35
36-define(RECONNS,3).
37-define(RECONN_TIMEOUT,5000).
38-define(DEFAULT_TIMEOUT,10000).
39-define(DEFAULT_PORT,23).
40-define(POLL_LIMIT,0).
41-define(POLL_INTERVAL,1000).
42
43-include("ct_util.hrl").
44
45-record(state,{host,
46	       port,
47	       teln_pid,
48	       prx,
49	       buffer=[],
50	       prompt=false,
51	       name,
52	       type,
53	       target_mod,
54	       keep_alive,
55	       poll_limit=?POLL_LIMIT,
56	       poll_interval=?POLL_INTERVAL,
57	       extra,
58	       conn_to=?DEFAULT_TIMEOUT,
59	       com_to=?DEFAULT_TIMEOUT,
60	       reconns=?RECONNS,
61	       reconn_int=?RECONN_TIMEOUT,
62	       tcp_nodelay=false}).
63
64open(Name) ->
65    open(Name,telnet).
66
67open(Name,ConnType) ->
68    case ct_util:get_key_from_name(Name) of
69	{ok, unix} -> % unix host
70	    open(Name, ConnType, unix_telnet, Name);
71	{ok, Key} -> % any other, e.g. interwatch (iw), etc.
72	    open(Name, ConnType, Key, Name);
73	Error ->
74	    Error
75    end.
76
77open(KeyOrName,ConnType,TargetMod) ->
78    open(KeyOrName,ConnType,TargetMod,KeyOrName).
79
80open(KeyOrName,ConnType,TargetMod,Extra) ->
81    case ct:get_config({KeyOrName,ConnType}) of
82	undefined ->
83	    log(undefined,open,"Failed: ~tp",[{not_available,KeyOrName}]),
84	    {error,{not_available,KeyOrName,ConnType}};
85	Addr ->
86	    Addr1 =
87		case Addr of
88		    {_IP,_Port} ->
89			Addr;
90		    IP ->
91			case ct:get_config({KeyOrName,port}) of
92			    undefined -> IP;
93			    P -> {IP,P}
94			end
95		end,
96	    KeepAlive =
97		case ct:get_config({KeyOrName,keep_alive}) of
98		    undefined ->
99			case ct:get_config({telnet_settings,keep_alive}) of
100			    undefined -> true;
101			    Bool -> Bool
102			end;
103		    Bool -> Bool
104		end,
105	    log(undefined,open,"Connecting to ~tp(~tp)",
106		[KeyOrName,Addr1]),
107	    Reconnect =
108		case ct:get_config({telnet_settings,reconnection_attempts}) of
109		    0 -> false;
110		    _ -> true
111		end,
112	    ct_gen_conn:start(full_addr(Addr1,ConnType),
113			      {TargetMod,KeepAlive,Extra},
114			      ?MODULE, [{name,KeyOrName},
115					{reconnect,Reconnect},
116					{old,true}])
117    end.
118
119close(Connection) ->
120    case get_handle(Connection) of
121	{ok,Pid} ->
122	    log(undefined,close,"Connection closed, handle: ~w",[Pid]),
123	    case ct_gen_conn:stop(Pid) of
124		{error,{process_down,Pid,_}} ->
125		    {error,already_closed};
126		Result ->
127		    Result
128	    end;
129	Error ->
130	    Error
131    end.
132
133%%%=================================================================
134%%% Test suite interface
135%%%-----------------------------------------------------------------
136
137cmd(Connection,Cmd) ->
138    cmd(Connection,Cmd,[]).
139
140cmd(Connection,Cmd,Opts) when is_list(Opts) ->
141    case check_cmd_opts(Opts) of
142	ok ->
143	    case get_handle(Connection) of
144		{ok,Pid} ->
145		    call(Pid,{cmd,Cmd,Opts});
146		Error ->
147		    Error
148	    end;
149	Error ->
150	    Error
151    end;
152cmd(Connection,Cmd,Timeout) when is_integer(Timeout); Timeout==default ->
153    %% This clause is kept for backwards compatibility only
154    cmd(Connection,Cmd,[{timeout,Timeout}]).
155
156check_cmd_opts([{timeout,Timeout}|Opts]) when is_integer(Timeout);
157					      Timeout==default ->
158    check_cmd_opts(Opts);
159check_cmd_opts([]) ->
160    ok;
161check_cmd_opts(Opts) ->
162    check_send_opts(Opts).
163
164cmdf(Connection,CmdFormat,Args) ->
165    cmdf(Connection,CmdFormat,Args,[]).
166
167cmdf(Connection,CmdFormat,Args,Opts) when is_list(Args) ->
168    Cmd = lists:flatten(io_lib:format(CmdFormat,Args)),
169    cmd(Connection,Cmd,Opts).
170
171get_data(Connection) ->
172    case get_handle(Connection) of
173	{ok,Pid} ->
174	    call(Pid,get_data);
175	Error ->
176	    Error
177    end.
178
179send(Connection,Cmd) ->
180    send(Connection,Cmd,[]).
181
182send(Connection,Cmd,Opts) ->
183    case check_send_opts(Opts) of
184	ok ->
185	    case get_handle(Connection) of
186		{ok,Pid} ->
187		    call(Pid,{send,Cmd,Opts});
188		Error ->
189		    Error
190	    end;
191	Error ->
192	    Error
193    end.
194
195check_send_opts([{newline,Bool}|Opts]) when is_boolean(Bool) ->
196    check_send_opts(Opts);
197check_send_opts([{newline,String}|Opts]) when is_list(String) ->
198    case lists:all(fun(I) when is_integer(I), I>=0, I=<127 -> true;
199                      (_) -> false
200                   end, String) of
201        true ->
202            check_send_opts(Opts);
203        false ->
204            {error,{invalid_option,{newline,String}}}
205    end;
206check_send_opts([Invalid|_]) ->
207    {error,{invalid_option,Invalid}};
208check_send_opts([]) ->
209    ok.
210
211sendf(Connection,CmdFormat,Args) when is_list(Args) ->
212    sendf(Connection,CmdFormat,Args,[]).
213
214sendf(Connection,CmdFormat,Args,Opts) when is_list(Args) ->
215    Cmd = lists:flatten(io_lib:format(CmdFormat,Args)),
216    send(Connection,Cmd,Opts).
217
218expect(Connection,Patterns) ->
219    expect(Connection,Patterns,[]).
220
221expect(Connection,Patterns,Opts) ->
222    case get_handle(Connection) of
223        {ok,Pid} ->
224            case call(Pid,{expect,Patterns,Opts}) of
225                {error,Reason} when element(1,Reason)==bad_pattern ->
226                    %% Faulty user input - should fail the test case
227                    exit({Reason,{?MODULE,?FUNCTION_NAME,3}});
228                Other ->
229                    Other
230            end;
231        Error ->
232            Error
233    end.
234
235%%%=================================================================
236%%% Callback functions
237
238init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) ->
239    S0 = case ct:get_config(telnet_settings) of
240	     undefined ->
241		 #state{};
242	     Settings ->
243		 set_telnet_defaults(Settings,#state{})
244	 end,
245    %% Handle old user versions of TargetMod
246    _ = code:ensure_loaded(TargetMod),
247    try
248	case erlang:function_exported(TargetMod,connect,7) of
249	    true ->
250		TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
251				  KeepAlive,S0#state.tcp_nodelay,Extra);
252	    false ->
253		TargetMod:connect(Name,Ip,Port,S0#state.conn_to,
254				  KeepAlive,Extra)
255	end
256    of
257	{ok,TelnPid} ->
258	    put({ct_telnet_pid2name,TelnPid},Name),
259	    S1 = S0#state{host=Ip,
260			  port=Port,
261			  teln_pid=TelnPid,
262			  name=Name,
263			  type=type(Type),
264			  target_mod=TargetMod,
265			  keep_alive=KeepAlive,
266			  extra=Extra,
267			  prx=TargetMod:get_prompt_regexp()},
268	    log(S1,open,
269		"Opened telnet connection\n"
270		"IP: ~p\n"
271		"Port: ~p\n"
272		"Command timeout: ~p\n"
273		"Reconnection attempts: ~p\n"
274		"Reconnection interval: ~p\n"
275		"Connection timeout: ~p\n"
276		"Keep alive: ~w\n"
277		"Poll limit: ~w\n"
278		"Poll interval: ~w\n"
279		"TCP nodelay: ~w",
280		[Ip,Port,S1#state.com_to,S1#state.reconns,
281		 S1#state.reconn_int,S1#state.conn_to,KeepAlive,
282		 S1#state.poll_limit,S1#state.poll_interval,
283		 S1#state.tcp_nodelay]),
284	    {ok,TelnPid,S1};
285	Error ->
286	    Error
287    catch
288	_:Reason ->
289	    {error,Reason}
290    end.
291
292type(telnet) -> ip;
293type(TS) when TS==ts1;TS==ts2 -> ts.
294
295set_telnet_defaults([{connect_timeout,CnTo}|Ss],S) ->
296    set_telnet_defaults(Ss,S#state{conn_to=CnTo});
297set_telnet_defaults([{command_timeout,CmTo}|Ss],S) ->
298    set_telnet_defaults(Ss,S#state{com_to=CmTo});
299set_telnet_defaults([{reconnection_attempts,Rs}|Ss],S) ->
300    set_telnet_defaults(Ss,S#state{reconns=Rs});
301set_telnet_defaults([{reconnection_interval,RInt}|Ss],S) ->
302    set_telnet_defaults(Ss,S#state{reconn_int=RInt});
303set_telnet_defaults([{keep_alive,_}|Ss],S) ->
304    set_telnet_defaults(Ss,S);
305set_telnet_defaults([{poll_limit,PL}|Ss],S) ->
306    set_telnet_defaults(Ss,S#state{poll_limit=PL});
307set_telnet_defaults([{poll_interval,PI}|Ss],S) ->
308    set_telnet_defaults(Ss,S#state{poll_interval=PI});
309set_telnet_defaults([{tcp_nodelay,NoDelay}|Ss],S) ->
310    set_telnet_defaults(Ss,S#state{tcp_nodelay=NoDelay});
311set_telnet_defaults([Unknown|Ss],S) ->
312    force_log(S,error,
313	      "Bad element in telnet_settings: ~tp",[Unknown]),
314    set_telnet_defaults(Ss,S);
315set_telnet_defaults([],S) ->
316    S.
317
318handle_msg({cmd,Cmd,Opts},State) ->
319    start_gen_log(heading(cmd,State#state.name)),
320    log(State,cmd,"Cmd: ~tp",[Cmd]),
321
322    %% whatever is in the buffer from previous operations
323    %% will be ignored as we go ahead with this telnet cmd
324
325    debug_cont_gen_log("Throwing Buffer:",[]),
326    debug_log_lines(State#state.buffer),
327
328    _ = case {State#state.type,State#state.prompt} of
329	{ts,_} ->
330	    silent_teln_expect(State#state.name,
331			       State#state.teln_pid,
332			       State#state.buffer,
333			       prompt,
334			       State#state.prx,
335			       [{idle_timeout,2000}]);
336	{ip,false} ->
337	    silent_teln_expect(State#state.name,
338			       State#state.teln_pid,
339			       State#state.buffer,
340			       prompt,
341			       State#state.prx,
342			       [{idle_timeout,200}]);
343	{ip,true} ->
344	    ok
345    end,
346    TO = case proplists:get_value(timeout,Opts,default) of
347	     default -> State#state.com_to;
348	     Timeout -> Timeout
349	 end,
350    Newline = proplists:get_value(newline,Opts,true),
351    {Return,NewBuffer,Prompt} =
352	case teln_cmd(State#state.teln_pid, Cmd, State#state.prx,
353		      Newline, TO) of
354	    {ok,Data,_PromptType,Rest} ->
355		log(State,recv,"Return: ~tp",[{ok,Data}]),
356		{{ok,Data},Rest,true};
357	    Error ->
358		Retry = {retry,{Error,
359				{State#state.name,
360				 State#state.type},
361				State#state.teln_pid,
362				{cmd,Cmd,Opts}}},
363		log(State,recv,"Return: ~tp",[Error]),
364		{Retry,[],false}
365	end,
366    end_gen_log(),
367    {Return,State#state{buffer=NewBuffer,prompt=Prompt}};
368handle_msg({send,Cmd,Opts},State) ->
369    start_gen_log(heading(send,State#state.name)),
370    log(State,send,"Sending: ~tp",[Cmd]),
371
372    debug_cont_gen_log("Throwing Buffer:",[]),
373    debug_log_lines(State#state.buffer),
374
375    _ = case {State#state.type,State#state.prompt} of
376	{ts,_} ->
377	    silent_teln_expect(State#state.name,
378			       State#state.teln_pid,
379			       State#state.buffer,
380			       prompt,
381			       State#state.prx,
382			       [{idle_timeout,2000}]);
383	{ip,false} ->
384	    silent_teln_expect(State#state.name,
385			       State#state.teln_pid,
386			       State#state.buffer,
387			       prompt,
388			       State#state.prx,
389			       [{idle_timeout,200}]);
390	{ip,true} ->
391	    ok
392    end,
393    Newline = proplists:get_value(newline,Opts,true),
394    ct_telnet_client:send_data(State#state.teln_pid,Cmd,Newline),
395    end_gen_log(),
396    {ok,State#state{buffer=[],prompt=false}};
397handle_msg(get_data,State) ->
398    start_gen_log(heading(get_data,State#state.name)),
399    log(State,cmd,"Reading data...",[]),
400    {ok,Data,Buffer} = teln_get_all_data(State,State#state.buffer,[],[],
401					 State#state.poll_limit),
402    log(State,recv,"Return: ~tp",[{ok,Data}]),
403    end_gen_log(),
404    {{ok,Data},State#state{buffer=Buffer}};
405handle_msg({expect,Pattern,Opts},State) ->
406    start_gen_log(heading(expect,State#state.name)),
407    log(State,expect,"Expect: ~tp\nOpts = ~tp\n",[Pattern,Opts]),
408    {Return,NewBuffer,Prompt} =
409	case teln_expect(State#state.name,
410			 State#state.teln_pid,
411			 State#state.buffer,
412			 Pattern,
413			 State#state.prx,
414			 Opts) of
415	    {ok,Data,Rest} ->
416		P = check_if_prompt_was_reached(Data,[]),
417		{{ok,Data},Rest,P};
418	    {ok,Data,HaltReason,Rest} ->
419		force_log(State,expect,"HaltReason: ~tp",[HaltReason]),
420		P = check_if_prompt_was_reached(Data,HaltReason),
421		{{ok,Data,HaltReason},Rest,P};
422	    {error,Reason,Rest} ->
423		force_log(State,expect,"Expect failed\n~tp",[{error,Reason}]),
424		P = check_if_prompt_was_reached([],Reason),
425		{{error,Reason},Rest,P};
426	    {error,Reason} ->
427		force_log(State,expect,"Expect failed\n~tp",[{error,Reason}]),
428		P = check_if_prompt_was_reached([],Reason),
429		{{error,Reason},[],P}
430	end,
431    end_gen_log(),
432    Return1 = case Return of
433		  {error,_} -> {retry,{Return,
434				       {State#state.name,
435					State#state.type},
436				       State#state.teln_pid,
437				       {expect,Pattern,Opts}}};
438		  _ -> Return
439	      end,
440    {Return1,State#state{buffer=NewBuffer,prompt=Prompt}}.
441
442
443reconnect({Ip,Port,_Type},State) ->
444    reconnect(Ip,Port,State#state.reconns,State).
445reconnect(Ip,Port,N,State=#state{name=Name,
446				 target_mod=TargetMod,
447				 keep_alive=KeepAlive,
448				 extra=Extra,
449				 conn_to=ConnTo,
450				 reconn_int=ReconnInt,
451				 tcp_nodelay=NoDelay}) ->
452    %% Handle old user versions of TargetMod
453    ConnResult =
454	case erlang:function_exported(TargetMod,connect,7) of
455	    true ->
456		TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,NoDelay,Extra);
457	    false ->
458		TargetMod:connect(Name,Ip,Port,ConnTo,KeepAlive,Extra)
459	end,
460    case ConnResult of
461	{ok,NewPid} ->
462	    put({ct_telnet_pid2name,NewPid},Name),
463	    {ok, NewPid, State#state{teln_pid=NewPid}};
464	Error when N==0 ->
465	    Error;
466	_Error ->
467	    log(State,reconnect,"Reconnect failed!","Retries left: ~w",[N]),
468	    timer:sleep(ReconnInt),
469	    reconnect(Ip,Port,N-1,State)
470    end.
471
472
473terminate(TelnPid,State) ->
474    Result = ct_telnet_client:close(TelnPid),
475    log(State,close,"Telnet connection for ~w closed.",[TelnPid]),
476    Result.
477
478%%%=================================================================
479%%% Internal function
480get_handle(Pid) when is_pid(Pid) ->
481    {ok,Pid};
482get_handle({Name,Type}) when Type==telnet;Type==ts1;Type==ts2 ->
483    case ct_util:get_connection(Name,?MODULE) of
484	{ok,Conn} ->
485	    case get_handle(Type,Conn) of
486		{ok,Pid} ->
487		    {ok,Pid};
488		_Error ->
489		    case ct_util:get_key_from_name(Name) of
490			{ok,node} ->
491			    open(Name,Type,ct_telnet_cello_node);
492			{ok,unix} -> % unix host
493			    open(Name,Type,unix_telnet,Name);
494			{ok,Key} -> % any other, e.g. interwatch (iw)
495			    open(Name,Type,Key,Name);
496			Error ->
497			    Error
498		    end
499	    end;
500	Error ->
501	    Error
502    end;
503get_handle(Name) ->
504    get_handle({Name,telnet}).
505
506get_handle(Type,{Pid,{_,_,Type}}) ->
507    {ok,Pid};
508get_handle(Type,_) ->
509    {error,{no_such_connection,Type}}.
510
511full_addr({Ip,Port},Type) ->
512    {Ip,Port,Type};
513full_addr(Ip,Type) ->
514    {Ip,?DEFAULT_PORT,Type}.
515
516call(Pid,Msg) ->
517    ct_gen_conn:call(Pid,Msg).
518
519check_if_prompt_was_reached({prompt,_},_) ->
520    true;
521check_if_prompt_was_reached(_,{prompt,_}) ->
522    true;
523check_if_prompt_was_reached(Data,_) when is_list(Data) ->
524    lists:keymember(prompt,1,Data);
525check_if_prompt_was_reached(_,_) ->
526    false.
527
528%%%-----------------------------------------------------------------
529%%% Functions for logging ct_telnet reports and telnet data
530
531heading(Action,undefined) ->
532    io_lib:format("~w ~w",[?MODULE,Action]);
533heading(Action,Name) ->
534    io_lib:format("~w ~w for ~tp",[?MODULE,Action,Name]).
535
536force_log(State,Action,String,Args) ->
537    log(State,Action,String,Args,true).
538
539%%%-----------------------------------------------------------------
540log(State,Action,String,Args) when is_record(State, state) ->
541    log(State,Action,String,Args,false);
542log(Name,Action,String,Args) when is_atom(Name) ->
543    log(#state{name=Name},Action,String,Args,false);
544log(TelnPid,Action,String,Args) when is_pid(TelnPid) ->
545    log(#state{teln_pid=TelnPid},Action,String,Args,false).
546
547%%%-----------------------------------------------------------------
548log(undefined,String,Args) ->
549    log(#state{},undefined,String,Args,false);
550log(Name,String,Args) when is_atom(Name) ->
551    log(#state{name=Name},undefined,String,Args,false);
552log(TelnPid,String,Args) when is_pid(TelnPid) ->
553    log(#state{teln_pid=TelnPid},undefined,String,Args).
554
555%%%-----------------------------------------------------------------
556log(#state{name=Name,teln_pid=TelnPid,host=Host,port=Port},
557    Action,String,Args,ForcePrint) ->
558    Name1 = if Name == undefined -> get({ct_telnet_pid2name,TelnPid});
559	       true              -> Name
560	    end,
561    Silent = get(silent),
562
563    if Action == general_io ->
564	    case ct_util:get_testdata({cth_conn_log,?MODULE}) of
565		HookMode when HookMode /= undefined, HookMode /= silent,
566			      Silent /= true ->
567		    error_logger:info_report(#conn_log{header=false,
568						       client=self(),
569						       conn_pid=TelnPid,
570						       address={Host,Port},
571						       name=Name1,
572						       action=Action,
573						       module=?MODULE},
574					     {String,Args});
575		_ -> %% hook inactive or silence requested
576		    ok
577	    end;
578
579       true ->
580	    if Action == open; Action == close; Action == reconnect;
581	       Action == info; Action == error ->
582		    ct_gen_conn:log(heading(Action,Name1),String,Args);
583
584	       ForcePrint == false ->
585		    case ct_util:is_silenced(telnet) of
586			true  ->
587			    ok;
588			false ->
589			    ct_gen_conn:cont_log_no_timestamp(String,Args)
590		    end;
591
592	       ForcePrint == true ->
593		    case ct_util:is_silenced(telnet) of
594			true ->
595			    %% call log/3 now instead of cont_log/2 since
596			    %% start_gen_log/1 will not have been previously
597			    %% called
598			    ct_gen_conn:log(heading(Action,Name1),String,Args);
599			false ->
600			    ct_gen_conn:cont_log_no_timestamp(String,Args)
601		    end
602	    end
603    end.
604
605%%%-----------------------------------------------------------------
606start_gen_log(Heading) ->
607    %% check if output is suppressed
608    case ct_util:is_silenced(telnet) of
609	true  -> ok;
610	false -> ct_gen_conn:start_log(Heading)
611    end.
612
613%%%-----------------------------------------------------------------
614end_gen_log() ->
615    %% check if output is suppressed
616    case ct_util:is_silenced(telnet) of
617	true  -> ok;
618	false -> ct_gen_conn:end_log()
619    end.
620
621%% Debug printouts.
622debug_cont_gen_log(Str,Args) ->
623    Old = put(silent,true),
624    ct_gen_conn:cont_log(Str,Args),
625    put(silent,Old).
626
627%% Log callback - called from the error handler process
628format_data(_How,{String,Args}) ->
629    io_lib:format(String,Args).
630
631%%%=================================================================
632%%% Abstraction layer on top of ct_telnet_client.erl
633teln_cmd(Pid,Cmd,Prx,Newline,Timeout) ->
634    ct_telnet_client:send_data(Pid,Cmd,Newline),
635    teln_receive_until_prompt(Pid,Prx,Timeout).
636
637teln_get_all_data(State=#state{teln_pid=Pid,prx=Prx},Data,Acc,LastLine,Polls) ->
638    case check_for_prompt(Prx,LastLine++Data) of
639	{prompt,Lines,_PromptType,Rest} ->
640	    teln_get_all_data(State,Rest,[Lines|Acc],[],State#state.poll_limit);
641	{noprompt,Lines,LastLine1} ->
642	    case ct_telnet_client:get_data(Pid) of
643		{ok,[]} when LastLine1 /= [], Polls > 0 ->
644		    %% No more data from server but the last string is not
645		    %% a complete line (maybe because of a slow connection),
646		    timer:sleep(State#state.poll_interval),
647		    NewPolls = if Polls == infinity -> infinity;
648				  true              -> Polls-1
649			       end,
650		    teln_get_all_data(State,[],[Lines|Acc],LastLine1,NewPolls);
651		{ok,[]} ->
652		    {ok,lists:reverse(lists:append([Lines|Acc])),LastLine1};
653		{ok,Data1} ->
654		    teln_get_all_data(State,Data1,[Lines|Acc],LastLine1,
655				      State#state.poll_limit)
656	    end
657    end.
658
659%% Expect options record
660-record(eo,{teln_pid,
661	    prx,
662	    idle_timeout,
663	    total_timeout,
664	    haltpatterns=[],
665	    seq=false,
666	    repeat=false,
667	    found_prompt=false,
668	    prompt_check=true}).
669
670%% Externally the silent_teln_expect function shall only be used
671%% by the TargetModule, i.e. the target specific module which
672%% implements connect/2 and get_prompt_regexp/0.
673silent_teln_expect(Name,Pid,Data,Pattern,Prx,Opts) ->
674    Old = put(silent,true),
675    Result = teln_expect(Name,Pid,Data,Pattern,Prx,Opts),
676    put(silent,Old),
677    Result.
678
679%% teln_expect/6
680%%
681%% This function implements the expect functionality over telnet. In
682%% general there are three possible ways to go:
683%% 1) Single: One or more patterns are given, and the function return
684%% when one of the patterns are matched.
685%% 2) Sequence: Several patterns are given, and they are matched in
686%% the order they appear in the pattern list.
687%% 3a) Repeat (single): 1) is repeated either N times or until a halt
688%% condition is fulfilled.
689%% 3b) Repeat (sequence): 2) is repeated either N times or until a
690%% halt condition is fulfilled.
691teln_expect(Name,Pid,Data,Pattern0,Prx,Opts) ->
692    HaltPatterns0 =
693	case get_ignore_prompt(Opts) of
694	    true ->
695		get_haltpatterns(Opts);
696	    false ->
697		[prompt | get_haltpatterns(Opts)]
698	end,
699    case convert_pattern(HaltPatterns0,false) of
700        {ok,HaltPatterns} ->
701            {WaitForPrompt,Pattern1,Opts1} = wait_for_prompt(Pattern0,Opts),
702            Seq = get_seq(Opts1),
703            case convert_pattern(Pattern1,Seq) of
704                {ok,Pattern2} ->
705                    {IdleTimeout,TotalTimeout} = get_timeouts(Opts1),
706                    PromptCheck = get_prompt_check(Opts1),
707
708                    EO = #eo{teln_pid=Pid,
709                             prx=Prx,
710                             idle_timeout=IdleTimeout,
711                             total_timeout=TotalTimeout,
712                             seq=Seq,
713                             haltpatterns=HaltPatterns,
714                             prompt_check=PromptCheck},
715
716                    case get_repeat(Opts1) of
717                        false ->
718                            case teln_expect1(Name,Pid,Data,Pattern2,[],EO) of
719                                {ok,Matched,Rest} when WaitForPrompt ->
720                                    case lists:reverse(Matched) of
721                                        [{prompt,_},Matched1] ->
722                                            {ok,Matched1,Rest};
723                                        [{prompt,_}|Matched1] ->
724                                            {ok,lists:reverse(Matched1),Rest}
725                                    end;
726                                {ok,Matched,Rest} ->
727                                    {ok,Matched,Rest};
728                                {halt,Why,Rest} ->
729                                    {error,Why,Rest};
730                                {error,Reason} ->
731                                    {error,Reason}
732                            end;
733                        N ->
734                            EO1 = EO#eo{repeat=N},
735                            repeat_expect(Name,Pid,Data,Pattern2,[],EO1)
736                    end;
737               Error ->
738                    Error
739            end;
740        Error ->
741            Error
742    end.
743
744convert_pattern(Pattern0,Seq)
745  when Pattern0==[] orelse (is_list(Pattern0) and not is_integer(hd(Pattern0))) ->
746    Pattern =
747        case Seq of
748            true -> Pattern0;
749            false -> rm_dupl(Pattern0,[])
750        end,
751    compile_pattern(Pattern,[]);
752convert_pattern(Pattern,_Seq) ->
753    compile_pattern([Pattern],[]).
754
755rm_dupl([P|Ps],Acc) ->
756    case lists:member(P,Acc) of
757	true ->
758	    rm_dupl(Ps,Acc);
759	false ->
760	    rm_dupl(Ps,[P|Acc])
761    end;
762rm_dupl([],Acc) ->
763    lists:reverse(Acc).
764
765compile_pattern([prompt|Patterns],Acc) ->
766    compile_pattern(Patterns,[prompt|Acc]);
767compile_pattern([{prompt,_}=P|Patterns],Acc) ->
768    compile_pattern(Patterns,[P|Acc]);
769compile_pattern([{Tag,Pattern}|Patterns],Acc) ->
770    try re:compile(Pattern,[unicode]) of
771        {ok,MP} -> compile_pattern(Patterns,[{Tag,MP}|Acc]);
772        {error,Error} -> {error,{bad_pattern,{Tag,Pattern},Error}}
773    catch error:badarg -> {error,{bad_pattern,{Tag,Pattern}}}
774    end;
775compile_pattern([Pattern|Patterns],Acc) ->
776    try re:compile(Pattern,[unicode]) of
777        {ok,MP} -> compile_pattern(Patterns,[MP|Acc]);
778        {error,Error} -> {error,{bad_pattern,Pattern,Error}}
779    catch error:badarg -> {error,{bad_pattern,Pattern}}
780    end;
781compile_pattern([],Acc) ->
782    {ok,lists:reverse(Acc)}.
783
784get_timeouts(Opts) ->
785    {case lists:keysearch(idle_timeout,1,Opts) of
786	 {value,{_,T}} ->
787	     T;
788	 false ->
789	     %% this check is for backwards compatibility (pre CT v1.8)
790	     case lists:keysearch(timeout,1,Opts) of
791		 {value,{_,T}} -> T;
792		 false -> ?DEFAULT_TIMEOUT
793	     end
794     end,
795     case lists:keysearch(total_timeout,1,Opts) of
796	 {value,{_,T}} -> T;
797	 false -> infinity
798     end}.
799
800get_repeat(Opts) ->
801    case lists:keysearch(repeat,1,Opts) of
802	{value,{repeat,N}} when is_integer(N) ->
803	    N;
804	false ->
805	    case lists:member(repeat,Opts) of
806		true ->
807		    -1;
808		false ->
809		    false
810	    end
811    end.
812get_seq(Opts) ->
813    lists:member(sequence,Opts).
814get_haltpatterns(Opts) ->
815    case lists:keysearch(halt,1,Opts) of
816	{value,{halt,HaltPatterns}} ->
817	    HaltPatterns;
818	false ->
819	    []
820    end.
821get_ignore_prompt(Opts) ->
822    lists:member(ignore_prompt,Opts).
823get_prompt_check(Opts) ->
824    not lists:member(no_prompt_check,Opts).
825
826wait_for_prompt(Pattern, Opts) ->
827    case lists:member(wait_for_prompt, Opts) of
828	true ->
829	    wait_for_prompt1(prompt, Pattern,
830			     lists:delete(wait_for_prompt,Opts));
831	false ->
832	    case proplists:get_value(wait_for_prompt, Opts) of
833		undefined ->
834		    {false,Pattern,Opts};
835		PromptStr ->
836		    wait_for_prompt1({prompt,PromptStr}, Pattern,
837				     proplists:delete(wait_for_prompt,Opts))
838	    end
839    end.
840
841wait_for_prompt1(Prompt, [Ch|_] = Pattern, Opts) when is_integer(Ch) ->
842    wait_for_prompt2(Prompt, [Pattern], Opts);
843wait_for_prompt1(Prompt, Pattern, Opts) when is_list(Pattern) ->
844    wait_for_prompt2(Prompt, Pattern, Opts);
845wait_for_prompt1(Prompt, Pattern, Opts) ->
846    wait_for_prompt2(Prompt, [Pattern], Opts).
847
848wait_for_prompt2(Prompt, Pattern, Opts) ->
849    Pattern1 = case lists:reverse(Pattern) of
850		   [prompt|_]     -> Pattern;
851		   [{prompt,_}|_] -> Pattern;
852		   _              -> Pattern ++ [Prompt]
853	       end,
854    Opts1 = case lists:member(sequence, Opts) of
855		true ->  Opts;
856		false -> [sequence|Opts]
857	    end,
858    {true,Pattern1,Opts1}.
859
860%% Repeat either single or sequence. All match results are accumulated
861%% and returned when a halt condition is fulfilled.
862repeat_expect(_Name,_Pid,Rest,_Pattern,Acc,#eo{repeat=0}) ->
863    {ok,lists:reverse(Acc),done,Rest};
864repeat_expect(Name,Pid,Data,Pattern,Acc,EO) ->
865    case teln_expect1(Name,Pid,Data,Pattern,[],EO) of
866	{ok,Matched,Rest} ->
867	    EO1 = EO#eo{repeat=EO#eo.repeat-1},
868	    repeat_expect(Name,Pid,Rest,Pattern,[Matched|Acc],EO1);
869	{halt,Why,Rest} ->
870	    {ok,lists:reverse(Acc),Why,Rest};
871	{error,Reason} ->
872	    {error,Reason}
873    end.
874
875teln_expect1(Name,Pid,Data,Pattern,Acc,EO=#eo{idle_timeout=IdleTO,
876					      total_timeout=TotalTO}) ->
877    %% TotalTO is a float value in this loop (unless it's 'infinity'),
878    %% but an integer value will be passed to the other functions
879    EOMod = if TotalTO /= infinity -> EO#eo{total_timeout=trunc(TotalTO)};
880	       true                -> EO
881	    end,
882    ExpectFun = case EOMod#eo.seq of
883		    true -> fun() ->
884				    seq_expect(Name,Pid,Data,Pattern,Acc,EOMod)
885			    end;
886		    false -> fun() ->
887				     one_expect(Name,Pid,Data,Pattern,EOMod)
888			     end
889		end,
890    case ExpectFun() of
891	{match,Match,Rest} ->
892	    {ok,Match,Rest};
893	{halt,Why,Rest} ->
894	    {halt,Why,Rest};
895	NotFinished ->
896	    %% Get more data
897	    Fun = fun() -> get_data1(EOMod#eo.teln_pid) end,
898	    BreakAfter = if TotalTO < IdleTO ->
899				 %% use the integer value
900				 EOMod#eo.total_timeout;
901			    true ->
902				 IdleTO
903			 end,
904	    {PatOrPats1,Acc1,Rest1} = case NotFinished of
905					 {nomatch,Rest0} ->
906					     %% one expect
907					     {Pattern,[],Rest0};
908					 {continue,Pats0,Acc0,Rest0} ->
909					     %% sequence
910					     {Pats0,Acc0,Rest0}
911				     end,
912	    case timer:tc(ct_gen_conn, do_within_time, [Fun,BreakAfter]) of
913		{_,{error,Reason}} ->
914		    %% A timeout will occur when the telnet connection
915		    %% is idle for EO#eo.idle_timeout milliseconds.
916		    if Rest1 /= [] ->
917			    log(name_or_pid(Name,Pid),"       ~ts",[Rest1]);
918		       true ->
919			    ok
920		    end,
921		    {error,Reason};
922		{_,{ok,Data1}} when TotalTO == infinity ->
923		    teln_expect1(Name,Pid,Rest1++Data1,PatOrPats1,Acc1,EOMod);
924		{Elapsed,{ok,Data1}} ->
925		    TVal = TotalTO - (Elapsed/1000),
926		    if TVal =< 0 ->
927			    {error,timeout};
928		       true ->
929			    EO1 = EO#eo{total_timeout = TVal},
930			    teln_expect1(Name,Pid,Rest1++Data1,
931					 PatOrPats1,Acc1,EO1)
932		    end
933	    end
934    end.
935
936get_data1(Pid) ->
937    case ct_telnet_client:get_data(Pid) of
938	{ok,[]} ->
939	    get_data1(Pid);
940	{ok,Data} ->
941	    {ok,Data}
942    end.
943
944%% 1) Single expect.
945%% First the whole data chunk is searched for a prompt (to avoid doing
946%% a regexp match for the prompt at each line).
947%% If we are searching for anything else, the datachunk is split into
948%% lines and each line is matched against each pattern.
949
950%% one_expect: split data chunk at prompts
951one_expect(Name,Pid,Data,Pattern,EO) when EO#eo.prompt_check==false ->
952%    io:format("Raw Data ~tp Pattern ~tp EO ~tp ",[Data,Pattern,EO]),
953    one_expect1(Name,Pid,Data,Pattern,[],EO#eo{found_prompt=false});
954one_expect(Name,Pid,Data,Pattern,EO) ->
955    case match_prompt(Data,EO#eo.prx) of
956	{prompt,UptoPrompt,PromptType,Rest} ->
957	    case Pattern of
958		[Prompt] when Prompt==prompt; Prompt=={prompt,PromptType} ->
959		    %% Only searching for prompt
960		    log_lines(Name,Pid,UptoPrompt),
961		    log(name_or_pid(Name,Pid),"PROMPT: ~ts",[PromptType]),
962		    {match,{prompt,PromptType},Rest};
963		[{prompt,_OtherPromptType}] ->
964		    %% Only searching for one specific prompt, not this one
965		    log_lines(Name,Pid,UptoPrompt),
966		    {nomatch,Rest};
967		_ ->
968		    one_expect1(Name,Pid,UptoPrompt,Pattern,Rest,
969				EO#eo{found_prompt=PromptType})
970	    end;
971	noprompt ->
972	    case Pattern of
973		[Prompt] when Prompt==prompt; element(1,Prompt)==prompt ->
974		    %% Only searching for prompt
975		    LastLine = log_lines_not_last(Name,Pid,Data),
976		    {nomatch,LastLine};
977		_ ->
978		    one_expect1(Name,Pid,Data,Pattern,[],
979				EO#eo{found_prompt=false})
980	    end
981    end.
982
983%% one_expect1: split data chunk at lines
984one_expect1(Name,Pid,Data,Pattern,Rest,EO) ->
985    case match_lines(Name,Pid,Data,Pattern,EO) of
986	{match,Match,MatchRest} ->
987	    {match,Match,MatchRest++Rest};
988	{nomatch,prompt} ->
989	    one_expect(Name,Pid,Rest,Pattern,EO);
990	{nomatch,NoMatchRest} ->
991	    {nomatch,NoMatchRest++Rest};
992	{halt,Why,HaltRest} ->
993	    {halt,Why,HaltRest++Rest}
994    end.
995
996
997%% 2) Sequence.
998%% First the whole data chunk is searched for a prompt (to avoid doing
999%% a regexp match for the prompt at each line).
1000%% If we are searching for anything else, the datachunk is split into
1001%% lines and each line is matched against the first pattern in the list.
1002%% When a match is found, the match result is accumulated, and we keep
1003%% searching for the next pattern in the list.
1004
1005%% seq_expect: Split data chunk at prompts
1006seq_expect(_Name,_Pid,Data,[],Acc,_EO) ->
1007    {match,lists:reverse(Acc),Data};
1008seq_expect(_Name,_Pid,[],Patterns,Acc,_EO) ->
1009    {continue,Patterns,lists:reverse(Acc),[]};
1010seq_expect(Name,Pid,Data,Patterns,Acc,EO) when EO#eo.prompt_check==false ->
1011    seq_expect1(Name,Pid,Data,Patterns,Acc,[],EO#eo{found_prompt=false});
1012seq_expect(Name,Pid,Data,Patterns,Acc,EO) ->
1013    case match_prompt(Data,EO#eo.prx) of
1014	{prompt,UptoPrompt,PromptType,Rest} ->
1015	    seq_expect1(Name,Pid,UptoPrompt,Patterns,Acc,Rest,
1016			EO#eo{found_prompt=PromptType});
1017	noprompt ->
1018	    seq_expect1(Name,Pid,Data,Patterns,Acc,[],EO#eo{found_prompt=false})
1019    end.
1020
1021%% seq_expect1: For one prompt-chunk, match each pattern - line by
1022%% line if it is other than the prompt we are seaching for.
1023seq_expect1(Name,Pid,Data,[prompt|Patterns],Acc,Rest,EO) ->
1024    case EO#eo.found_prompt of
1025	false ->
1026	    LastLine = log_lines_not_last(Name,Pid,Data),
1027	    %% Rest==[] because no prompt is found
1028	    {continue,[prompt|Patterns],Acc,LastLine};
1029	PromptType ->
1030	    log_lines(Name,Pid,Data),
1031	    log(name_or_pid(Name,Pid),"PROMPT: ~ts",[PromptType]),
1032	    seq_expect(Name,Pid,Rest,Patterns,[{prompt,PromptType}|Acc],EO)
1033    end;
1034seq_expect1(Name,Pid,Data,[{prompt,PromptType}|Patterns],Acc,Rest,EO) ->
1035    case EO#eo.found_prompt of
1036	false ->
1037	    LastLine = log_lines_not_last(Name,Pid,Data),
1038	    %% Rest==[] because no prompt is found
1039	    {continue,[{prompt,PromptType}|Patterns],Acc,LastLine};
1040	PromptType ->
1041	    log_lines(Name,Pid,Data),
1042	    log(name_or_pid(Name,Pid),"PROMPT: ~ts", [PromptType]),
1043	    seq_expect(Name,Pid,Rest,Patterns,[{prompt,PromptType}|Acc],EO);
1044	_OtherPromptType ->
1045	    log_lines(Name,Pid,Data),
1046	    seq_expect(Name,Pid,Rest,[{prompt,PromptType}|Patterns],Acc,EO)
1047    end;
1048seq_expect1(Name,Pid,Data,[Pattern|Patterns],Acc,Rest,EO) ->
1049    case match_lines(Name,Pid,Data,[Pattern],EO) of
1050	{match,Match,MatchRest} ->
1051	    seq_expect1(Name,Pid,MatchRest,Patterns,[Match|Acc],Rest,EO);
1052	{nomatch,prompt} ->
1053	    seq_expect(Name,Pid,Rest,[Pattern|Patterns],Acc,EO);
1054	{nomatch,NoMatchRest} when Rest==[] ->
1055	    %% The data did not end with a prompt
1056	    {continue,[Pattern|Patterns],Acc,NoMatchRest};
1057	{halt,Why,HaltRest} ->
1058	    {halt,Why,HaltRest++Rest}
1059    end;
1060seq_expect1(_Name,_Pid,Data,[],Acc,Rest,_EO) ->
1061    {match,lists:reverse(Acc),Data++Rest}.
1062
1063%% Split prompt-chunk at lines
1064match_lines(Name,Pid,Data,Patterns,EO) ->
1065    FoundPrompt = EO#eo.found_prompt,
1066    case one_line(Data,[]) of
1067	{noline,Rest} when FoundPrompt=/=false ->
1068	    %% This is the line including the prompt
1069	    case match_line(Name,Pid,Rest,Patterns,FoundPrompt,false,EO) of
1070		nomatch ->
1071		    {nomatch,prompt};
1072		{Tag,Match} ->
1073		    {Tag,Match,[]}
1074	    end;
1075	{noline,Rest} when EO#eo.prompt_check==false ->
1076	    case match_line(Name,Pid,Rest,Patterns,false,false,EO) of
1077		nomatch ->
1078		    {nomatch,Rest};
1079		{Tag,Match} ->
1080		    {Tag,Match,[]}
1081	    end;
1082	{noline,Rest} ->
1083	    {nomatch,Rest};
1084	{Line,Rest} ->
1085	    case match_line(Name,Pid,Line,Patterns,false,true,EO) of
1086		nomatch ->
1087		    match_lines(Name,Pid,Rest,Patterns,EO);
1088		{Tag,Match} ->
1089		    {Tag,Match,Rest}
1090	    end
1091    end.
1092
1093%% For one line, match each pattern
1094match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO) ->
1095    match_line(Name,Pid,Line,Patterns,FoundPrompt,Terminated,EO,match).
1096
1097match_line(Name,Pid,Line,[prompt|Patterns],false,Term,EO,RetTag) ->
1098    match_line(Name,Pid,Line,Patterns,false,Term,EO,RetTag);
1099match_line(Name,Pid,Line,[prompt|_Patterns],FoundPrompt,_Term,_EO,RetTag) ->
1100    log(name_or_pid(Name,Pid),"       ~ts",[Line]),
1101    log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
1102    {RetTag,{prompt,FoundPrompt}};
1103match_line(Name,Pid,Line,[{prompt,PromptType}|_Patterns],FoundPrompt,_Term,
1104	   _EO,RetTag) when PromptType==FoundPrompt ->
1105    log(name_or_pid(Name,Pid),"       ~ts",[Line]),
1106    log(name_or_pid(Name,Pid),"PROMPT: ~ts",[FoundPrompt]),
1107    {RetTag,{prompt,FoundPrompt}};
1108match_line(Name,Pid,Line,[{prompt,PromptType}|Patterns],FoundPrompt,Term,
1109	   EO,RetTag)
1110  when PromptType=/=FoundPrompt ->
1111    match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
1112match_line(Name,Pid,Line,[{Tag,Pattern}|Patterns],FoundPrompt,Term,EO,RetTag) ->
1113    case re:run(Line,Pattern,[{capture,all,list}]) of
1114	nomatch ->
1115	    match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
1116	{match,Match} ->
1117	    log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
1118	    {RetTag,{Tag,Match}}
1119    end;
1120match_line(Name,Pid,Line,[Pattern|Patterns],FoundPrompt,Term,EO,RetTag) ->
1121    case re:run(Line,Pattern,[{capture,all,list}]) of
1122	nomatch ->
1123	    match_line(Name,Pid,Line,Patterns,FoundPrompt,Term,EO,RetTag);
1124	{match,Match} ->
1125	    log(name_or_pid(Name,Pid),"MATCH: ~ts",[Line]),
1126	    {RetTag,Match}
1127    end;
1128match_line(Name,Pid,Line,[],FoundPrompt,Term,EO,match) ->
1129    match_line(Name,Pid,Line,EO#eo.haltpatterns,FoundPrompt,Term,EO,halt);
1130%% print any terminated line that cannot be matched
1131match_line(Name,Pid,Line,[],_FoundPrompt,true,_EO,halt) ->
1132    log(name_or_pid(Name,Pid),"       ~ts",[Line]),
1133    nomatch;
1134%% if there's no line termination, Line is saved as Rest (above) and will
1135%% be printed later
1136match_line(_Name,_Pid,_Line,[],_FoundPrompt,false,_EO,halt) ->
1137    nomatch.
1138
1139one_line([$\n|Rest],Line) ->
1140    {lists:reverse(Line),Rest};
1141one_line([$\r|Rest],Line) ->
1142    one_line(Rest,Line);
1143one_line([0|Rest],Line) ->
1144    one_line(Rest,Line);
1145one_line([Char|Rest],Line) ->
1146    one_line(Rest,[Char|Line]);
1147one_line([],Line) ->
1148    {noline,lists:reverse(Line)}.
1149
1150debug_log_lines(String) ->
1151    Old = put(silent,true),
1152    log_lines(undefined,undefined,String),
1153    put(silent,Old).
1154
1155log_lines(Name,Pid,String) ->
1156    case log_lines_not_last(Name,Pid,String) of
1157	[] ->
1158	    ok;
1159	LastLine ->
1160	    log(name_or_pid(Name,Pid),"       ~ts",[LastLine])
1161    end.
1162
1163log_lines_not_last(Name,Pid,String) ->
1164    case add_tabs(String,[],[]) of
1165	{[],LastLine} ->
1166	    LastLine;
1167	{String1,LastLine} ->
1168	    log(name_or_pid(Name,Pid),"~ts",[String1]),
1169	    LastLine
1170    end.
1171
1172name_or_pid(undefined,Pid) -> Pid;
1173name_or_pid(Name,_) -> Name.
1174
1175add_tabs([0|Rest],Acc,LastLine) ->
1176    add_tabs(Rest,Acc,LastLine);
1177add_tabs([$\r|Rest],Acc,LastLine) ->
1178    add_tabs(Rest,Acc,LastLine);
1179add_tabs([$\n|Rest],Acc,LastLine) ->
1180    add_tabs(Rest,[$\n|LastLine] ++ [$\s,$\s,$\s,$\s,$\s,$\s,$\s|Acc],[]);
1181add_tabs([Ch|Rest],Acc,LastLine) ->
1182    add_tabs(Rest,Acc,[Ch|LastLine]);
1183add_tabs([],[$\n|Acc],LastLine) ->
1184    {lists:reverse(Acc),lists:reverse(LastLine)};
1185add_tabs([],[],LastLine) ->
1186    {[],lists:reverse(LastLine)}.
1187
1188teln_receive_until_prompt(Pid,Prx,Timeout) ->
1189    Fun = fun() -> teln_receive_until_prompt(Pid,Prx,[],[]) end,
1190    ct_gen_conn:do_within_time(Fun, Timeout).
1191
1192teln_receive_until_prompt(Pid,Prx,Acc,LastLine) ->
1193    {ok,Data} = ct_telnet_client:get_data(Pid),
1194    case check_for_prompt(Prx,LastLine++Data) of
1195	{prompt,Lines,PromptType,Rest} ->
1196	    Return = lists:reverse(lists:append([Lines|Acc])),
1197	   {ok,Return,PromptType,Rest};
1198	{noprompt,Lines,LastLine1} ->
1199	    teln_receive_until_prompt(Pid,Prx,[Lines|Acc],LastLine1)
1200   end.
1201
1202check_for_prompt(Prx,Data) ->
1203    case match_prompt(Data,Prx) of
1204	{prompt,UptoPrompt,PromptType,Rest} ->
1205	    {RevLines,LastLine} = split_lines(UptoPrompt),
1206	    {prompt,[LastLine|RevLines],PromptType,Rest};
1207	noprompt ->
1208	    {RevLines,Rest} = split_lines(Data),
1209	    {noprompt,RevLines,Rest}
1210    end.
1211
1212split_lines(String) ->
1213    split_lines(String,[],[]).
1214split_lines([$\n|Rest],Line,Lines) when Line /= [] ->
1215    split_lines(Rest,[],[lists:reverse(Line)|Lines]);
1216split_lines([$\n|Rest],[],Lines) ->
1217    split_lines(Rest,[],Lines);
1218split_lines([$\r|Rest],Line,Lines) ->
1219    split_lines(Rest,Line,Lines);
1220split_lines([0|Rest],Line,Lines) ->
1221    split_lines(Rest,Line,Lines);
1222split_lines([Char|Rest],Line,Lines) ->
1223    split_lines(Rest,[Char|Line],Lines);
1224split_lines([],Line,Lines) ->
1225    {Lines,lists:reverse(Line)}.
1226
1227
1228match_prompt(Str,Prx) ->
1229    match_prompt(Str,Prx,[]).
1230match_prompt(Str,Prx,Acc) ->
1231    case re:run(Str,Prx,[unicode]) of
1232	nomatch ->
1233	    noprompt;
1234	{match,[{Start,Len}]} ->
1235	    case split_prompt_string(Str,Start+1,Start+Len,1,[],[]) of
1236		{noprompt,Done,Rest} ->
1237		    match_prompt(Rest,Prx,Done);
1238		{prompt,UptoPrompt,Prompt,Rest} ->
1239		    {prompt,lists:reverse(UptoPrompt++Acc),
1240		     lists:reverse(Prompt),Rest}
1241	    end
1242    end.
1243
1244split_prompt_string([Ch|Str],Start,End,N,UptoPrompt,Prompt) when N<Start ->
1245    split_prompt_string(Str,Start,End,N+1,[Ch|UptoPrompt],Prompt);
1246split_prompt_string([Ch|Str],Start,End,N,UptoPrompt,Prompt)
1247  when N>=Start, N<End->
1248    split_prompt_string(Str,Start,End,N+1,UptoPrompt,[Ch|Prompt]);
1249split_prompt_string([Ch|Rest],_Start,End,N,UptoPrompt,Prompt) when N==End ->
1250    case UptoPrompt of
1251	[$",$=,$T,$P,$M,$O,$R,$P|_] ->
1252	    %% This is a line from "listenv", it is not a real prompt
1253	    {noprompt,[Ch|Prompt]++UptoPrompt,Rest};
1254	[$\s,$t,$s,$a|_] when Prompt==":nigol" ->
1255	    %% This is probably the "Last login:" statement which is
1256	    %% written when telnet connection is openend.
1257	    {noprompt,[Ch|Prompt]++UptoPrompt,Rest};
1258	_ ->
1259	    {prompt,[Ch|Prompt]++UptoPrompt,[Ch|Prompt],Rest}
1260    end.
1261