:- module ed.:- interface.:- import_module io.:- pred main(io::di, io::uo) is det.:- implementation.:- import_module list, string, maybe, bool, exception, int.:- type editor ---> editor( filename :: maybe(string), file :: list(string) ).:- pred load(editor, editor, io, io).:- mode load(in, out, di, uo) is det.:- pred write(editor, editor, io, io).:- mode write(in, out, di, uo) is det.:- pred print(editor, io, io).:- mode print(in, di, uo) is det.:- pred edit(int, string, list(string), list(string), list(string)).:- mode edit(in, in, in, in, out) is det.:- pred replace(string, string, editor, editor).:- mode replace(in, in, in, out) is det.:- pred ed(editor, io, io).:- mode ed(in, di, uo) is det.main(!IO) :- io.command_line_arguments(Args, !IO), ( if Args = [Filename] then some [!E] ( load(editor(yes(Filename), []), !:E, !IO), ed(!.E, !IO) ) else if Args = [] then ed(editor(no, []), !IO) else io.progname_base("ed", Program, !IO), io.format(io.stderr_stream, "usage: %s []\n", [s(Program)], !IO), io.set_exit_status(1, !IO) ).load(editor(no, _), _, !IO) :- throw(software_error("loaded without a filename")).load(!.E @ editor(yes(Filename), _), !:E, !IO) :- io.open_input(Filename, OpenRes, !IO), ( OpenRes = ok(Stream), read_lines(Stream, Lines, !IO), !E^file := Lines ; OpenRes = error(Error), warn_open(Filename, "input", io.error_message(Error), !IO) ).write(editor(no, _), _, !IO) :- throw(software_error("write without a filename")).write(!.E @ editor(yes(Filename), _), !:E, !IO) :- io.open_output(Filename, OpenRes, !IO), ( OpenRes = ok(Stream), foldl(io.write_string(Stream), !.E^file, !IO), io.close_output(Stream, !IO) ; OpenRes = error(Error), warn_open(Filename, "output", io.error_message(Error), !IO) ).print(editor(_, Lines), !IO) :- foldl(io.write_string, Lines, !IO).edit(LineNo, Line, Pre, [], L) :- ( if LineNo =< 0 then list.reverse([Line | Pre], L) else edit(LineNo - 1, Line, ["\n" | Pre], [], L) ).edit(LineNo, Line, Pre, [H | Post], L) :- ( if LineNo =< 0 then L = list.reverse(Pre) ++ [Line | Post] else edit(LineNo - 1, Line, [H | Pre], Post, L) ).replace(From, To, !E) :- foldl((pred(!.Line::in, Acc::in, [!:Line | Acc]::out) is det :- string.replace_all(!.Line, From, To, !:Line) ), !.E^file, [], Lines), !E^file := list.reverse(Lines).ed(!.E, !IO) :- io.read_line_as_string(Res, !IO), ( Res = ok(String), Line = chomp(String), Cmd = split_at_separator((pred(' '::in) is semidet), Line), ( if Cmd = ["p"] then ed.print(!.E, !IO) else if Cmd = [NStr, "r" | Replace], to_int(NStr, N) then edit(N, join_list(" ", Replace) ++ "\n", [], !.E^file, Lines), !E^file := Lines else if Cmd = ["w"] then write(!E, !IO) else if index(Line, 1, Sep), ["s", From, To, ""] = split_at_separator((pred(C::in) is semidet :- C = Sep), Line) then ed.replace(From, To, !E) else io.print(Cmd, !IO), io.write_string(io.stderr_stream, "?\n", !IO) ), ed(!.E, !IO) ; Res = eof ; Res = error(E), io.format(io.stderr_stream, "got error reading editor command: %s\n", [s(io.error_message(E))], !IO) ).:- pred read_lines(io.text_input_stream, list(string), io, io).:- mode read_lines(in, out, di, uo) is det.read_lines(Stream, !:L, !IO) :- read_lines(Stream, [], !:L, !IO), list.reverse(!L).:- pred read_lines(io.text_input_stream, list(string), list(string), io, io).:- mode read_lines(in, in, out, di, uo) is det.read_lines(Stream, !L, !IO) :- io.read_line_as_string(Stream, Res, !IO), ( Res = eof, io.close_input(Stream, !IO) ; Res = ok(Line), read_lines(Stream, [Line | !.L], !:L, !IO) ; Res = error(_), throw(Res) ).:- pred warn_open(string, string, string, io, io).:- mode warn_open(in, in, in, di, uo) is det.warn_open(Filename, Use, Reason, !IO) :- io.format(io.stderr_stream, "failed to open %s for %s: %s\n", [s(Filename), s(Use), s(Reason)], !IO).