=== source code for eprof_SUITE:eed/1

=== Test case started with:
eprof_SUITE:eed([{watchdog,<0.1453.0>},
                 {tc_logfile,"/home/pi/R16B03/otp-support/release/tests/test_server/ct_run.test_server@localhost.2014-01-03_22.55.34/tests.tools_test.logs/run.2014-01-03_22.56.14/eprof_suite.eed.html"},
                 {tc_group_properties,[]},
                 {tc_group_path,[]},
                 {data_dir,"/home/pi/R16B03/otp-support/release/tests/tools_test/eprof_SUITE_data/"},
                 {priv_dir,"/home/pi/R16B03/otp-support/release/tests/test_server/ct_run.test_server@localhost.2014-01-03_22.55.34/tests.tools_test.logs/run.2014-01-03_22.56.14/log_private/"},
                 {nodenames,[]}])

=== Current directory is "/home/pi/R16B03/otp-support/release/tests/test_server/ct_run.test_server@localhost.2014-01-03_22.55.34"

=== Started at 2014-01-03 22:57:36


23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. 23901 %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson cmd_line([Script]) -> file(Script), halt(). %%%---------------------------------------------------------------------- %%% File : eed.erl %%% Author : Bjorn Gustavsson %%% Purpose : Unix `ed' look-alike. %%% Skapad : 24 Aug 1997 by Bjorn Gustavsson %%%---------------------------------------------------------------------- -module(eed). -author('bjorn@strider'). -export([edit/0, edit/1, file/1, cmd_line/1]). -compile({no_auto_import,[error/1]}). -record(state, {dot = 0, % Line number of dot. upto_dot = [], % Lines up to dot (reversed). after_dot = [], % Lines after dot. lines = 0, % Total number of lines. print=false, % Print after command. filename=[], % Current file. pattern, % Current pattern. in_global=false, % True if executing global command. input=[], % Global input stream. undo, % Last undo state. marks=[], % List of marks. modified=false, % Buffer is modified. opts=[{prompt, ''}], % Options. last_error, % The last error encountered. input_fd % Input file descriptor. }). -record(line, {contents, % Contents of line. mark=false % Marked (for global prefix). }). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- cmd_line([Script]) -> file(Script), halt(). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file(Script) -> case file:open(Script, [read]) of {ok,Fd} -> loop(#state{input_fd=Fd}), ok; {error,E} -> {error,E} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit() -> loop(#state{input_fd=group_leader()}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- edit(Name) -> loop(command([$e|Name], #state{input_fd=group_leader()})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- loop(St0) -> {ok, St1, Cmd} = get_line(St0), case catch command(lib:nonl(Cmd), St1) of {'EXIT', Reason} -> %% XXX Should clear outstanding global command here. loop(print_error({'EXIT', Reason}, St1)); quit -> ok; {error, Reason} -> loop(print_error(Reason, St1)); St2 when is_record(St2, state) -> loop(St2) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- command(Cmd, St) -> case parse_command(Cmd, St) of quit -> quit; St1 when is_function(St1#state.print) -> if St1#state.dot /= 0 -> print_current(St1); true -> ok end, St1#state{print=false}; St1 when is_record(St1, state) -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(St) -> Opts = St#state.opts, {value, {prompt, Prompt}} = lists:keysearch(prompt, 1, Opts), get_line(Prompt, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(Prompt, St) when St#state.input == [] -> Line = get_line1(St#state.input_fd, Prompt, []), {ok, St, Line}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_line(_, St) -> get_input(St#state.input, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, []) -> {ok, St, eof}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([eof], St, Result) -> {ok, St#state{input=[eof]}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([$\n|Rest], St, Result) -> {ok, St#state{input=Rest}, lists:reverse(Result)}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_input([C|Rest], St, Result) -> get_input(Rest, St, [C|Result]). get_line1(Io, Prompt, Result) -> get_line2(Io, io:get_line(Io, Prompt), Result). get_line2(_Io, eof, []) -> eof; get_line2(_Io, eof, Result) -> lists:reverse(Result); get_line2(Io, [$\\, $\n], Result) -> get_line1(Io, '', [$\n|Result]); get_line2(_Io, [$\n], Result) -> lists:reverse(Result, [$\n]); get_line2(Io, [C|Rest], Result) -> get_line2(Io, Rest, [C|Result]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_error(Reason, St0) -> St1 = St0#state{last_error=Reason}, io:put_chars("?\n"), case lists:member(help_always, St1#state.opts) of true -> help_command([], [], St1), St1; false -> St1 end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_command) -> "unknown command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_filename) -> "illegal or missing filename"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_file) -> "cannot open input file"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_linenum) -> "line out of range"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_delimiter) -> "illegal or missing delimiter"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_undo) -> "nothing to undo"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_mark) -> "mark not lower case ascii"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(bad_pattern) -> "invalid regular expression"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(buffer_modified) -> "warning: expecting `w'"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nested_globals) -> "multiple globals not allowed"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(nomatch) -> "search string not found"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(missing_space) -> "no space after command"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(garbage_after_command) -> "illegal suffix"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(not_implemented) -> "not implemented yet"; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error({'EXIT', {Code, {Mod, Func, Args}}}) -> lists:flatten(io_lib:format("aborted due to bug (~p)", [{Code, {Mod, Func, length(Args)}}])); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- format_error(A) -> atom_to_list(A). %%% Parsing commands. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, St) -> parse_command(Cmd, St, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_command(Cmd, State, Nums) -> case get_one(Cmd, State) of {ok, Num, Rest, NewState} -> parse_next_address(Rest, NewState, [Num|Nums]); false -> parse_command1(Cmd, State, Nums) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$,|Rest], State, Nums) -> parse_command(Rest, State, Nums); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address([$;|Rest], State, [Num|Nums]) -> parse_command(Rest, move_to(Num, State), [Num|Nums]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_next_address(Rest, State, Nums) -> parse_command1(Rest, State, Nums). parse_command1([Letter|Rest], State, Nums) -> Cont = fun(Fun, NumLines, Def) -> execute_command(Fun, NumLines, Def, State, Nums, Rest) end, parse_cmd_char(Letter, Cont); parse_command1([], State, Nums) -> execute_command(fun print_command/3, 1, next, State, Nums, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_one(Cmd, St) -> case get_address(Cmd, St) of {ok, Addr, Cmd1, St1} -> get_one1(Cmd1, Addr, St1); false -> get_one1(Cmd, false, St) end. get_one1([D|Rest], false, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, 0, St); get_one1([D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$+, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), 1, Sum, St); get_one1([$-, D|Rest], Sum, St) when $0 =< D, D =< $9 -> get_one2(get_number([D|Rest]), -1, Sum, St); get_one1([$+|Rest], Sum, St) -> get_one2({ok, 1, Rest}, 1, Sum, St); get_one1([$-|Rest], Sum, St) -> get_one2({ok, 1, Rest}, -1, Sum, St); get_one1(_Cmd, false, _St) -> false; get_one1(Cmd, Sum, St) -> {ok, Sum, Cmd, St}. get_one2({ok, Number, Rest}, Mul, false, St) -> get_one1(Rest, St#state.dot+Mul*Number, St); get_one2({ok, Number, Rest}, Mul, Sum, St) -> get_one1(Rest, Sum+Mul*Number, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Cmd) -> get_number(Cmd, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number([D|Rest], Result) when $0 =< D, D =< $9 -> get_number(Rest, Result*10+D-$0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_number(Rest, Result) -> {ok, Result, Rest}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$.|Rest], State) -> {ok, State#state.dot, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$$|Rest], State) -> {ok, State#state.lines, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$', Mark|Rest], St) when $a =< Mark, Mark =< $z -> case lists:keysearch(Mark, 2, St#state.marks) of {value, {Line, Mark}} -> {ok, Line, Rest, St}; false -> {ok, 0, Rest, St} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$'|_Rest], _State) -> error(bad_mark); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$/|Rest], State) -> scan_forward($/, Rest, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address([$?|_Rest], _State) -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_address(_Cmd, _St) -> false. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- scan_forward(End, Patt0, State) -> {ok, Rest, NewState} = get_pattern(End, Patt0, State), Dot = NewState#state.dot, After = NewState#state.after_dot, scan_forward1(Dot+1, After, NewState, Rest). scan_forward1(Linenum, [Line|Rest], State, RestCmd) -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> scan_forward1(Linenum+1, Rest, State, RestCmd) end; scan_forward1(_, [], State, RestCmd) -> Dot = State#state.dot, Upto = State#state.upto_dot, case scan_forward2(Dot, Upto, State, RestCmd) of false -> error(bad_linenum); Other -> Other end. scan_forward2(0, [], _State, _RestCmd) -> false; scan_forward2(Linenum, [Line|Rest], State, RestCmd) -> case scan_forward2(Linenum-1, Rest, State, RestCmd) of false -> case re:run(Line#line.contents, State#state.pattern, [{capture, none}]) of match -> {ok, Linenum, RestCmd, State}; nomatch -> false end; Other -> Other end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($S, Cont) -> Cont(fun quest_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($T, Cont) -> Cont(fun time_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($=, Cont) -> Cont(fun print_linenum/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($a, Cont) -> Cont(fun append_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($c, Cont) -> Cont(fun change_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($d, Cont) -> Cont(fun delete_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($e, Cont) -> Cont(fun enter_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($E, Cont) -> Cont(fun enter_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($f, Cont) -> Cont(fun file_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($g, Cont) -> Cont(fun global_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($h, Cont) -> Cont(fun help_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($H, Cont) -> Cont(fun help_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($i, Cont) -> Cont(fun insert_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($k, Cont) -> Cont(fun mark_command/3, 1, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($l, Cont) -> Cont(fun list_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($m, Cont) -> Cont(fun move_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($n, Cont) -> Cont(fun number_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($p, Cont) -> Cont(fun print_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($P, Cont) -> Cont(fun prompt_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($q, Cont) -> Cont(fun quit_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($Q, Cont) -> Cont(fun quit_always_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($r, Cont) -> Cont(fun read_command/3, 1, last); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($s, Cont) -> Cont(fun subst_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($t, Cont) -> Cont(fun transpose_command/3, 2, dot); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($u, Cont) -> Cont(fun undo_command/3, 0, none); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($v, Cont) -> Cont(fun vglobal_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char($w, Cont) -> Cont(fun write_command/3, 2, all); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- parse_cmd_char(_, _Cont) -> error(bad_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- execute_command(Fun, NumLines, Def, State, Nums, Rest) -> Lines = check_lines(NumLines, Def, Nums, State), Fun(Rest, Lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(0, _, [], _State) -> []; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, dot, [], #state{dot=Dot}) -> [Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, next, [], State) when State#state.dot < State#state.lines -> [State#state.dot+1]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, last, [], State) -> [State#state.lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(1, _, [Num|_], State) when 0 =< Num, Num =< State#state.lines -> [Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, dot, [], #state{dot=Dot}) -> [Dot, Dot]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, all, [], #state{lines=Lines}) -> [1, Lines]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num], State) when 0 =< Num, Num =< State#state.lines -> [Num, Num]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(2, _, [Num2, Num1|_], State) when 0 =< Num1, Num1 =< Num2, Num2 =< State#state.lines -> [Num1, Num2]; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_lines(_, _, _, _) -> error(bad_linenum). %%% Executing commands. %% ($)= - print line number %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_linenum(Rest, [Line], State) -> NewState = check_trailing_p(Rest, State), io:format("~w\n", [Line]), NewState. %% ? - print state (for debugging) %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quest_command([], [], State) -> io:format("~p\n", [State]), State. %% Tcmd - time command %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- time_command(Cmd, [], St) -> Fun = fun parse_command/2, erlang:garbage_collect(), {Elapsed, Val} = timer:tc(erlang, apply, [Fun, [Cmd, St]]), io:format("Time used: ~p s~n", [Elapsed/1000000.0]), case Val of {error, Reason} -> throw({error, Reason}); Other -> Other end. %% (.)a - append text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append_command(Rest, [Line], St0) -> St1 = save_for_undo(St0), append(move_to(Line, check_trailing_p(Rest, St1))). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- append(St0) -> {ok, St1, Line0} = get_line('', St0), case Line0 of eof -> St1; ".\n" -> St1; Line -> append(insert_line(Line, St1)) end. %% (.,.)c %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- change_command(Rest, Lines, St0) -> St1 = delete_command(Rest, Lines, St0), St2 = append_command([], [St1#state.dot-1], St1), save_for_undo(St2, St0). %% (.,.)d - delete lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(_Rest, [0, _Last], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_command(Rest, [First, Last], St0) -> St1 = check_trailing_p(Rest, save_for_undo(St0)), delete(Last-First+1, move_to(Last, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) when St#state.dot == St#state.lines -> St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(0, St) -> next_line(St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete(Left, St0) -> St1 = delete_current_line(St0), delete(Left-1, St1). %% e file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(_Name, [], St) when St#state.modified == true -> error(buffer_modified); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_command(Name, [], St0) -> enter_always_command(Name, [], St0). %% E file - replace buffer with new file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- enter_always_command(Name, [], St0) -> St1 = read_command(Name, [0], #state{filename=St0#state.filename, opts=St0#state.opts}), St1#state{modified=false}. %% f file - print filename; set filename %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([], [], St) -> io:format("~s~n", [St#state.filename]), St; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command([$_|Name0], [], St) -> Name = skip_blanks(Name0), file_command([], [], St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- file_command(_, _, _) -> error(missing_space). %% (1,$)g/RE/commands - execute commands on all matching lines. %% (1,$)v/RE/commands - execute commands on all non-matching lines. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- global_command(Cmd, Lines, St) -> check_global0(true, Cmd, Lines, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- vglobal_command(Cmd, Lines, St) -> check_global0(false, Cmd, Lines, St). check_global0(_, _, _, St) when St#state.in_global == true -> error(nested_globals); check_global0(Sense, [Sep|Pattern], Lines, St0) -> {ok, Cmd, St1} = get_pattern(Sep, Pattern, St0), St2 = mark(Sense, Lines, St1), do_global_command(Cmd, St2#state{in_global=true}, 0). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark(Sense, [First, Last], St0) -> St1 = move_to(Last, St0), mark1(Sense, First-1, St1). mark1(_Sense, First, St) when St#state.dot == First -> St; mark1(Sense, First, St) -> [Line|Prev] = St#state.upto_dot, NewLine = case match(St) of true -> Line#line{mark=Sense}; false -> Line#line{mark=not(Sense)} end, mark1(Sense, First, prev_line(St#state{upto_dot=[NewLine|Prev]})). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- do_global_command(Cmd, St0, Matches) -> case find_mark(St0) of {ok, St1} -> St2 = St1#state{input=Cmd++[eof]}, {ok, St3, Cmd1} = get_line(St2), St4 = command(Cmd1, St3), %% XXX There might be several commands. do_global_command(Cmd, St4, Matches+1); false when Matches == 0 -> error(nomatch); false -> St0#state{in_global=false, input=[]} end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(State) -> find_mark(State#state.lines, State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(0, _State) -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) when State#state.dot == 0 -> find_mark(Limit, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- find_mark(Limit, State) -> case State#state.upto_dot of [Line|Prev] when Line#line.mark == true -> NewLine = Line#line{mark=false}, {ok, State#state{upto_dot=[NewLine|Prev]}}; _Other -> find_mark(Limit-1, wrap_next_line(State)) end. %% h - print info about last error %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command([], [], St) -> case St#state.last_error of undefined -> St; Reason -> io:put_chars(format_error(Reason)), io:nl(), St end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_command(_, _, _) -> error(garbage_after_command). %% H - toggle automatic help mode on/off %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- help_always_command([], [], St) -> Opts = St#state.opts, case lists:member(help_always, Opts) of true -> St#state{opts=Opts--[help_always]}; false -> help_command([], [], St), St#state{opts=[help_always|Opts]} end. %% (.)i - insert text %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(_Rest, [0], _State) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_command(Rest, [Line], State) -> append_command(Rest, [Line-1], State). %% (.)kx - mark line %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, [0], _St) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command([Mark|_Rest], [_Line], _St) when $a =< Mark, Mark =< $z -> error(not_implemented); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- mark_command(_, _, _) -> error(bad_mark). %% (.,.)l - list lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_command(Rest, Lines, St) -> print([$l|Rest], Lines, St). %% (.,.)m - move lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)t - copy lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- transpose_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %% (.,.)n - print lines with line numbers %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- number_command(Rest, Lines, St) -> print([$n|Rest], Lines, St). %% (.,.)p - print lines %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_command(Rest, Lines, St) -> print([$p|Rest], Lines, St). %% P - toggle prompt %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command([], [], St) -> Opts = St#state.opts, case lists:keysearch(prompt, 1, Opts) of {value, {prompt, ''}} -> St#state{opts=[{prompt, '*'}|Opts]}; {value, Value} -> St#state{opts=[{prompt, ''} | Opts--[Value]]} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prompt_command(_, _, _) -> error(garbage_after_command). %% q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_command(_, _, _) -> error(garbage_after_command). %% Q - quit editor %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command([], [], _) -> quit; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- quit_always_command(_, _, _) -> error(garbage_after_command). %% ($)r file - read file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], _, St) when St#state.filename == [] -> error(bad_filename); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([], [After], St) -> read(After, St#state.filename, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) when St#state.filename == [] -> Name = skip_blanks(Name0), read(After, Name, St#state{filename=Name}); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command([$ |Name0], [After], St) -> Name = skip_blanks(Name0), read(After, Name, St); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read_command(_, _, _) -> error(missing_space). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- read(After, Name, St0) -> case file:read_file(Name) of {ok, Bin} -> Chars = size(Bin), St1 = move_to(After, St0), St2 = insert_line(binary_to_list(Bin), St1), io:format("~w~n", [Chars]), St2; {error, _} -> error(bad_file) end. %% s/pattern/replacement/gp %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(_, [0, _], _) -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$ |_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([$\n|_Cmd0], [_First, _Last], _St0) -> error(bad_delimiter); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([Sep|Cmd0], [First, Last], St0) -> St1 = save_for_undo(St0), {ok, Cmd1, St2} = get_pattern(Sep, Cmd0, St1), {ok, Replacement, Cmd2} = get_replacement(Sep, Cmd1), {ok, Opts, Cmd3} = subst_check_gflag(Cmd2), St3 = check_trailing_p(Cmd3, St2), subst_command(Last-First+1, Opts, Replacement, move_to(First-1, St3), nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command([], _, _) -> error(bad_delimiter). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, nomatch) -> error(nomatch); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(0, _, _, _, StLast) when is_record(StLast, state) -> StLast; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_command(Left, Opts, Repl, St0, LastMatch) -> St1 = next_line(St0), [Line|_] = St1#state.upto_dot, Contents = Line#line.contents, case re:replace(Contents, St1#state.pattern, Repl, Opts) of Contents -> subst_command(Left-1, Opts, Repl, St1, LastMatch); NewContents -> %% XXX This doesn't work with marks. St2 = delete_current_line(St1), St3 = insert_line(NewContents, St2), subst_command(Left-1, Opts, Repl, St3, St3) end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag([$g|Cmd]) -> {ok, [global,{return,list}], Cmd}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- subst_check_gflag(Cmd) -> {ok, [{return,list}], Cmd}. %% u - undo %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], St) when St#state.undo == undefined -> error(bad_undo); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command([], [], #state{undo=Undo}) -> Undo; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- undo_command(_, _, _) -> error(garbage_after_command). %% (1,$)w - write buffer to file %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- write_command(_Cmd, [_First, _Last], _St) -> error(not_implemented). %%% Primitive buffer operations. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print_current(St) -> [Line|_] = St#state.upto_dot, Printer = St#state.print, Printer(Line#line.contents, St). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) when St#state.dot == 0 -> error(bad_linenum); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- delete_current_line(St) -> Lines = St#state.lines, [_|Prev] = St#state.upto_dot, St#state{dot=St#state.dot-1, upto_dot=Prev, lines=Lines-1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_line(Line, State) -> insert_line1(Line, State, []). insert_line1([$\n|Rest], State, Result) -> NewState = insert_single_line(lists:reverse(Result, [$\n]), State), insert_line1(Rest, NewState, []); insert_line1([C|Rest], State, Result) -> insert_line1(Rest, State, [C|Result]); insert_line1([], State, []) -> State; insert_line1([], State, Result) -> insert_single_line(lists:reverse(Result, [$\n]), State). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- insert_single_line(Line0, State) -> Line = #line{contents=Line0}, Dot = State#state.dot, Before = State#state.upto_dot, Lines = State#state.lines, %% XXX Avoid updating the record every time. State#state{dot=Dot+1, upto_dot=[Line|Before], lines=Lines+1, modified=true}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line < State#state.dot -> move_to(Line, prev_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when State#state.dot < Line -> move_to(Line, next_line(State)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- move_to(Line, State) when Line == State#state.dot -> State. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- prev_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot-1, upto_dot=tl(Before), after_dot=[hd(Before)|After]}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- next_line(State) -> Dot = State#state.dot, Before = State#state.upto_dot, After = State#state.after_dot, State#state{dot=Dot+1, upto_dot=[hd(After)|Before], after_dot=tl(After)}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) when State#state.dot == State#state.lines -> move_to(1, State); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- wrap_next_line(State) -> next_line(State). %%% Utilities. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, Cmd, State) -> get_pattern(End, Cmd, State, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, []) when State#state.pattern /= undefined -> {ok, Rest, State}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [End|Rest], State, Result) -> case re:compile(lists:reverse(Result)) of {error, _} -> error(bad_pattern); {ok, Re} -> {ok, Rest, State#state{pattern=Re}} end; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [C|Rest], State, Result) -> get_pattern(End, Rest, State, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_pattern(End, [], State, Result) -> get_pattern(End, [End], State, Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, Cmd) -> get_replacement(End, Cmd, []). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [End|Rest], Result) -> {ok, lists:reverse(Result), Rest}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, $&|Rest], Result) -> get_replacement(End, Rest, [$&, $\\|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [$\\, C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [C|Rest], Result) -> get_replacement(End, Rest, [C|Result]); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- get_replacement(End, [], Result) -> get_replacement(End, [End], Result). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$l], St) -> St#state{print=fun(Line, _) -> lister(Line, 0) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$n], St) -> St#state{print=fun numberer/2}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([$p], St) -> St#state{print=fun(Line, _) -> io:put_chars(Line) end}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p([], State) -> State; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- check_trailing_p(_Other, _State) -> error(garbage_after_command). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- error(Reason) -> throw({error, Reason}). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) when State#state.dot == 0 -> false; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- match(State) -> [Line|_] = State#state.upto_dot, Re = State#state.pattern, case re:run(Line#line.contents, Re, [{capture, none}]) of match -> true; nomatch -> false end. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks([$ |Rest]) -> skip_blanks(Rest); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- skip_blanks(Rest) -> Rest. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [Line], St0) when Line > 0 -> St1 = check_trailing_p(Rest, St0), print(Line, move_to(Line-1, St1)); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Rest, [First, Last], St0) when First > 0 -> St1 = check_trailing_p(Rest, St0), print(Last, move_to(First-1, St1)). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St) when St#state.dot == Last -> St#state{print=false}; %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- print(Last, St0) -> St1 = next_line(St0), print_current(St1), print(Last, St1). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister(Rest, 64) -> io:put_chars("\\\n"), lister(Rest, 0); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([C|Rest], Num) -> list_char(C), lister(Rest, Num+1); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- lister([], _) -> ok. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\t) -> io:put_chars("\\t"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char($\n) -> io:put_chars("$\n"); %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- list_char(C) -> io:put_chars([C]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- numberer(Line, St) -> io:format("~w\t~s", [St#state.dot, Line]). %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St) -> St#state{undo=St#state{undo=undefined, print=false}}. %%% ------------------------------------------------------------- %%% A stupid function header. %%% ------------------------------------------------------------- save_for_undo(St, OldSt) -> St#state{undo=OldSt#state{undo=undefined, print=false}}. ****** Process <0.25.0> -- 0.07 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] prim_file:drv_close/1 1 0.41 5 [ 5.00] prim_file:close/1 1 0.49 6 [ 6.00] prim_file:drv_command/3 1 0.49 6 [ 6.00] prim_file:drv_command/4 1 0.49 6 [ 6.00] prim_file:drv_get_response/1 1 0.49 6 [ 6.00] file_io_server:do_start/4 1 0.49 6 [ 6.00] erlang:iolist_size/1 1 0.49 6 [ 6.00] prim_file:read_file/1 1 0.66 8 [ 8.00] prim_file:drv_command/2 1 0.74 9 [ 9.00] prim_file:translate_response/2 1 0.74 9 [ 9.00] prim_file:pathname/1 1 0.74 9 [ 9.00] erlang:port_close/1 1 0.91 11 [ 11.00] prim_file:internal_name2native/1 1 0.91 11 [ 11.00] erlang:make_ref/0 1 0.91 11 [ 11.00] prim_file:drv_open/2 1 0.99 12 [ 12.00] file_io_server:start_link/3 1 1.07 13 [ 13.00] erlang:spawn_link/1 1 1.07 13 [ 13.00] prim_file:drv_get_response/2 1 1.15 14 [ 14.00] erlang:port_command/2 1 1.15 14 [ 14.00] prim_file:read_file/2 1 1.24 15 [ 15.00] gen_server:decode_msg/8 2 1.40 17 [ 8.50] gen_server:handle_msg/5 2 1.65 20 [ 10.00] erlang:demonitor/1 1 1.65 20 [ 20.00] erlang:bump_reductions/1 1 1.81 22 [ 22.00] erlang:monitor/2 1 2.06 25 [ 25.00] ets:insert/2 1 2.31 28 [ 28.00] file_server:handle_call/3 2 2.47 30 [ 15.00] gen_server:reply/2 2 3.54 43 [ 21.50] erlang:spawn_link/3 1 3.96 48 [ 48.00] erts_internal:port_close/1 1 5.19 63 [ 63.00] erlang:open_port/2 1 5.94 72 [ 72.00] gen_server:loop/6 2 7.09 86 [ 43.00] erts_internal:port_command/3 1 45.26 549 [ 549.00] ****** Process <0.397.0> -- 17.23 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] io_lib_format:term/5 1 0.00 3 [ 3.00] io_lib_format:newline/4 1 0.00 3 [ 3.00] io_lib_format:pcount/1 1 0.00 5 [ 5.00] io_lib_format:fwrite/2 1 0.00 6 [ 6.00] erlang:integer_to_list/1 1 0.00 8 [ 8.00] io_lib:format/2 1 0.00 9 [ 9.00] io_lib:write/2 1 0.00 9 [ 9.00] io_lib_format:encoding/2 2 0.00 9 [ 4.50] io_lib_format:pcount/2 3 0.00 9 [ 3.00] io_lib_format:collect_cseq/2 2 0.00 10 [ 5.00] io_lib_format:field_width/2 2 0.00 10 [ 5.00] io_lib_format:field_width/3 2 0.00 10 [ 5.00] io_lib_format:strings/2 2 0.00 11 [ 5.50] io_lib_format:precision/2 2 0.00 11 [ 5.50] io_lib_format:pad_char/2 2 0.00 11 [ 5.50] io_lib_format:decr_pc/2 2 0.00 11 [ 5.50] io_lib_format:collect/2 3 0.00 13 [ 4.33] io_lib_format:collect_cc/2 2 0.00 13 [ 6.50] io_lib_format:build/3 3 0.00 13 [ 4.33] io_lib_format:control/9 2 0.00 13 [ 6.50] io_lib_format:field_value/2 2 0.01 16 [ 8.00] io:o_request/3 1414 1.40 4003 [ 2.83] gen_server:handle_msg/5 1414 1.92 5492 [ 3.88] test_server_gl:output_to_file/4 1414 2.21 6310 [ 4.46] io:execute_request/2 1414 2.29 6547 [ 4.63] gen_server:decode_msg/8 1414 2.39 6833 [ 4.83] test_server_gl:handle_info/2 1414 2.51 7177 [ 5.08] io:put_chars/2 1414 2.74 7847 [ 5.55] io:request/2 1414 2.79 7970 [ 5.64] gen_server:handle_common_reply/6 1414 3.10 8867 [ 6.27] test_server_gl:dress_output/3 1414 3.16 9049 [ 6.40] test_server_gl:io_req/3 1414 3.74 10702 [ 7.57] test_server_gl:is_io_permitted/2 1414 3.97 11354 [ 8.03] io:io_request/2 1414 4.47 12794 [ 9.05] gen_server:loop/6 1414 6.01 17197 [ 12.16] unicode:characters_to_list/2 1414 6.12 17516 [ 12.39] io:wait_io_mon_reply/2 1414 6.22 17786 [ 12.58] gen_server:dispatch/3 1414 6.56 18763 [ 13.27] test_server_gl:output/5 1414 6.90 19735 [ 13.96] unicode:characters_to_binary/2 1414 10.07 28788 [ 20.36] erlang:demonitor/2 1414 10.36 29619 [ 20.95] erlang:monitor/2 1414 11.00 31464 [ 22.25] ****** Process <0.1502.0> -- 33.71 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] file_io_server:put_chars/3 1414 1.17 6535 [ 4.62] prim_file:drv_get_response/1 1414 1.40 7844 [ 5.55] prim_file:get_uint64/1 1414 1.44 8036 [ 5.68] unicode:characters_to_binary_int/3 1414 1.63 9146 [ 6.47] unicode:characters_to_binary/2 1414 1.74 9745 [ 6.89] prim_file:drv_command_nt/3 1414 1.78 9946 [ 7.03] prim_file:translate_response/2 1414 1.85 10334 [ 7.31] prim_file:drv_get_response/2 1414 2.01 11257 [ 7.96] prim_file:write/2 1414 2.13 11924 [ 8.43] unicode:characters_to_binary/3 1414 2.52 14116 [ 9.98] file_io_server:io_request/2 1414 2.95 16501 [ 11.67] file_io_server:server_loop/1 1414 3.03 16963 [ 12.00] erlang:port_command/2 1414 3.16 17708 [ 12.52] prim_file:get_uint32/1 2828 3.85 21531 [ 7.61] file_io_server:io_reply/3 1414 3.99 22330 [ 15.79] erlang:bump_reductions/1 1414 4.68 26199 [ 18.53] erts_internal:port_command/3 1414 60.66 339423 [ 240.04] ****** Process <0.1511.0> -- 2.09 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] gen_server:handle_msg/5 1 0.02 6 [ 6.00] gen_server:decode_msg/8 1 0.02 7 [ 7.00] gen_server:dispatch/3 1 0.03 10 [ 10.00] erlang:now/0 1 0.07 24 [ 24.00] gen_server:loop/6 1 0.07 25 [ 25.00] erlang:system_flag/2 2 0.10 34 [ 17.00] gb_trees:empty/0 1 0.30 105 [ 105.00] erlang:trace_pattern/3 2 99.39 34496 [ 17248.00] ****** Process <0.1522.0> -- 45.91 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] eed:quit_command/3 1 0.00 2 [ 2.00] eed:'-parse_cmd_char/2-fun-21-'/3 1 0.00 3 [ 3.00] file:check_and_call/2 1 0.00 3 [ 3.00] io:format/3 1 0.00 4 [ 4.00] eed:global_command/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-22-'/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-11-'/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-19-'/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-9-'/3 1 0.00 4 [ 4.00] eed:check_global0/4 1 0.00 5 [ 5.00] eed:read_command/3 1 0.00 5 [ 5.00] eed:read/3 1 0.00 5 [ 5.00] eed:subst_command/3 1 0.00 5 [ 5.00] eed:skip_blanks/1 1 0.00 5 [ 5.00] re:apply_mlist/3 1 0.00 5 [ 5.00] eed:scan_forward/3 2 0.00 6 [ 3.00] eed:help_always_command/3 1 0.00 6 [ 6.00] eed:get_replacement/2 1 0.00 6 [ 6.00] eed:'-parse_cmd_char/2-fun-17-'/3 2 0.00 6 [ 3.00] eed:subst_check_gflag/1 1 0.00 7 [ 7.00] re:do_replace/3 1 0.00 7 [ 7.00] eed:mark/3 1 0.00 8 [ 8.00] eed:help_command/3 1 0.00 8 [ 8.00] eed:print_command/3 2 0.00 8 [ 4.00] eed:print/3 2 0.00 9 [ 4.50] file:read_file/1 1 0.00 9 [ 9.00] file:call/2 2 0.00 9 [ 4.50] eed:delete_current_line/1 1 0.00 10 [ 10.00] io:format/2 1 0.00 11 [ 11.00] gen:do_call/4 2 0.00 11 [ 5.50] file:open/2 1 0.00 11 [ 11.00] file:native_name_encoding/0 2 0.00 11 [ 5.50] erlang:apply/2 1 0.00 12 [ 12.00] erlang:whereis/1 2 0.00 12 [ 6.00] eed:get_one2/4 3 0.00 14 [ 4.67] erlang:list_to_tuple/1 2 0.00 14 [ 7.00] eed:file/1 1 0.00 15 [ 15.00] gen_server:call/3 2 0.00 15 [ 7.50] lists:member/2 3 0.00 15 [ 5.00] eed:parse_next_address/3 6 0.00 18 [ 3.00] file:file_name/1 2 0.00 18 [ 9.00] file:check_args/1 5 0.00 18 [ 3.60] eed:get_number/1 3 0.00 19 [ 6.33] gen:call/4 2 0.00 20 [ 10.00] eed:get_replacement/3 10 0.00 25 [ 2.50] eed:get_number/2 6 0.00 30 [ 5.00] eed:get_pattern/3 4 0.00 30 [ 7.50] eed:loop/1 7 0.00 33 [ 4.71] net_kernel:dflag_unicode_io/1 11 0.01 53 [ 4.82] io:bc_req/3 11 0.01 55 [ 5.00] re:do_mlist/5 3 0.01 55 [ 18.33] eed:get_line1/3 10 0.01 56 [ 5.60] erlang:send/3 2 0.01 64 [ 32.00] io:get_line/2 10 0.01 73 [ 7.30] eed:get_pattern/4 45 0.02 124 [ 2.76] re:precomp_repl/1 10 0.02 129 [ 12.90] re:compile/1 4 0.03 242 [ 60.50] eed:scan_forward1/4 66 0.05 387 [ 5.86] file:file_name_1/2 88 0.06 441 [ 5.01] eed:parse_command/3 206 0.07 510 [ 2.48] eed:execute_command/6 203 0.07 530 [ 2.61] eed:'-parse_cmd_char/2-fun-12-'/3 196 0.07 542 [ 2.77] eed:do_global_command/3 197 0.08 590 [ 2.99] eed:'-parse_command1/3-fun-0-'/6 203 0.08 590 [ 2.91] eed:append_command/3 196 0.09 668 [ 3.41] eed:get_line2/3 254 0.09 679 [ 2.67] eed:insert_command/3 196 0.09 686 [ 3.50] eed:get_one1/3 209 0.11 805 [ 3.85] eed:parse_command1/3 203 0.11 817 [ 4.02] lib:nonl/1 254 0.11 828 [ 3.26] eed:check_trailing_p/2 199 0.12 885 [ 4.45] eed:parse_command/2 203 0.13 983 [ 4.84] lists:keysearch/3 203 0.14 1034 [ 5.09] eed:parse_cmd_char/2 203 0.14 1068 [ 5.26] eed:save_for_undo/1 197 0.15 1124 [ 5.71] eed:get_address/2 206 0.15 1134 [ 5.50] eed:command/2 203 0.15 1136 [ 5.60] eed:get_one/2 206 0.15 1136 [ 5.51] eed:check_lines/4 203 0.15 1153 [ 5.68] eed:find_mark/1 197 0.16 1255 [ 6.37] eed:get_line/1 203 0.19 1476 [ 7.27] eed:append/1 784 0.24 1866 [ 2.38] eed:insert_line/2 590 0.35 2653 [ 4.50] eed:subst_command/5 821 0.39 2984 [ 3.63] erlang:group_leader/0 1414 0.45 3422 [ 2.42] eed:mark1/3 821 0.52 3979 [ 4.85] io:o_request/3 1414 0.53 4029 [ 2.85] io:put_chars/1 1413 0.56 4269 [ 3.02] lists:reverse/1 789 0.59 4524 [ 5.73] io:put_chars/2 1413 0.61 4646 [ 3.29] eed:'-check_trailing_p/2-fun-2-'/2 1413 0.62 4743 [ 3.36] re:do_replace/5 820 0.67 5133 [ 6.26] eed:get_line/2 987 0.77 5904 [ 5.98] re:process_repl_params/3 1640 0.87 6602 [ 4.03] eed:insert_single_line/2 1409 0.89 6776 [ 4.81] io:execute_request/2 1424 0.98 7445 [ 5.23] re:replace/4 820 0.99 7540 [ 9.20] io:default_output/0 1414 1.04 7931 [ 5.61] eed:match/1 820 1.04 7949 [ 9.69] io:request/2 1424 1.08 8223 [ 5.77] re:to_binary/2 1640 1.14 8673 [ 5.29] erlang:binary_to_list/1 821 1.22 9260 [ 11.28] eed:find_mark/2 2619 1.36 10375 [ 3.96] erlang:'++'/2 196 1.40 10679 [ 54.48] eed:wrap_next_line/1 2421 1.56 11849 [ 4.89] eed:move_to/2 4842 1.70 12934 [ 2.67] eed:print/2 1414 1.84 14033 [ 9.92] eed:print_current/1 1413 1.90 14468 [ 10.24] erlang:demonitor/2 1426 1.95 14823 [ 10.39] erlang:iolist_to_binary/1 2460 1.98 15068 [ 6.13] io:wait_io_mon_reply/2 1424 2.09 15902 [ 11.17] lists:reverse/2 2009 2.15 16365 [ 8.15] io:io_request/2 1424 2.43 18491 [ 12.99] eed:prev_line/1 4649 3.39 25847 [ 5.56] erlang:monitor/2 1426 4.35 33151 [ 23.25] eed:next_line/1 5464 4.44 33797 [ 6.19] re:run/3 1706 5.99 45661 [ 26.76] unicode:characters_to_binary/2 1413 6.16 46917 [ 33.20] erlang:setelement/3 14943 9.05 68973 [ 4.62] eed:get_input/3 32340 10.85 82690 [ 2.56] eed:insert_line1/3 55717 17.00 129553 [ 2.33] ****** Process <0.1523.0> -- 0.98 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] prim_file:open_int_setopts/3 1 0.02 4 [ 4.00] file_io_server:'-do_start/4-fun-0-'/6 1 0.02 4 [ 4.00] lists:reverse/1 1 0.03 5 [ 5.00] file_io_server:get_chars_empty/6 1 0.03 5 [ 5.00] prim_file:open_mode/1 1 0.04 6 [ 6.00] erlang:iolist_size/1 1 0.04 7 [ 7.00] prim_file:open/2 1 0.05 8 [ 8.00] file_io_server:parse_options/1 1 0.06 9 [ 9.00] file_io_server:parse_options/4 2 0.06 9 [ 4.50] prim_file:drv_open/2 1 0.06 10 [ 10.00] prim_file:reverse/1 2 0.06 10 [ 5.00] file_io_server:expand_encoding/1 2 0.06 10 [ 5.00] erlang:apply/2 1 0.07 11 [ 11.00] prim_file:pathname/1 1 0.07 12 [ 12.00] prim_file:open_mode/4 3 0.08 13 [ 4.33] prim_file:drv_command/3 4 0.09 14 [ 3.50] file_io_server:get_chars_notempty/6 2 0.09 15 [ 7.50] prim_file:open_int/4 2 0.10 16 [ 8.00] prim_file:drv_command/4 4 0.10 17 [ 4.25] file_io_server:read_size/1 3 0.12 19 [ 6.33] prim_file:drv_get_response/1 4 0.12 20 [ 5.00] prim_file:get_uint64/1 4 0.12 20 [ 5.00] prim_file:drv_command/2 4 0.17 28 [ 7.00] prim_file:translate_response/2 4 0.19 31 [ 7.75] prim_file:drv_get_response/2 4 0.20 32 [ 8.00] erlang:monitor/2 1 0.20 32 [ 32.00] erlang:port_command/2 4 0.28 45 [ 11.25] prim_file:get_uint32/1 8 0.34 56 [ 7.00] lists:reverse/2 12 0.34 56 [ 4.67] prim_file:internal_name2native/1 1 0.39 63 [ 63.00] file_io_server:get_chars/5 10 0.40 65 [ 6.50] erlang:open_port/2 1 0.40 66 [ 66.00] unicode:o_trans/1 10 0.42 68 [ 6.80] prim_file:read/2 3 0.45 74 [ 24.67] file_io_server:get_chars_apply/7 12 0.49 80 [ 6.67] unicode:characters_to_binary/3 10 0.50 81 [ 8.10] unicode:i_trans_chk/1 20 0.59 96 [ 4.80] file_io_server:io_request/2 10 0.61 99 [ 9.90] erlang:setelement/3 21 0.67 109 [ 5.19] unicode:characters_to_binary_int/3 10 0.71 116 [ 11.60] file_io_server:server_loop/1 11 0.80 130 [ 11.82] io_lib:collect_line/4 12 0.80 131 [ 10.92] erlang:bump_reductions/1 4 1.13 184 [ 46.00] file_io_server:io_reply/3 10 1.61 262 [ 26.20] unicode:characters_to_list/2 12 3.16 516 [ 43.00] io_lib:collect_line_list/2 259 3.34 544 [ 2.10] erts_internal:port_command/3 4 4.92 803 [ 200.75] erlang:list_to_binary/1 633 11.44 1865 [ 2.95] unicode:ml_map/3 631 18.32 2987 [ 4.73] unicode:'-characters_to_binary_int/3-fun-0-'/5 621 21.97 3583 [ 5.77] unicode:'-o_trans/1-fun-0-'/1 621 23.67 3859 [ 6.21] FUNCTION CALLS % TIME [uS / CALLS] -------- ----- --- ---- [----------] eed:quit_command/3 1 0.00 2 [ 2.00] eed:'-parse_cmd_char/2-fun-21-'/3 1 0.00 3 [ 3.00] io_lib_format:term/5 1 0.00 3 [ 3.00] io_lib_format:newline/4 1 0.00 3 [ 3.00] file:check_and_call/2 1 0.00 3 [ 3.00] io:format/3 1 0.00 4 [ 4.00] prim_file:open_int_setopts/3 1 0.00 4 [ 4.00] eed:global_command/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-22-'/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-11-'/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-19-'/3 1 0.00 4 [ 4.00] eed:'-parse_cmd_char/2-fun-9-'/3 1 0.00 4 [ 4.00] file_io_server:'-do_start/4-fun-0-'/6 1 0.00 4 [ 4.00] prim_file:drv_close/1 1 0.00 5 [ 5.00] eed:check_global0/4 1 0.00 5 [ 5.00] eed:read_command/3 1 0.00 5 [ 5.00] eed:read/3 1 0.00 5 [ 5.00] eed:subst_command/3 1 0.00 5 [ 5.00] eed:skip_blanks/1 1 0.00 5 [ 5.00] io_lib_format:pcount/1 1 0.00 5 [ 5.00] re:apply_mlist/3 1 0.00 5 [ 5.00] file_io_server:get_chars_empty/6 1 0.00 5 [ 5.00] prim_file:close/1 1 0.00 6 [ 6.00] prim_file:open_mode/1 1 0.00 6 [ 6.00] eed:scan_forward/3 2 0.00 6 [ 3.00] eed:help_always_command/3 1 0.00 6 [ 6.00] eed:get_replacement/2 1 0.00 6 [ 6.00] eed:'-parse_cmd_char/2-fun-17-'/3 2 0.00 6 [ 3.00] io_lib_format:fwrite/2 1 0.00 6 [ 6.00] file_io_server:do_start/4 1 0.00 6 [ 6.00] eed:subst_check_gflag/1 1 0.00 7 [ 7.00] re:do_replace/3 1 0.00 7 [ 7.00] prim_file:open/2 1 0.00 8 [ 8.00] prim_file:read_file/1 1 0.00 8 [ 8.00] eed:mark/3 1 0.00 8 [ 8.00] eed:help_command/3 1 0.00 8 [ 8.00] eed:print_command/3 2 0.00 8 [ 4.00] erlang:integer_to_list/1 1 0.00 8 [ 8.00] io_lib:format/2 1 0.00 9 [ 9.00] io_lib:write/2 1 0.00 9 [ 9.00] eed:print/3 2 0.00 9 [ 4.50] io_lib_format:encoding/2 2 0.00 9 [ 4.50] io_lib_format:pcount/2 3 0.00 9 [ 3.00] file_io_server:parse_options/1 1 0.00 9 [ 9.00] file_io_server:parse_options/4 2 0.00 9 [ 4.50] file:read_file/1 1 0.00 9 [ 9.00] file:call/2 2 0.00 9 [ 4.50] prim_file:reverse/1 2 0.00 10 [ 5.00] eed:delete_current_line/1 1 0.00 10 [ 10.00] io_lib_format:collect_cseq/2 2 0.00 10 [ 5.00] io_lib_format:field_width/2 2 0.00 10 [ 5.00] io_lib_format:field_width/3 2 0.00 10 [ 5.00] file_io_server:expand_encoding/1 2 0.00 10 [ 5.00] io:format/2 1 0.00 11 [ 11.00] gen:do_call/4 2 0.00 11 [ 5.50] io_lib_format:strings/2 2 0.00 11 [ 5.50] io_lib_format:precision/2 2 0.00 11 [ 5.50] io_lib_format:pad_char/2 2 0.00 11 [ 5.50] io_lib_format:decr_pc/2 2 0.00 11 [ 5.50] erlang:port_close/1 1 0.00 11 [ 11.00] file:open/2 1 0.00 11 [ 11.00] file:native_name_encoding/0 2 0.00 11 [ 5.50] erlang:make_ref/0 1 0.00 11 [ 11.00] erlang:whereis/1 2 0.00 12 [ 6.00] prim_file:open_mode/4 3 0.00 13 [ 4.33] io_lib_format:collect/2 3 0.00 13 [ 4.33] io_lib_format:collect_cc/2 2 0.00 13 [ 6.50] io_lib_format:build/3 3 0.00 13 [ 4.33] io_lib_format:control/9 2 0.00 13 [ 6.50] file_io_server:start_link/3 1 0.00 13 [ 13.00] erlang:spawn_link/1 1 0.00 13 [ 13.00] erlang:iolist_size/1 2 0.00 13 [ 6.50] eed:get_one2/4 3 0.00 14 [ 4.67] erlang:list_to_tuple/1 2 0.00 14 [ 7.00] prim_file:read_file/2 1 0.00 15 [ 15.00] eed:file/1 1 0.00 15 [ 15.00] gen_server:call/3 2 0.00 15 [ 7.50] file_io_server:get_chars_notempty/6 2 0.00 15 [ 7.50] lists:member/2 3 0.00 15 [ 5.00] prim_file:open_int/4 2 0.00 16 [ 8.00] io_lib_format:field_value/2 2 0.00 16 [ 8.00] eed:parse_next_address/3 6 0.00 18 [ 3.00] file:file_name/1 2 0.00 18 [ 9.00] file:check_args/1 5 0.00 18 [ 3.60] eed:get_number/1 3 0.00 19 [ 6.33] file_io_server:read_size/1 3 0.00 19 [ 6.33] gen:call/4 2 0.00 20 [ 10.00] prim_file:drv_command/3 5 0.00 20 [ 4.00] erlang:demonitor/1 1 0.00 20 [ 20.00] prim_file:pathname/1 2 0.00 21 [ 10.50] prim_file:drv_open/2 2 0.00 22 [ 11.00] prim_file:drv_command/4 5 0.00 23 [ 4.60] erlang:apply/2 2 0.00 23 [ 11.50] erlang:now/0 1 0.00 24 [ 24.00] eed:get_replacement/3 10 0.00 25 [ 2.50] ets:insert/2 1 0.00 28 [ 28.00] eed:get_number/2 6 0.00 30 [ 5.00] eed:get_pattern/3 4 0.00 30 [ 7.50] file_server:handle_call/3 2 0.00 30 [ 15.00] eed:loop/1 7 0.00 33 [ 4.71] erlang:system_flag/2 2 0.00 34 [ 17.00] prim_file:drv_command/2 5 0.00 37 [ 7.40] gen_server:reply/2 2 0.00 43 [ 21.50] erlang:spawn_link/3 1 0.00 48 [ 48.00] net_kernel:dflag_unicode_io/1 11 0.00 53 [ 4.82] io:bc_req/3 11 0.00 55 [ 5.00] re:do_mlist/5 3 0.00 55 [ 18.33] eed:get_line1/3 10 0.00 56 [ 5.60] erts_internal:port_close/1 1 0.00 63 [ 63.00] erlang:send/3 2 0.00 64 [ 32.00] file_io_server:get_chars/5 10 0.00 65 [ 6.50] unicode:o_trans/1 10 0.00 68 [ 6.80] io:get_line/2 10 0.00 73 [ 7.30] prim_file:read/2 3 0.00 74 [ 24.67] prim_file:internal_name2native/1 2 0.00 74 [ 37.00] file_io_server:get_chars_apply/7 12 0.00 80 [ 6.67] unicode:i_trans_chk/1 20 0.01 96 [ 4.80] gb_trees:empty/0 1 0.01 105 [ 105.00] eed:get_pattern/4 45 0.01 124 [ 2.76] re:precomp_repl/1 10 0.01 129 [ 12.90] io_lib:collect_line/4 12 0.01 131 [ 10.92] erlang:open_port/2 2 0.01 138 [ 69.00] re:compile/1 4 0.01 242 [ 60.50] eed:scan_forward1/4 66 0.02 387 [ 5.86] file:file_name_1/2 88 0.03 441 [ 5.01] eed:parse_command/3 206 0.03 510 [ 2.48] eed:execute_command/6 203 0.03 530 [ 2.61] eed:'-parse_cmd_char/2-fun-12-'/3 196 0.03 542 [ 2.77] io_lib:collect_line_list/2 259 0.03 544 [ 2.10] eed:do_global_command/3 197 0.04 590 [ 2.99] eed:'-parse_command1/3-fun-0-'/6 203 0.04 590 [ 2.91] eed:append_command/3 196 0.04 668 [ 3.41] eed:get_line2/3 254 0.04 679 [ 2.67] eed:insert_command/3 196 0.04 686 [ 3.50] eed:get_one1/3 209 0.05 805 [ 3.85] eed:parse_command1/3 203 0.05 817 [ 4.02] lib:nonl/1 254 0.05 828 [ 3.26] eed:check_trailing_p/2 199 0.05 885 [ 4.45] eed:parse_command/2 203 0.06 983 [ 4.84] lists:keysearch/3 203 0.06 1034 [ 5.09] eed:parse_cmd_char/2 203 0.06 1068 [ 5.26] eed:save_for_undo/1 197 0.07 1124 [ 5.71] eed:get_address/2 206 0.07 1134 [ 5.50] eed:command/2 203 0.07 1136 [ 5.60] eed:get_one/2 206 0.07 1136 [ 5.51] eed:check_lines/4 203 0.07 1153 [ 5.68] eed:find_mark/1 197 0.08 1255 [ 6.37] eed:get_line/1 203 0.09 1476 [ 7.27] erlang:list_to_binary/1 633 0.11 1865 [ 2.95] eed:append/1 784 0.11 1866 [ 2.38] eed:insert_line/2 590 0.16 2653 [ 4.50] eed:subst_command/5 821 0.18 2984 [ 3.63] unicode:ml_map/3 631 0.18 2987 [ 4.73] erlang:group_leader/0 1414 0.21 3422 [ 2.42] unicode:'-characters_to_binary_int/3-fun-0-'/5 621 0.22 3583 [ 5.77] unicode:'-o_trans/1-fun-0-'/1 621 0.23 3859 [ 6.21] eed:mark1/3 821 0.24 3979 [ 4.85] io:put_chars/1 1413 0.26 4269 [ 3.02] lists:reverse/1 790 0.27 4529 [ 5.73] eed:'-check_trailing_p/2-fun-2-'/2 1413 0.29 4743 [ 3.36] re:do_replace/5 820 0.31 5133 [ 6.26] gen_server:handle_msg/5 1417 0.33 5518 [ 3.89] eed:get_line/2 987 0.36 5904 [ 5.98] test_server_gl:output_to_file/4 1414 0.38 6310 [ 4.46] file_io_server:put_chars/3 1414 0.39 6535 [ 4.62] re:process_repl_params/3 1640 0.40 6602 [ 4.03] eed:insert_single_line/2 1409 0.41 6776 [ 4.81] gen_server:decode_msg/8 1417 0.41 6857 [ 4.84] test_server_gl:handle_info/2 1414 0.43 7177 [ 5.08] re:replace/4 820 0.45 7540 [ 9.20] prim_file:drv_get_response/1 1419 0.47 7870 [ 5.55] io:default_output/0 1414 0.48 7931 [ 5.61] eed:match/1 820 0.48 7949 [ 9.69] io:o_request/3 2828 0.48 8032 [ 2.84] prim_file:get_uint64/1 1418 0.49 8056 [ 5.68] re:to_binary/2 1640 0.52 8673 [ 5.29] gen_server:handle_common_reply/6 1414 0.53 8867 [ 6.27] test_server_gl:dress_output/3 1414 0.55 9049 [ 6.40] erlang:binary_to_list/1 821 0.56 9260 [ 11.28] unicode:characters_to_binary_int/3 1424 0.56 9262 [ 6.50] prim_file:drv_command_nt/3 1414 0.60 9946 [ 7.03] prim_file:translate_response/2 1419 0.63 10374 [ 7.31] eed:find_mark/2 2619 0.63 10375 [ 3.96] erlang:'++'/2 196 0.64 10679 [ 54.48] test_server_gl:io_req/3 1414 0.64 10702 [ 7.57] prim_file:drv_get_response/2 1419 0.68 11303 [ 7.97] test_server_gl:is_io_permitted/2 1414 0.68 11354 [ 8.03] eed:wrap_next_line/1 2421 0.71 11849 [ 4.89] prim_file:write/2 1414 0.72 11924 [ 8.43] io:put_chars/2 2827 0.75 12493 [ 4.42] eed:move_to/2 4842 0.78 12934 [ 2.67] io:execute_request/2 2838 0.84 13992 [ 4.93] eed:print/2 1414 0.85 14033 [ 9.92] unicode:characters_to_binary/3 1424 0.86 14197 [ 9.97] eed:print_current/1 1413 0.87 14468 [ 10.24] erlang:iolist_to_binary/1 2460 0.91 15068 [ 6.13] io:request/2 2838 0.98 16193 [ 5.71] lists:reverse/2 2021 0.99 16421 [ 8.13] file_io_server:io_request/2 1424 1.00 16600 [ 11.66] file_io_server:server_loop/1 1425 1.03 17093 [ 12.00] gen_server:loop/6 1417 1.04 17308 [ 12.21] erlang:port_command/2 1419 1.07 17767 [ 12.52] unicode:characters_to_list/2 1426 1.09 18032 [ 12.65] gen_server:dispatch/3 1415 1.13 18773 [ 13.27] test_server_gl:output/5 1414 1.19 19735 [ 13.96] prim_file:get_uint32/1 2836 1.30 21587 [ 7.61] file_io_server:io_reply/3 1424 1.36 22592 [ 15.87] eed:prev_line/1 4649 1.56 25847 [ 5.56] erlang:bump_reductions/1 1419 1.59 26405 [ 18.61] io:io_request/2 2838 1.88 31285 [ 11.02] io:wait_io_mon_reply/2 2838 2.03 33688 [ 11.87] eed:next_line/1 5464 2.04 33797 [ 6.19] erlang:trace_pattern/3 2 2.08 34496 [ 17248.00] erlang:demonitor/2 2840 2.68 44442 [ 15.65] re:run/3 1706 2.75 45661 [ 26.76] erlang:monitor/2 2842 3.90 64672 [ 22.76] erlang:setelement/3 14964 4.16 69082 [ 4.62] eed:get_input/3 32340 4.98 82690 [ 2.56] unicode:characters_to_binary/2 4241 5.15 85450 [ 20.15] eed:insert_line1/3 55717 7.81 129553 [ 2.33] erts_internal:port_command/3 1419 20.53 340775 [ 240.15] === Ended at 2014-01-03 22:57:54 === successfully completed test case === returned value = {comment,"2.7824858757062145 times slower"}


Test run history | Top level test index